From 8cafeeb79e84889049f8d83ff9f3a1e5de925409 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 28 Apr 2021 18:06:38 +0000 Subject: [PATCH] Bug 739096 - Update cairo to commit 277a1daec80cb6cf7bfb0e200cf78e7842cb2f82 (release 1.17.4 + post-release fixes on trunk) from https://gitlab.freedesktop.org/cairo/cairo/-/tree/master. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D112558 --- gfx/cairo/cairo/AUTHORS | 30 +- gfx/cairo/cairo/COPYING | 22 +- gfx/cairo/cairo/COPYING-LGPL-2.1 | 4 +- gfx/cairo/cairo/INSTALL | 71 +- gfx/cairo/cairo/NEWS | 3231 +++++++++- gfx/cairo/cairo/README | 64 +- gfx/cairo/cairo/README.win32 | 66 + gfx/cairo/cairo/src/Makefile.am.analysis | 35 + gfx/cairo/cairo/src/Makefile.am.features | 657 ++ gfx/cairo/cairo/src/Makefile.am.hide | 117 + gfx/cairo/cairo/src/Makefile.in.hide | 3703 +++++++++++ gfx/cairo/cairo/src/Makefile.sources | 461 ++ gfx/cairo/cairo/src/Makefile.win32 | 28 + gfx/cairo/cairo/src/Makefile.win32.features | 661 ++ gfx/cairo/cairo/src/README | 69 + .../src/cairo-analysis-surface-private.h | 5 +- gfx/cairo/cairo/src/cairo-analysis-surface.c | 647 +- gfx/cairo/cairo/src/cairo-arc-private.h | 4 + gfx/cairo/cairo/src/cairo-arc.c | 54 +- gfx/cairo/cairo/src/cairo-array-private.h | 90 + gfx/cairo/cairo/src/cairo-array.c | 180 +- gfx/cairo/cairo/src/cairo-atomic-private.h | 85 +- gfx/cairo/cairo/src/cairo-backend-private.h | 204 + gfx/cairo/cairo/src/cairo-base64-stream.c | 3 +- gfx/cairo/cairo/src/cairo-base85-stream.c | 4 +- .../src/cairo-bentley-ottmann-rectangular.c | 617 +- .../src/cairo-bentley-ottmann-rectilinear.c | 73 +- gfx/cairo/cairo/src/cairo-bentley-ottmann.c | 540 +- gfx/cairo/cairo/src/cairo-beos-surface.cpp | 433 +- .../cairo/src/cairo-botor-scan-converter.c | 19 +- gfx/cairo/cairo/src/cairo-box-inline.h | 131 + gfx/cairo/cairo/src/cairo-boxes-intersect.c | 690 ++ gfx/cairo/cairo/src/cairo-boxes-private.h | 42 +- gfx/cairo/cairo/src/cairo-boxes.c | 242 +- gfx/cairo/cairo/src/cairo-cache-private.h | 4 +- gfx/cairo/cairo/src/cairo-cff-subset.c | 1610 ++++- gfx/cairo/cairo/src/cairo-clip-boxes.c | 609 ++ gfx/cairo/cairo/src/cairo-clip-inline.h | 96 + gfx/cairo/cairo/src/cairo-clip-polygon.c | 156 + gfx/cairo/cairo/src/cairo-clip-private.h | 163 +- gfx/cairo/cairo/src/cairo-clip-region.c | 123 + gfx/cairo/cairo/src/cairo-clip-surface.c | 240 + .../cairo/src/cairo-clip-tor-scan-converter.c | 1845 ++++++ gfx/cairo/cairo/src/cairo-clip.c | 1732 ++--- .../cairo/src/cairo-cogl-gradient-private.h | 90 + gfx/cairo/cairo/src/cairo-cogl-gradient.c | 678 ++ gfx/cairo/cairo/src/cairo-cogl-private.h | 164 + gfx/cairo/cairo/src/cairo-cogl-surface.c | 4106 ++++++++++++ gfx/cairo/cairo/src/cairo-cogl.h | 86 + gfx/cairo/cairo/src/cairo-color.c | 24 +- ...sort-private.h => cairo-combsort-inline.h} | 23 + gfx/cairo/cairo/src/cairo-compiler-private.h | 65 +- .../src/cairo-composite-rectangles-private.h | 92 +- .../cairo/src/cairo-composite-rectangles.c | 400 +- .../cairo/src/cairo-compositor-private.h | 365 ++ gfx/cairo/cairo/src/cairo-compositor.c | 268 + gfx/cairo/cairo/src/cairo-contour-inline.h | 80 + gfx/cairo/cairo/src/cairo-contour-private.h | 124 + gfx/cairo/cairo/src/cairo-contour.c | 453 ++ gfx/cairo/cairo/src/cairo-d2d-private-fx.h | 1164 ---- gfx/cairo/cairo/src/cairo-damage-private.h | 85 + gfx/cairo/cairo/src/cairo-damage.c | 241 + gfx/cairo/cairo/src/cairo-debug.c | 83 +- .../cairo/src/cairo-default-context-private.h | 68 + gfx/cairo/cairo/src/cairo-default-context.c | 1499 +++++ gfx/cairo/cairo/src/cairo-deflate-stream.c | 7 +- gfx/cairo/cairo/src/cairo-device.c | 34 +- gfx/cairo/cairo/src/cairo-directfb-surface.c | 1917 +----- gfx/cairo/cairo/src/cairo-eagle-context.c | 181 - gfx/cairo/cairo/src/cairo-egl-context.c | 317 + .../{cairo-glitz.h => cairo-error-inline.h} | 29 +- gfx/cairo/cairo/src/cairo-error-private.h | 73 +- gfx/cairo/cairo/src/cairo-error.c | 73 + .../cairo/src/cairo-fallback-compositor.c | 185 + .../src/cairo-features-uninstalled.pc.in | 7 + gfx/cairo/cairo/src/cairo-features-win32.h | 16 - gfx/cairo/cairo/src/cairo-features.pc.in | 12 + gfx/cairo/cairo/src/cairo-fixed-private.h | 59 +- .../cairo/src/cairo-fixed-type-private.h | 2 +- gfx/cairo/cairo/src/cairo-font-face-twin.c | 35 +- gfx/cairo/cairo/src/cairo-font-face.c | 97 +- gfx/cairo/cairo/src/cairo-font-options.c | 142 +- .../cairo/src/cairo-freed-pool-private.h | 14 +- gfx/cairo/cairo/src/cairo-freelist-private.h | 4 +- gfx/cairo/cairo/src/cairo-freelist.c | 12 +- gfx/cairo/cairo/src/cairo-ft-font.c | 1264 ++-- gfx/cairo/cairo/src/cairo-ft-private.h | 14 +- gfx/cairo/cairo/src/cairo-ft.h | 41 +- gfx/cairo/cairo/src/cairo-gl-composite.c | 1364 ++++ gfx/cairo/cairo/src/cairo-gl-device.c | 851 +++ .../cairo/src/cairo-gl-dispatch-private.h | 129 + gfx/cairo/cairo/src/cairo-gl-dispatch.c | 273 + .../cairo/src/cairo-gl-ext-def-private.h | 143 + gfx/cairo/cairo/src/cairo-gl-glyphs.c | 606 +- .../cairo/src/cairo-gl-gradient-private.h | 96 + gfx/cairo/cairo/src/cairo-gl-gradient.c | 339 + gfx/cairo/cairo/src/cairo-gl-info.c | 145 + .../cairo/src/cairo-gl-msaa-compositor.c | 956 +++ gfx/cairo/cairo/src/cairo-gl-operand.c | 793 +++ gfx/cairo/cairo/src/cairo-gl-private.h | 638 +- gfx/cairo/cairo/src/cairo-gl-shaders.c | 1454 +++-- gfx/cairo/cairo/src/cairo-gl-source.c | 113 + .../cairo/src/cairo-gl-spans-compositor.c | 556 ++ gfx/cairo/cairo/src/cairo-gl-surface-legacy.c | 602 ++ gfx/cairo/cairo/src/cairo-gl-surface.c | 1933 +++--- .../cairo/src/cairo-gl-traps-compositor.c | 531 ++ gfx/cairo/cairo/src/cairo-gl.h | 38 +- gfx/cairo/cairo/src/cairo-glitz-surface.c | 2446 ------- gfx/cairo/cairo/src/cairo-glx-context.c | 92 +- gfx/cairo/cairo/src/cairo-gstate-private.h | 63 +- gfx/cairo/cairo/src/cairo-gstate.c | 951 +-- gfx/cairo/cairo/src/cairo-hash.c | 252 +- gfx/cairo/cairo/src/cairo-image-compositor.c | 3176 +++++++++ .../cairo/src/cairo-image-info-private.h | 5 + gfx/cairo/cairo/src/cairo-image-info.c | 161 +- .../cairo/src/cairo-image-mask-compositor.c | 414 ++ gfx/cairo/cairo/src/cairo-image-source.c | 1648 +++++ .../cairo/src/cairo-image-surface-inline.h | 96 + .../cairo/src/cairo-image-surface-private.h | 239 + gfx/cairo/cairo/src/cairo-image-surface.c | 4498 ++----------- gfx/cairo/cairo/src/cairo-line-inline.h | 48 + ...allback-surface.h => cairo-line-private.h} | 25 +- gfx/cairo/cairo/src/cairo-line.c | 307 + gfx/cairo/cairo/src/cairo-list-inline.h | 215 + gfx/cairo/cairo/src/cairo-list-private.h | 167 - gfx/cairo/cairo/src/cairo-lzw.c | 2 +- gfx/cairo/cairo/src/cairo-malloc-private.h | 41 +- gfx/cairo/cairo/src/cairo-mask-compositor.c | 1481 +++++ gfx/cairo/cairo/src/cairo-matrix.c | 450 +- gfx/cairo/cairo/src/cairo-mempool-private.h | 85 + gfx/cairo/cairo/src/cairo-mempool.c | 369 ++ .../cairo/src/cairo-mesh-pattern-rasterizer.c | 941 +++ .../cairo/src/cairo-meta-surface-private.h | 187 - gfx/cairo/cairo/src/cairo-misc.c | 230 +- .../cairo/src/cairo-mono-scan-converter.c | 612 ++ .../cairo/src/cairo-mutex-impl-private.h | 1 - .../cairo/src/cairo-mutex-list-private.h | 3 +- .../cairo/src/cairo-mutex-type-private.h | 1 - gfx/cairo/cairo/src/cairo-no-compositor.c | 107 + gfx/cairo/cairo/src/cairo-no-features.h | 12 - gfx/cairo/cairo/src/cairo-observer.c | 2 + gfx/cairo/cairo/src/cairo-os2-private.h | 2 +- gfx/cairo/cairo/src/cairo-os2-surface.c | 214 +- gfx/cairo/cairo/src/cairo-os2.h | 2 +- .../cairo/src/cairo-output-stream-private.h | 5 + gfx/cairo/cairo/src/cairo-output-stream.c | 86 +- gfx/cairo/cairo/src/cairo-paginated-private.h | 23 +- gfx/cairo/cairo/src/cairo-paginated-surface.c | 266 +- gfx/cairo/cairo/src/cairo-path-bounds.c | 309 +- gfx/cairo/cairo/src/cairo-path-fill.c | 454 +- .../cairo/src/cairo-path-fixed-private.h | 71 +- gfx/cairo/cairo/src/cairo-path-fixed.c | 811 ++- gfx/cairo/cairo/src/cairo-path-in-fill.c | 5 +- gfx/cairo/cairo/src/cairo-path-private.h | 4 +- gfx/cairo/cairo/src/cairo-path-stroke-boxes.c | 711 ++ .../cairo/src/cairo-path-stroke-polygon.c | 1364 ++++ gfx/cairo/cairo/src/cairo-path-stroke-traps.c | 1148 ++++ .../cairo/src/cairo-path-stroke-tristrip.c | 1088 ++++ gfx/cairo/cairo/src/cairo-path-stroke.c | 1252 +--- gfx/cairo/cairo/src/cairo-path.c | 133 +- gfx/cairo/cairo/src/cairo-pattern-inline.h | 65 + gfx/cairo/cairo/src/cairo-pattern-private.h | 375 ++ gfx/cairo/cairo/src/cairo-pattern.c | 4037 ++++++++---- gfx/cairo/cairo/src/cairo-pdf-interchange.c | 1736 +++++ .../cairo/src/cairo-pdf-operators-private.h | 35 +- gfx/cairo/cairo/src/cairo-pdf-operators.c | 397 +- .../cairo/src/cairo-pdf-shading-private.h | 100 + gfx/cairo/cairo/src/cairo-pdf-shading.c | 279 + .../cairo/src/cairo-pdf-surface-private.h | 207 +- gfx/cairo/cairo/src/cairo-pdf-surface.c | 5720 ++++++++++++----- gfx/cairo/cairo/src/cairo-pdf.h | 75 +- gfx/cairo/cairo/src/cairo-pen.c | 121 +- gfx/cairo/cairo/src/cairo-pixman-private.h | 51 + gfx/cairo/cairo/src/cairo-png.c | 279 +- gfx/cairo/cairo/src/cairo-polygon-intersect.c | 1472 +++++ gfx/cairo/cairo/src/cairo-polygon-reduce.c | 1438 +++++ gfx/cairo/cairo/src/cairo-polygon.c | 548 +- gfx/cairo/cairo/src/cairo-private.h | 25 +- .../cairo/src/cairo-ps-surface-private.h | 34 +- gfx/cairo/cairo/src/cairo-ps-surface.c | 3723 +++++++---- gfx/cairo/cairo/src/cairo-ps.h | 8 +- gfx/cairo/cairo/src/cairo-qt-surface.cpp | 591 +- gfx/cairo/cairo/src/cairo-quartz-font.c | 235 +- .../cairo/src/cairo-quartz-image-surface.c | 271 +- gfx/cairo/cairo/src/cairo-quartz-image.h | 7 +- gfx/cairo/cairo/src/cairo-quartz-private.h | 43 +- gfx/cairo/cairo/src/cairo-quartz-surface.c | 3550 ++++------ gfx/cairo/cairo/src/cairo-quartz.h | 20 +- .../cairo/src/cairo-raster-source-pattern.c | 430 ++ .../src/cairo-recording-surface-inline.h | 68 + .../src/cairo-recording-surface-private.h | 67 +- gfx/cairo/cairo/src/cairo-recording-surface.c | 1829 +++++- gfx/cairo/cairo/src/cairo-rectangle.c | 119 +- .../src/cairo-rectangular-scan-converter.c | 142 +- .../cairo/src/cairo-reference-count-private.h | 3 +- gfx/cairo/cairo/src/cairo-region-private.h | 6 + gfx/cairo/cairo/src/cairo-region.c | 74 +- gfx/cairo/cairo/src/cairo-rtree-private.h | 14 +- gfx/cairo/cairo/src/cairo-rtree.c | 127 +- .../cairo/src/cairo-scaled-font-private.h | 71 +- .../src/cairo-scaled-font-subsets-private.h | 136 +- .../cairo/src/cairo-scaled-font-subsets.c | 564 +- gfx/cairo/cairo/src/cairo-scaled-font.c | 644 +- gfx/cairo/cairo/src/cairo-script-private.h | 59 + gfx/cairo/cairo/src/cairo-script-surface.c | 1131 ++-- gfx/cairo/cairo/src/cairo-script.h | 13 +- .../cairo/src/cairo-shape-mask-compositor.c | 340 + gfx/cairo/cairo/src/cairo-slope.c | 4 +- .../src/cairo-spans-compositor-private.h | 111 + gfx/cairo/cairo/src/cairo-spans-compositor.c | 1201 ++++ gfx/cairo/cairo/src/cairo-spans-private.h | 61 +- gfx/cairo/cairo/src/cairo-spans.c | 99 +- gfx/cairo/cairo/src/cairo-spline.c | 83 +- ...iro-skia.h => cairo-stroke-dash-private.h} | 60 +- gfx/cairo/cairo/src/cairo-stroke-dash.c | 96 + gfx/cairo/cairo/src/cairo-stroke-style.c | 60 +- .../cairo/src/cairo-supported-features.h | 25 - .../cairo/src/cairo-surface-backend-private.h | 233 + .../cairo/src/cairo-surface-clipper-private.h | 5 +- gfx/cairo/cairo/src/cairo-surface-clipper.c | 159 +- .../src/cairo-surface-fallback-private.h | 120 +- gfx/cairo/cairo/src/cairo-surface-fallback.c | 1619 +---- ...o-xcb-xrender.h => cairo-surface-inline.h} | 45 +- .../cairo/src/cairo-surface-observer-inline.h | 59 + .../src/cairo-surface-observer-private.h | 208 + gfx/cairo/cairo/src/cairo-surface-observer.c | 2105 ++++++ .../cairo/src/cairo-surface-offset-private.h | 14 +- gfx/cairo/cairo/src/cairo-surface-offset.c | 104 +- gfx/cairo/cairo/src/cairo-surface-private.h | 21 +- .../cairo/src/cairo-surface-snapshot-inline.h | 67 + .../src/cairo-surface-snapshot-private.h | 3 + gfx/cairo/cairo/src/cairo-surface-snapshot.c | 190 +- .../src/cairo-surface-subsurface-inline.h | 72 + .../src/cairo-surface-subsurface-private.h | 6 + .../cairo/src/cairo-surface-subsurface.c | 459 +- .../cairo/src/cairo-surface-wrapper-private.h | 64 +- gfx/cairo/cairo/src/cairo-surface-wrapper.c | 618 +- gfx/cairo/cairo/src/cairo-surface.c | 2910 ++++----- .../cairo/src/cairo-svg-surface-private.h | 9 + gfx/cairo/cairo/src/cairo-svg-surface.c | 499 +- gfx/cairo/cairo/src/cairo-svg.h | 55 +- .../cairo/src/cairo-tag-attributes-private.h | 99 + gfx/cairo/cairo/src/cairo-tag-attributes.c | 721 +++ gfx/cairo/cairo/src/cairo-tag-stack-private.h | 107 + gfx/cairo/cairo/src/cairo-tag-stack.c | 280 + gfx/cairo/cairo/src/cairo-tee-surface.c | 271 +- gfx/cairo/cairo/src/cairo-tee.h | 2 +- gfx/cairo/cairo/src/cairo-time-private.h | 94 + gfx/cairo/cairo/src/cairo-time.c | 225 + .../cairo/src/cairo-tor-scan-converter.c | 1871 +++--- .../cairo/src/cairo-tor22-scan-converter.c | 1712 +++++ gfx/cairo/cairo/src/cairo-toy-font-face.c | 32 +- gfx/cairo/cairo/src/cairo-traps-compositor.c | 2351 +++++++ gfx/cairo/cairo/src/cairo-traps-private.h | 143 + gfx/cairo/cairo/src/cairo-traps.c | 566 +- gfx/cairo/cairo/src/cairo-tristrip-private.h | 94 + gfx/cairo/cairo/src/cairo-tristrip.c | 185 + .../cairo/src/cairo-truetype-subset-private.h | 19 +- gfx/cairo/cairo/src/cairo-truetype-subset.c | 600 +- gfx/cairo/cairo/src/cairo-type1-fallback.c | 48 +- gfx/cairo/cairo/src/cairo-type1-glyph-names.c | 410 ++ gfx/cairo/cairo/src/cairo-type1-subset.c | 1468 +++-- .../src/cairo-type3-glyph-surface-private.h | 8 +- .../cairo/src/cairo-type3-glyph-surface.c | 109 +- gfx/cairo/cairo/src/cairo-types-private.h | 208 +- gfx/cairo/cairo/src/cairo-unicode.c | 37 +- gfx/cairo/cairo/src/cairo-uninstalled.pc.in | 8 + gfx/cairo/cairo/src/cairo-user-font.c | 24 +- gfx/cairo/cairo/src/cairo-version.c | 79 +- gfx/cairo/cairo/src/cairo-version.h | 14 +- gfx/cairo/cairo/src/cairo-vg-surface.c | 204 +- gfx/cairo/cairo/src/cairo-vg.h | 4 +- gfx/cairo/cairo/src/cairo-wgl-context.c | 261 + gfx/cairo/cairo/src/cairo-wideint-private.h | 20 +- .../cairo/src/cairo-wideint-type-private.h | 10 +- gfx/cairo/cairo/src/cairo-wideint.c | 43 +- gfx/cairo/cairo/src/cairo-win32-surface.c | 4102 ------------ gfx/cairo/cairo/src/cairo-win32.h | 50 +- .../cairo/src/cairo-xcb-connection-core.c | 300 + .../cairo/src/cairo-xcb-connection-render.c | 299 + .../cairo/src/cairo-xcb-connection-shm.c | 115 + gfx/cairo/cairo/src/cairo-xcb-connection.c | 1006 +++ gfx/cairo/cairo/src/cairo-xcb-private.h | 801 +++ gfx/cairo/cairo/src/cairo-xcb-resources.c | 281 + gfx/cairo/cairo/src/cairo-xcb-screen.c | 494 ++ gfx/cairo/cairo/src/cairo-xcb-shm.c | 337 + gfx/cairo/cairo/src/cairo-xcb-surface-core.c | 641 ++ .../cairo/src/cairo-xcb-surface-render.c | 4879 ++++++++++++++ gfx/cairo/cairo/src/cairo-xcb-surface.c | 1429 ++-- gfx/cairo/cairo/src/cairo-xcb.h | 20 + .../cairo/src/cairo-xlib-core-compositor.c | 653 ++ gfx/cairo/cairo/src/cairo-xlib-display.c | 612 +- .../src/cairo-xlib-fallback-compositor.c | 248 + gfx/cairo/cairo/src/cairo-xlib-private.h | 364 +- .../cairo/src/cairo-xlib-render-compositor.c | 2022 ++++++ gfx/cairo/cairo/src/cairo-xlib-screen.c | 61 +- gfx/cairo/cairo/src/cairo-xlib-source.c | 1171 ++++ .../cairo/src/cairo-xlib-surface-private.h | 72 +- gfx/cairo/cairo/src/cairo-xlib-surface-shm.c | 1463 +++++ gfx/cairo/cairo/src/cairo-xlib-surface.c | 3887 ++--------- gfx/cairo/cairo/src/cairo-xlib-visual.c | 9 +- gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c | 848 +++ .../cairo/src/cairo-xlib-xrender-private.h | 27 +- gfx/cairo/cairo/src/cairo-xlib.h | 18 + gfx/cairo/cairo/src/cairo-xml-surface.c | 200 +- gfx/cairo/cairo/src/cairo.c | 1858 +++--- gfx/cairo/cairo/src/cairo.h | 985 ++- gfx/cairo/cairo/src/cairo.pc.in | 13 + gfx/cairo/cairo/src/cairoint.h | 1430 ++--- gfx/cairo/cairo/src/check-def.sh | 48 + gfx/cairo/cairo/src/check-doc-syntax.awk | 106 + gfx/cairo/cairo/src/check-doc-syntax.sh | 82 + gfx/cairo/cairo/src/check-headers.sh | 27 + gfx/cairo/cairo/src/check-link.c | 7 - gfx/cairo/cairo/src/check-plt.sh | 28 + .../cairo/src/check-preprocessor-syntax.sh | 59 + gfx/cairo/cairo/src/drm/cairo-drm-bo.c | 99 + .../cairo/src/drm/cairo-drm-gallium-surface.c | 826 +++ .../cairo/src/drm/cairo-drm-i915-glyphs.c | 564 ++ .../cairo/src/drm/cairo-drm-i915-private.h | 1270 ++++ .../cairo/src/drm/cairo-drm-i915-shader.c | 2859 ++++++++ .../cairo/src/drm/cairo-drm-i915-spans.c | 799 +++ .../cairo/src/drm/cairo-drm-i915-surface.c | 2942 +++++++++ .../cairo/src/drm/cairo-drm-i965-glyphs.c | 504 ++ .../cairo/src/drm/cairo-drm-i965-private.h | 737 +++ .../cairo/src/drm/cairo-drm-i965-shader.c | 2825 ++++++++ .../cairo/src/drm/cairo-drm-i965-spans.c | 407 ++ .../cairo/src/drm/cairo-drm-i965-surface.c | 1926 ++++++ .../src/drm/cairo-drm-intel-brw-defines.h | 824 +++ .../src/drm/cairo-drm-intel-brw-eu-emit.c | 1089 ++++ .../src/drm/cairo-drm-intel-brw-eu-util.c | 121 + .../cairo/src/drm/cairo-drm-intel-brw-eu.c | 250 + .../cairo/src/drm/cairo-drm-intel-brw-eu.h | 1044 +++ .../src/drm/cairo-drm-intel-brw-structs.h | 1329 ++++ .../src/drm/cairo-drm-intel-command-private.h | 909 +++ .../cairo/src/drm/cairo-drm-intel-debug.c | 1209 ++++ .../cairo-drm-intel-ioctl-private.h} | 26 +- .../cairo/src/drm/cairo-drm-intel-private.h | 515 ++ .../cairo/src/drm/cairo-drm-intel-surface.c | 454 ++ gfx/cairo/cairo/src/drm/cairo-drm-intel.c | 1347 ++++ gfx/cairo/cairo/src/drm/cairo-drm-private.h | 238 + .../cairo/src/drm/cairo-drm-radeon-private.h | 103 + .../cairo/src/drm/cairo-drm-radeon-surface.c | 454 ++ gfx/cairo/cairo/src/drm/cairo-drm-radeon.c | 331 + gfx/cairo/cairo/src/drm/cairo-drm-surface.c | 369 ++ gfx/cairo/cairo/src/drm/cairo-drm.c | 390 ++ gfx/cairo/cairo/src/filterpublic.awk | 22 - gfx/cairo/cairo/src/meson.build | 321 + .../cairo/src/test-base-compositor-surface.c | 824 +++ ...ce.h => test-compositor-surface-private.h} | 28 +- gfx/cairo/cairo/src/test-compositor-surface.c | 265 + gfx/cairo/cairo/src/test-compositor-surface.h | 71 + gfx/cairo/cairo/src/test-fallback-surface.c | 237 - gfx/cairo/cairo/src/test-meta-surface.c | 342 - .../cairo/src/test-null-compositor-surface.c | 487 ++ .../cairo/src/test-null-compositor-surface.h | 60 + gfx/cairo/cairo/src/test-paginated-surface.c | 56 +- .../src/{ => win32}/cairo-dwrite-font.cpp | 2 +- .../src/{ => win32}/cairo-dwrite-private.h | 2 +- gfx/cairo/cairo/src/win32/cairo-win32-debug.c | 87 + .../cairo/src/win32/cairo-win32-device.c | 202 + .../src/win32/cairo-win32-display-surface.c | 1147 ++++ .../cairo/src/{ => win32}/cairo-win32-font.c | 676 +- .../src/win32/cairo-win32-gdi-compositor.c | 671 ++ .../cairo-win32-printing-surface.c | 1080 ++-- .../src/{ => win32}/cairo-win32-private.h | 269 +- .../src/{ => win32}/cairo-win32-refptr.h | 4 +- .../cairo/src/win32/cairo-win32-surface.c | 337 + .../cairo-win32-system.c} | 8 - 369 files changed, 147260 insertions(+), 46188 deletions(-) create mode 100644 gfx/cairo/cairo/README.win32 create mode 100644 gfx/cairo/cairo/src/Makefile.am.analysis create mode 100644 gfx/cairo/cairo/src/Makefile.am.features create mode 100644 gfx/cairo/cairo/src/Makefile.am.hide create mode 100644 gfx/cairo/cairo/src/Makefile.in.hide create mode 100644 gfx/cairo/cairo/src/Makefile.sources create mode 100644 gfx/cairo/cairo/src/Makefile.win32 create mode 100644 gfx/cairo/cairo/src/Makefile.win32.features create mode 100644 gfx/cairo/cairo/src/README create mode 100644 gfx/cairo/cairo/src/cairo-array-private.h create mode 100644 gfx/cairo/cairo/src/cairo-backend-private.h create mode 100644 gfx/cairo/cairo/src/cairo-box-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-boxes-intersect.c create mode 100644 gfx/cairo/cairo/src/cairo-clip-boxes.c create mode 100644 gfx/cairo/cairo/src/cairo-clip-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-clip-polygon.c create mode 100644 gfx/cairo/cairo/src/cairo-clip-region.c create mode 100644 gfx/cairo/cairo/src/cairo-clip-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-clip-tor-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-cogl-gradient-private.h create mode 100644 gfx/cairo/cairo/src/cairo-cogl-gradient.c create mode 100644 gfx/cairo/cairo/src/cairo-cogl-private.h create mode 100644 gfx/cairo/cairo/src/cairo-cogl-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-cogl.h rename gfx/cairo/cairo/src/{cairo-combsort-private.h => cairo-combsort-inline.h} (80%) create mode 100644 gfx/cairo/cairo/src/cairo-compositor-private.h create mode 100644 gfx/cairo/cairo/src/cairo-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-contour-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-contour-private.h create mode 100644 gfx/cairo/cairo/src/cairo-contour.c delete mode 100644 gfx/cairo/cairo/src/cairo-d2d-private-fx.h create mode 100644 gfx/cairo/cairo/src/cairo-damage-private.h create mode 100644 gfx/cairo/cairo/src/cairo-damage.c create mode 100644 gfx/cairo/cairo/src/cairo-default-context-private.h create mode 100644 gfx/cairo/cairo/src/cairo-default-context.c delete mode 100644 gfx/cairo/cairo/src/cairo-eagle-context.c create mode 100644 gfx/cairo/cairo/src/cairo-egl-context.c rename gfx/cairo/cairo/src/{cairo-glitz.h => cairo-error-inline.h} (76%) create mode 100644 gfx/cairo/cairo/src/cairo-error.c create mode 100644 gfx/cairo/cairo/src/cairo-fallback-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-features-uninstalled.pc.in delete mode 100644 gfx/cairo/cairo/src/cairo-features-win32.h create mode 100644 gfx/cairo/cairo/src/cairo-features.pc.in create mode 100644 gfx/cairo/cairo/src/cairo-gl-composite.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-device.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-dispatch-private.h create mode 100644 gfx/cairo/cairo/src/cairo-gl-dispatch.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-ext-def-private.h create mode 100644 gfx/cairo/cairo/src/cairo-gl-gradient-private.h create mode 100644 gfx/cairo/cairo/src/cairo-gl-gradient.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-info.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-msaa-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-operand.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-source.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-spans-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-surface-legacy.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-traps-compositor.c delete mode 100644 gfx/cairo/cairo/src/cairo-glitz-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-image-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-image-mask-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-image-source.c create mode 100644 gfx/cairo/cairo/src/cairo-image-surface-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-image-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-line-inline.h rename gfx/cairo/cairo/src/{test-fallback-surface.h => cairo-line-private.h} (76%) create mode 100644 gfx/cairo/cairo/src/cairo-line.c create mode 100644 gfx/cairo/cairo/src/cairo-list-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-mask-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-mempool-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mempool.c create mode 100644 gfx/cairo/cairo/src/cairo-mesh-pattern-rasterizer.c delete mode 100644 gfx/cairo/cairo/src/cairo-meta-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mono-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-no-compositor.c delete mode 100644 gfx/cairo/cairo/src/cairo-no-features.h create mode 100644 gfx/cairo/cairo/src/cairo-path-stroke-boxes.c create mode 100644 gfx/cairo/cairo/src/cairo-path-stroke-polygon.c create mode 100644 gfx/cairo/cairo/src/cairo-path-stroke-traps.c create mode 100644 gfx/cairo/cairo/src/cairo-path-stroke-tristrip.c create mode 100644 gfx/cairo/cairo/src/cairo-pattern-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-pattern-private.h create mode 100644 gfx/cairo/cairo/src/cairo-pdf-interchange.c create mode 100644 gfx/cairo/cairo/src/cairo-pdf-shading-private.h create mode 100644 gfx/cairo/cairo/src/cairo-pdf-shading.c create mode 100644 gfx/cairo/cairo/src/cairo-pixman-private.h create mode 100644 gfx/cairo/cairo/src/cairo-polygon-intersect.c create mode 100644 gfx/cairo/cairo/src/cairo-polygon-reduce.c create mode 100644 gfx/cairo/cairo/src/cairo-raster-source-pattern.c create mode 100644 gfx/cairo/cairo/src/cairo-recording-surface-inline.h mode change 100644 => 100755 gfx/cairo/cairo/src/cairo-scaled-font.c create mode 100644 gfx/cairo/cairo/src/cairo-script-private.h create mode 100644 gfx/cairo/cairo/src/cairo-shape-mask-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-spans-compositor-private.h create mode 100644 gfx/cairo/cairo/src/cairo-spans-compositor.c rename gfx/cairo/cairo/src/{cairo-skia.h => cairo-stroke-dash-private.h} (62%) create mode 100644 gfx/cairo/cairo/src/cairo-stroke-dash.c delete mode 100644 gfx/cairo/cairo/src/cairo-supported-features.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-backend-private.h rename gfx/cairo/cairo/src/{cairo-xcb-xrender.h => cairo-surface-inline.h} (67%) create mode 100644 gfx/cairo/cairo/src/cairo-surface-observer-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-observer-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-observer.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-snapshot-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-subsurface-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-tag-attributes-private.h create mode 100644 gfx/cairo/cairo/src/cairo-tag-attributes.c create mode 100644 gfx/cairo/cairo/src/cairo-tag-stack-private.h create mode 100644 gfx/cairo/cairo/src/cairo-tag-stack.c create mode 100644 gfx/cairo/cairo/src/cairo-time-private.h create mode 100644 gfx/cairo/cairo/src/cairo-time.c create mode 100644 gfx/cairo/cairo/src/cairo-tor22-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-traps-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-traps-private.h create mode 100644 gfx/cairo/cairo/src/cairo-tristrip-private.h create mode 100644 gfx/cairo/cairo/src/cairo-tristrip.c create mode 100644 gfx/cairo/cairo/src/cairo-type1-glyph-names.c create mode 100644 gfx/cairo/cairo/src/cairo-uninstalled.pc.in create mode 100644 gfx/cairo/cairo/src/cairo-wgl-context.c delete mode 100644 gfx/cairo/cairo/src/cairo-win32-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-connection-core.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-connection-render.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-connection-shm.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-connection.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-private.h create mode 100644 gfx/cairo/cairo/src/cairo-xcb-resources.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-screen.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-shm.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-surface-core.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb-surface-render.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-core-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-fallback-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-render-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-source.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-surface-shm.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c create mode 100644 gfx/cairo/cairo/src/cairo.pc.in create mode 100755 gfx/cairo/cairo/src/check-def.sh create mode 100644 gfx/cairo/cairo/src/check-doc-syntax.awk create mode 100755 gfx/cairo/cairo/src/check-doc-syntax.sh create mode 100755 gfx/cairo/cairo/src/check-headers.sh create mode 100755 gfx/cairo/cairo/src/check-plt.sh create mode 100755 gfx/cairo/cairo/src/check-preprocessor-syntax.sh create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-bo.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-gallium-surface.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i915-glyphs.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i915-private.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i915-shader.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i915-spans.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i915-surface.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i965-glyphs.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i965-private.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i965-shader.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i965-spans.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-i965-surface.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-defines.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-emit.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-util.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-structs.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-command-private.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-debug.c rename gfx/cairo/cairo/src/{cairo-glitz-private.h => drm/cairo-drm-intel-ioctl-private.h} (69%) create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-private.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-surface.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-private.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-radeon-private.h create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-radeon-surface.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-radeon.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-surface.c create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm.c delete mode 100644 gfx/cairo/cairo/src/filterpublic.awk create mode 100644 gfx/cairo/cairo/src/meson.build create mode 100644 gfx/cairo/cairo/src/test-base-compositor-surface.c rename gfx/cairo/cairo/src/{test-meta-surface.h => test-compositor-surface-private.h} (67%) create mode 100644 gfx/cairo/cairo/src/test-compositor-surface.c create mode 100644 gfx/cairo/cairo/src/test-compositor-surface.h delete mode 100644 gfx/cairo/cairo/src/test-fallback-surface.c delete mode 100644 gfx/cairo/cairo/src/test-meta-surface.c create mode 100644 gfx/cairo/cairo/src/test-null-compositor-surface.c create mode 100644 gfx/cairo/cairo/src/test-null-compositor-surface.h rename gfx/cairo/cairo/src/{ => win32}/cairo-dwrite-font.cpp (99%) rename gfx/cairo/cairo/src/{ => win32}/cairo-dwrite-private.h (99%) create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-debug.c create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-device.c create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-display-surface.c rename gfx/cairo/cairo/src/{ => win32}/cairo-win32-font.c (86%) create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-gdi-compositor.c rename gfx/cairo/cairo/src/{ => win32}/cairo-win32-printing-surface.c (65%) rename gfx/cairo/cairo/src/{ => win32}/cairo-win32-private.h (54%) rename gfx/cairo/cairo/src/{ => win32}/cairo-win32-refptr.h (98%) create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-surface.c rename gfx/cairo/cairo/src/{cairo-system.c => win32/cairo-win32-system.c} (94%) diff --git a/gfx/cairo/cairo/AUTHORS b/gfx/cairo/cairo/AUTHORS index bdde62a31aac..fe5a883e8b87 100644 --- a/gfx/cairo/cairo/AUTHORS +++ b/gfx/cairo/cairo/AUTHORS @@ -4,62 +4,75 @@ Shawn T. Amundson Build fix Olivier Andrieu PNG backend Peter Dennis Bartok Bug fix for clipping Dave Beckett Build fixes, Debian packaging +Kai-Uwe Behrmann SVG bug fixes Christian Biesinger BeOS backend Billy Biggs Pixman code merge. Optimization. Fixes for subtle rendering bugs. Hans Breuer win32 bug fixes, build fixes, and improvements Brian Cameron Flag bug in Sun's X server +Carlos Garcia Campos libspectre integration into the test-suite +Andrea Canciani Bugs, quartz backend improvements and type 6/7 patterns. Damien Carbery Build fixes Andrew Chant Adding const where needed Steve Chaplin Bug fixes for PNG reading Tomasz Cholewo Bug fixes Manu Cornet SVG build fix Frederic Crozat Fix test suite for OPD platforms (IA64 or PPC64) +Julien Danjou XCB fixes Radek Doulík Bug report and test case John Ehresman Build fixes for win32 John Ellson First font/glyph extents functions Michael Emmel DirectFB backend Miklós Erdélyi Fix typo leading to a crash Behdad Esfahbod Huge piles of bug fixes, improvements, and general maintenance +Gilles Espinasse Font related fixes +Larry Ewing Test case for group-clip Brian Ewins ATSUI maintenance (first success at making it really work) Bertram Felgenhauer Fixes for subtle arithmetic errors -Bdale Garbee Provided essential support for cairo achitecture sessions +Damian Frank Build system improvements for win32 +Bdale Garbee Provided essential support for cairo architecture sessions Jens Granseuer Fixes to generate proper compiler flags Laxmi Harikumar Build fix J. Ali Harlow win32 backend updates +Bryce Harrington Test cases, bug/typo fixes Mathias Hasselmann Significant reduction of calls to malloc Richard Henderson "slim" macros for better shared libraries James Henstridge Build fixes related to freetype Graydon Hoare Support for non-render X server, first real text support Thomas Hunger Initial version of cairo_in_stroke/fill +Thomas Jaeger Extended repeat modes for X +Björn Lindqvist Performance test cases Kristian Høgsberg PDF backend, PS backend with meta-surfaces -Amaury Jacquot Documentation review, appplication testing +Amaury Jacquot Documentation review, application testing Adrian Johnson PDF backend improvement Michael Johnson Bug fix for pre-C99 compilers Jonathon Jongsma Fix documentation typos -Øyvind Kolås Bug fixes. Better default values. +Øyvind Kolås OpenVG backend, Bug fixes. Better default values. Martin Kretzschmar Arithmetic fix for 64-bit architectures Mathieu Lacage several bug/typo fixes Dominic Lachowicz PDF conformance fix, fix image surface to zero out contents Alexander Larsson Profiling and performance fixes. +Sylvestre Ledru Static analysis fixes. Tor Lillqvist win32 build fixes, build scripts Jinghua Luo Add bitmap glyph transformation, many freetype and glitz fixes Luke-Jr Build fix for cross-compiling Kjartan Maraas Several fixes for sparse, lots of debug help for multi-thread bugs +Nis Martensen Bug fix for sub paths Jordi Mas Bug fix for cairo_show_text Nicholas Miell Fixes for linking bugs on AMD64 Eugeniy Meshcheryakov PS/PDF font subsetting improvements Zakharov Mikhail Build fix for HP-UX -Christopher (Monty) Montgomery Performnace fix (subimage_copy), multi-thread testing +Christopher (Monty) Montgomery Performance fix (subimage_copy), multi-thread testing Tim Mooney Fix test suite to compile with Solaris compiler Jeff Muizelaar Patient, painful, pixman code merge. Many fixes for intricacies of dashing. Yevgen Muntyan win32 build fix +Ravi Nanjundappa Static analysis fixes, test cases, skia backend update/fixes Declan Naughton Fix documentation typos Peter Nilsson Glitz backend Henning Noren Fix memory leak Geoff Norton Build fixes Robert O'Callahan Const-correctness fixes, several new API functions for completeness (and to help mozilla) Ian Osgood XCB backend maintenance -Benjamin Otte Refinements to cairo/perf timing +Benjamin Otte Refinements to cairo/perf timing, OpenGL backend fixups, random fixes Mike Owens Bug fixes Emmanuel Pacaud SVG backend Keith Packard Original concept, polygon tessellation, dashing, font metrics rewrite @@ -73,14 +86,17 @@ Calum Robinson Quartz backend Pavel Roskin Several cleanups to eliminate warnings Tim Rowley Quartz/ATSUI fixes, X server workarounds, win32 glyph path support, test case to expose gradient regression Soeren Sandmann Lots of MMX love for pixman compositing +Uli Schlachter Some more XCB fixes Torsten Schönfeld Build fixes Jamey Sharp Surface/font backend virtualization, XCB backend Jason Dorje Short Build fixes and bug fixes Jeff Smith Fixes for intricacies of stroking code Travis Spencer XCB backend fix -Bill Spitzak Build fix to find Xrender.h without xrender.pc +Bill Spitzak Build fix to find Xrender.h without xrender.pc, downscaling support Zhe Su Add support for fontconfig's embeddedbitmap option Owen Taylor Font rewrite, documentation, win32 backend +Pierre Tardy EGL support and testing, OpenVG backend +Karl Tomlinson Optimisation and obscure bug fixes (mozilla) Alp Toker Fix several code/comment typos Malcolm Tredinnick Documentation fixes David Turner Optimize gradient calculations @@ -89,7 +105,7 @@ Sasha Vasko Build fix to compile without xlib backend Vladimir Vukicevic Quartz backend rewrite, win32/quartz maintenance Jonathan Watt win32 fixes Peter Weilbacher OS/2 backend -Dan Williams Implemnt MMX function to help OLPC +Dan Williams Implement MMX function to help OLPC Chris Wilson Large-scale robustness improvements, (warn_unsed_result and malloc failure injection) Carl Worth Original library, support for paths, images Richard D. Worth Build fixes for cygwin diff --git a/gfx/cairo/cairo/COPYING b/gfx/cairo/cairo/COPYING index 145e629666ff..f54969f1c947 100644 --- a/gfx/cairo/cairo/COPYING +++ b/gfx/cairo/cairo/COPYING @@ -1,6 +1,6 @@ Cairo is free software. -Every source file in the implementation of cairo is available to be +Every source file in the implementation[*] of cairo is available to be redistributed and/or modified under the terms of either the GNU Lesser General Public License (LGPL) version 2.1 or the Mozilla Public License (MPL) version 1.1. Some files are available under more @@ -13,5 +13,21 @@ conditions of either license: COPYING-LGPL-2.1 COPYING-MPL-1.1 -Please see each file in the implementation for Copyright and licensing -information. +Please see each file in the implementation for copyright and licensing +information, (in the opening comment of each file). + +[*] The implementation of cairo is contained entirely within the "src" +directory of the cairo source distribution. There are other components +of the cairo source distribution (such as the "test", "util", and "perf") +that are auxiliary to the library itself. None of the source code in these +directories contributes to a build of the cairo library itself, (libcairo.so +or cairo.dll or similar). + +These auxiliary components are also free software, but may be under +different license terms than cairo itself. For example, most of the +test cases in the perf and test directories are made available under +an MIT license to simplify any use of this code for reference purposes +in using cairo itself. Other files might be available under the GNU +General Public License (GPL), for example. Again, please see the COPYING +file under each directory and the opening comment of each file for copyright +and licensing information. diff --git a/gfx/cairo/cairo/COPYING-LGPL-2.1 b/gfx/cairo/cairo/COPYING-LGPL-2.1 index b124cf581250..f1ed6182c528 100644 --- a/gfx/cairo/cairo/COPYING-LGPL-2.1 +++ b/gfx/cairo/cairo/COPYING-LGPL-2.1 @@ -3,7 +3,7 @@ Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -490,7 +490,7 @@ notice is found. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA Also add information on how to contact you by electronic and paper mail. diff --git a/gfx/cairo/cairo/INSTALL b/gfx/cairo/cairo/INSTALL index dfff8bebbaa4..dfdc2139ed01 100644 --- a/gfx/cairo/cairo/INSTALL +++ b/gfx/cairo/cairo/INSTALL @@ -66,34 +66,33 @@ More detailed build instructions (NOTE: On Mac OS X, at least, use DYLD_LIBRARY_PATH in place of LD_LIBRARY_PATH above.) - --enable-quartz - --enable-atsui - --enable-xcb - --enable-glitz - --enable-beos - --enable-os2 - --enable-directfb + --enable-XYZ + --enable-XYZ=yes + --enable-XYZ=auto + --enable-XYZ=no + --disable-XYZ - Some of cairo's backends are marked as experimental and will - not be built by default. If you would like to build and - experiment with these backends, you will need to pass one of - the above options to the configure script. You may need to - have certain libraries installed first as discussed in the - dependencies section of the README file. + Cairo's various font and surface backends and other features can be + enabled or disabled at configure time. Features can be divided into + three categories based on their default state: - --disable-xlib - --disable-win32 - --disable-png - --disable-freetype - --disable-ps - --disable-pdf - --disable-svg + * default=yes: These are the recommended features like PNG functions + and PS/PDF/SVG backends. It is highly recommended to not disable + these features but if that's really what one wants, they can be + disabled using --disable-XYZ. - Cairo's configure script detects the libraries needed to build - each stable backend, and when it finds them, enables each - backend. If you would like to override this detection and - disable a backend, (even when it would be possible to build - it), use one of the options above to disable the backend. + * default=auto: These are the "native" features, that is, they are + platform specific, like the Xlib surface backend. You probably + want one or two of these. They will be automatically enabled if + all their required facilities are available. Or you can use + --enable-XYZ or --disable-XYZ to make your desire clear, and then + cairo errs during configure if your intention cannot be followed. + + * default=no: These are the "experimental" features, and hence by + default off. Use --enable-XYZ to enable them. + + The list of all features and their default state can be seen in the + output of ./configure --help. 2) Compile the package: @@ -141,11 +140,11 @@ itself), then you're in the right place and should read on. However, if you don't need such a bleeding-edge version of cairo, then you might prefer to start by building the latest stable cairo release: - http://cairographics.org/releases + https://cairographics.org/releases or perhaps the latest (unstable) development snapshot: - http://cairographics.org/snapshots + https://cairographics.org/snapshots There you'll find nicely packaged tar files that include a configure script so you can go back the the simpler instructions above. @@ -156,27 +155,25 @@ contributions to cairo. Since you're not using a packaged tar file, you're going to need some additional tools beyond just a C compiler in order to compile cairo. Specifically, you need the following utilities: - automake (1.8 or newer) + automake autoconf - libtool + autoheader + aclocal + libtoolize + pkg-config [at least version 0.16] + gtk-doc (recommended) Hopefully your platform of choice has packages readily available so that you can easily install things with your system's package management tool, (such as "apt-get install automake" on Debian or "yum -install automake" on Fedora, etc.). Note that Mac OS X ships with it's -own utility called libtool which is not what you want, (the one you do -want goes by the name of glibtool). +install automake" on Fedora, etc.). Note that Mac OS X ships with +glibtoolize instead of libtoolize. Once you have all of those packages installed, the next step is to run the autogen.sh script. That can be as simple as: ./autogen.sh -Or, if you're using Mac OS X, you'll have to let it know to use -glibtool by instead doing: - - LIBTOOLIZE=glibtoolize ./autogen.sh - But before you run that command, note that the autogen.sh script accepts all the same arguments as the configure script, (and in fact, will generate the configure script and run it with the arguments you diff --git a/gfx/cairo/cairo/NEWS b/gfx/cairo/cairo/NEWS index 9be4062caf21..3eeb1c389e21 100644 --- a/gfx/cairo/cairo/NEWS +++ b/gfx/cairo/cairo/NEWS @@ -1,3 +1,3056 @@ +Release 1.17.4 (2020-11-27 Bryce Harrington ) +======================================================================== + +Thank you to the many people who have contributed the large number of +bug fixes and refinements since 1.17.2. + +A particularly noteworthy improvement in this release is the addition of +the meson build system as an alternative to autotools. Autotools is +still used for producing the releases, so will be the default in the +tarball and presumably will still be preferred by distro packagers of +Cairo. It should be possible to build the release tarball using meson, +but as this is new functionality consider it still a work in progress. +The meson configuration has striven to track the autotools +implementation but be aware there may still be some differences between +the two. + +Continuous Integration configurations have been added that enable +testing on a variety of platforms including Fedora, Windows MSVC, etc. +This work has helped in identifying updates and fixes including +adjusting to changes in API calls in dependencies like rsvg and +fontconfig, and to fix platform-specific build issues. + +The cogl Cairo backend underwent significant development this cycle. +Cogl provides GPU accelerated drawing support. The development work +includes implementation of core functionality, performance +optimizations, and stabilization. + +Subpixel positioning support allows improved glyph outlines with the +Freetype font backend. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.17.4 + +[On a personal note, this will be my last release for Cairo. My Cairo +time availability has been non-existent (particularly this crazy past +year). The release process is well documented and hopefully will help +whomever picks up the baton from here.] + + +Release 1.17.2 (2019-01-31 Bryce Harrington ) +======================================================================== +This snapshot provides the new support for writing floating point +formats as 16 bpc PNGs, with support for RGBA128F and RGB96F formats. +This new feature increases Cairo's pixman version requirement to 0.36.0. + +Beyond this are a range of bugfixes and some work on establishing CI for +Cairo. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.17.2 + +API Changes +----------- +None + +Dependency Changes +------------------ +pixman 0.36.0 + + +Release 1.16.0 (2018-10-19 Bryce Harrington ) +======================================================================== +This new stable release incorporates a number of improvements made in +the four years since 1.14.0. + +Of particular note is a wealth of work by Adrian Johnson to enhance PDF +functionality, including restoring support for MacOSX 10.4, metadata, +hyperlinks, and more. + +Much attention also went into fonts, including new colored emoji glyph +support, variable fonts, and fixes for various font idiosyncrasies. + +Other noteworthy changes include GLESv3 support for the cairo_gl +backend, tracking of SVG units in generated SVG documents, and cleanups +for numerous test failures and related issues in the PDF and Postscript +backends. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.16.0 + +Features and Enhancements +------------------------- +* Add support for OpenGL ES 3.0 to the gl backend. +* The PDF backend has gained support for a range of widely used + features, including thumbnails, page labels, metadata, document + outlines, structured text, hyperlinks, and tags. Tags permit adding + logical info such as headings, tables, figures, etc. that facilitates + indexing, accessibility, text reflow, searching, and extraction of the + tagged items to other software. For details on this new PDF + functionality, see: + https://lists.cairographics.org/archives/cairo/2016-June/027427.html +* Variable font support. Variable fonts are single font files with + various typography characteristics, such as weight or slant, that users + of the font can adjust between two points. Effectively this enables a + single font to behave as multiple fonts. +* Restore MacOSX 10.4 support. Cairo had dropped 10.4 support when + moving to the CoreText API. Now we automatically detect which API to + use via dynamic linking, so can resume supporting this older version + of MacOSX. +* Support colored emoji glyphs, stored as PNG images in OpenType fonts. +* Skia backend is removed +* Use Reusable streams for forms in Level 3 Postscript. +* Add CAIRO_MIME_TYPE_EPS mime type for embedding EPS files. +* Add CCITT_FAX mime type for PDF and PS surfaces +* svg: add a new function to specify the SVG document unit + (Bug #90166) +* Use UTF-8 filenames on Windows + + +API Changes +----------- +Several new APIs were added. No existing APIs were altered. + +New PDF functionality: + + * cairo_pdf_surface_add_outline + * cairo_pdf_surface_set_metadata + * cairo_pdf_surface_set_page_label + * cairo_pdf_surface_set_thumbnail_size + * cairo_tag_begin + * cairo_tag_end + * CAIRO_STATUS_TAG_ERROR + +New error status items for problems relating to PDF tagging: + + * CAIRO_STATUS_WIN32_GDI_ERROR + * CAIRO_STATUS_FREETYPE_ERROR + * CAIRO_STATUS_PNG_ERROR + + New error status items for handling of GDI, libfreetype, and libpng + errors, respectively. + + +Setting up Win32 surfaces for HDC with alpha channels: + + * cairo_win32_surface_create_with_format + + New API for added PDF functionality (see above), and new error + status item for problems relating to PDF tagging. + +Variable fonts: + + * cairo_font_options_get_variations + * cairo_font_options_set_variations + +Tracking units in SVG documents: + + * cairo_svg_surface_set_document_unit + * cairo_svg_surface_get_document_unit + + + +Dependency Changes +------------------ +None + + +Performance Optimizations +------------------------- +None + + +Notable Bug Fixes +----------------- +* Fix thin lines that don't show up when printing in Inkscape due to + overly aggressive culling. + (Bug #77298) +* Fix playback of recording surfaces into PDF surfaces, where objects + with negative coordinates were not getting drawn. To address this, + the coordinate systems for PDF and PS have been changed to match + cairo's coordinate system. This allows recording surfaces to be + emitted in cairo coordinates, and results in the same origin being + used for all operations when using the recording surface XObject. + Test cases for PDF and PS have also been updated accordingly. + (Bug #89232) +* Fix "invalidfont" error on some printers when printing PDFs with + embedded fonts that have glyphs (such as spaces) with + num_contours == 0. (Bug #79897) +* Fix missing glyphs such as thin dashes, which get scaled to 0 in + userspace and thus have their drawing operations culled. (Bug #94615) +* Fix other oddities caused by variously idiosyncratic fonts. +* Fix a data race in freed_pool discovered by Firefox's cairo usage. + The patch adads atomic int load and store functions, with relaxed + memory ordering. (Bug #90318) +* Handle SOURCE and CLEAR operators when painting color glyphs. + (Bug #102661) +* Fix falling back to system font with PDFs using certain embedded + fonts, due to truncated font names. + (Bug #103249) +* Prevent curved strokes in small ctms from being culled from vector + surfaces + (Bug #103071) +* Fix assertion hit with PDFs using Type 4 fonts rendered with user + fonts, due to error when destroying glyph page. + (Bug #103335) +* Prevent invalid ptr access for > 4GB images. + (Bug #98165) +* pdf: Fix internal links pointing to other pages, by pre-calculating + page heights so that link positions can be calculated more accurately. +* Fix error reporting in the xcb backend if fallback fails. Instead of + returning NULL when the X11 server can't do some operation, return a + surface in an error state. +* Clarify documentation regarding device scale inheritance and the units + used in cairo_surface_create_similar_image. + (Bug #99094) +* Call XSync in the xlib backend before setting the error handler to + ignore errors for certain requests, to make sure all pending errors + are handled first. +* Fix regression with text containing space character on Win32. + (Bug: https://gitlab.freedesktop.org/cairo/cairo/issues/339) + +For a more comprehensive listing of fixed bugs, see the release notes for the +individual 1.15.x releases. + + +Release 1.15.14 (2018-09-19 Bryce Harrington ) +============================================================================ +We're nearly ready to finalize the 1.16.0 release, so this snapshot +can be considered a beta for 1.16. + +The most notable change this release is a performance optimization for +windows, discussed below. Other than that, much of the development +focus was on final polish and stability as we prepare for 1.16. + +Some attention went into getting the testsuite passing at least for the +image backend. The Cairo testsuite depends on external software like +Pixman, and changes in the rendering behavior of these dependencies +change test behavior, leading to false positives. + +Results from the Coverity static testing tool were also reviewed. Most +of the issues flagged were false positives, but there were several +legitimate problems found and fixed. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.15.14 + +Features and Enhancements +------------------------- +* Add more FreeeType font color conversions to support COLR/CPAL +* Update test reference images against current pixman + +API Changes +----------- +None + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +Vasily Galkin introduced a Win32 performance optimization for +CAIRO_OPERATOR_SOURCE when copying data from a backbuffer to an argb32 +surface corresponding to a Win32 DC. With this, argb32 drawing should +perform as fast as typical dibsection-buffered GDI drawing. See the +Cairo mailing list for April 2018 for data and discussion of the +performance improvements. + + +Bug Fixes +--------- +* Fix crash when rendering Microsoft's Segoe UI Emoji Regular font. +* Fix build breakage with glesv3 enabled due to non-existant glesv3.pc. +* Fix memory leaks found by Coverity +* Fix incorrect null ptr handling found by Coverity +* Fix test compilation when font-config is disabled +* Use _cairo_malloc instead of malloc (Bug #101547) (CVE-2017-9814) +* Fix assertion failure in the freetype backend (Bug #105746) + + +Release 1.15.12 (2018-04-04 Bryce Harrington ) +======================================================================== +The main focus for this release is the addition of Variable Font +support. Variable fonts are single font files with various typography +characteristics, such as weight or slant, that users of the font can +adjust between two points. Effectively this enables a single font to +behave as multiple fonts. + +The Skia backend is disabled in this release, due to severe bitrot, and +will be removed in future releases. Contact the cairo team if you have +a need of this backend. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.15.12 + +Features and Enhancements +------------------------- +* Variable font support +* Skia backend is disabled + +API Changes +----------- +* cairo_font_options_get_variations() and + cairo_font_options_set_variations() are added. + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Fix errors in csi-trace --help and --version options +* Fix a 'memory leak' in the image compositor, with + pixman_glyph_cache_t. +* Fix access of uninitialized memory found by valgrind + (Bug #91271) +* Fix improper initialization of memory in + _cairo_ft_font_face_create_for_pattern() + (Bug #105084) +* Fix multi-monitor virtual desktop with negative coords on Win32 + (Bug #100793) +* Fix issues occurring with older FreeType versions. + + +Release 1.15.10 (2017-12-07 Bryce Harrington ) +======================================================================== +This release adds GLESv3 support to the cairo_gl backend, adds +tracking of SVG units in generated svg documents, and cleans up numerous +test failures and related issues in the PDF and Postscript backends. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.15.10 + +Features and Enhancements +------------------------- +* Add support for OpenGL ES 3.0 to the gl backend. +* Use Reusable streams for forms in Level 3 Postscript. +* Add CAIRO_MIME_TYPE_EPS mime type for embedding EPS files. +* Add CCITT_FAX mime type for PDF and PS surfaces +* svg: add a new function to specify the SVG document unit + (Bug #90166) +* Use UTF-8 filenames on Windows + +API Changes +----------- +* cairo_svg_surface_set_document_unit() and + cairo_svg_surface_get_document_unit() + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Fix regression in gles version detection +* Fix undefined-behavior with integer math. +* Handle SOURCE and CLEAR operators when painting color glyphs. + (Bug #102661) +* Convert images to rgba or a8 formats when uploading with GLESv2 +* Use _WIN32 instead of windows.h to check for windows build. +* Fix sigabrt printing documents with fonts lacking the mandatory .nodef + glyph. + (Bug #102922) +* Prevent curved strokes in small ctms from being culled from vector + surfaces + (Bug #103071) +* Fix painting an unbounded recording surface with the SVG backend. +* Fix falling back to system font with PDFs using certain embedded + fonts, due to truncated font names. + (Bug #103249) +* Fix handling of truetype fonts with excessively long font names + (Bug #103249) +* Fix race conditions with cairo_mask_compositor_t + (Bug #103037) +* Fix build error with util/font-view +* Fix assertion hit with PDFs using Type 4 fonts rendered with user + fonts, due to error when destroying glyph page. + (Bug #103335) +* Set default creation date for PDFs +* Prevent invalid ptr access for > 4GB images. + (Bug #98165) +* Prevent self-copy infinite loop in Postscript surface. +* Fix padded image crash in Postscript surface. +* Fix annotation bugs in PDFs and related memory leaks +* Fix test failures and other assorted issues in ps and pdf code. +* Fix code generation when using GCC legacy atomic operations + (Bug #103559) +* Fix various compilation warnings and errors. +* Fix various distcheck errors with private symbols, doxygen formatting, + etc. + +Release 1.15.8 (2017-08-29 Bryce Harrington ) +======================================================================== +This small snapshot provides new colored emoji glyph support, and a +handful of minor fixes. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.15.8 + +Features and Enhancements +------------------------- +* Support colored emoji glyphs, stored as PNG images in OpenType fonts. + + +API Changes +----------- +None + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- + +* pdf: Fix internal links pointing to other pages, by pre-calculating + page heights so that link positions can be calculated more accurately. + +* image: Fix crash on negative lengths + +* win32: Fix initialization of mutexes for static builds + +* pdf: Don't emit /PageLabel dict when no labels defined + +* font: Fix color font loading on big-endian systems + +* font: Fix color font support infinite-loop with empty glyphs + +* Fix off by one check in cairo-image-info.c + + + +Release 1.15.6 (2017-06-13 Bryce Harrington ) +======================================================================== +This new snapshot incorporates changes over the past half-year since the +1.15.4 snapshot, including all the fixes from the 1.14 release series. + +The PDF code continues to be enhanced, and we're restored MacOSX 10.4 +support. Font-related fixes and improved error handling for X round out +the release. + +For a complete log of changes, please see + + https://cairographics.org/releases/ChangeLog.1.15.6 + + +Features and Enhancements +------------------------- +* Detect if variable fonts have synthesized bold/italic or non-default + variants, and use a fallback font where needed. + +* Restore MacOSX 10.4 support. Cairo had dropped 10.4 support when + moving to the CoreText API. Now we automatically detect which API to + use via dynamic linking, so can resume supporting this older version + of MacOSX. + + +API Changes +----------- +None + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Fix error reporting in the xcb backend if fallback fails. Instead of + returning NULL when the X11 server can't do some operation, return a + surface in an error state. + +* Call XSync in the xlib backend before setting the error handler to + ignore errors for certain requests, to make sure all pending errors + are handled first. + +* Fix text-glyph-range for quartz-font. Use 0xFFFF instead of 0 for + invalid index tracking. + +* Fix handling of Supplementary Multilingual Plane (SMP) Unicode + characters in quartz-font. + +* Fix various issues in the drm backend including updating API usage and + general code cleanup. + +* Clarify documentation regarding device scale inheritance and the units + used in cairo_surface_create_similar_image. + Bug #99094. + + +Release 1.15.4 (2016-12-9 Bryce Harrington ) +======================================================================= +This new snapshot incorporates changes over the past year since the +1.15.2 snapshot, including all the fixes from the 1.14 release series. + +Of particular note in this snapshot is a wealth of work by Adrian +Johnson to enhance PDF support, as well as numerous bug fixes provided +by him and other contributors. + +For a complete log of changes since the last release, please see: + + https://cairographics.org/releases/ChangeLog.1.15.4 + +Features +-------- +* The PDF backend has gained support for a range of widely used + features, including thumbnails, page labels, metadata, document + outlines, structured text, hyperlinks, and tags. Tags permit adding + logical info such as headings, tables, figures, etc. that facilitates + indexing, accessibility, text reflow, searching, and extraction of the + tagged items to other software. For details on this new PDF + functionality, see: + + https://lists.cairographics.org/archives/cairo/2016-June/027427.html + + +API Changes +----------- + + cairo_win32_surface_create_with_format + + Added a cairo API to set up Win32 surfaces for HDC with alpha channels. + + cairo_pdf_surface_add_outline + cairo_pdf_surface_set_metadata + cairo_pdf_surface_set_page_label + cairo_pdf_surface_set_thumbnail_size + cairo_tag_begin + cairo_tag_end + CAIRO_STATUS_TAG_ERROR + + New API for added PDF functionality (see above), and new error + status item for problems relating to PDF tagging. + + CAIRO_STATUS_WIN32_GDI_ERROR + CAIRO_STATUS_FREETYPE_ERROR + CAIRO_STATUS_PNG_ERROR + + New error status items for handling of GDI, libfreetype, and libpng + errors, respectively. + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Bug fixes from 1.15.2 (see the 1.15.2 NEWS for details) + +* Fix playback of recording surfaces into PDF surfaces, where objects + with negative coordinates were not getting drawn. To address this, + the coordinate systems for PDF and PS have been changed to match + cairo's coordinate system. This allows recording surfaces to be + emitted in cairo coordinates, and results in the same origin being + used for all operations when using the recording surface XObject. + Test cases for PDF and PS have also been updated accordingly. + (Bug #89232) + +* Fix "invalidfont" error on some printers when printing PDFs with + embedded fonts that have glyphs (such as spaces) with + num_contours == 0. (Bug #79897) + +* Fix missing glyphs such as thin dashes, which get scaled to 0 in + userspace and thus have their drawing operations culled. (Bug #94615) + +* Fix other oddities caused by variously idiosyncratic fonts. + +* Fix deadlock when destruction of a scaled font indirectly triggers + destruction of a second scaled font, causing the global cache to be + locked twice. (Bug #93891) + +* Fix X errors reported to applications when shmdt() is called before + the Attach request is processed, due to missing xcb and xlib calls. + +* Fix random failure in record-paint-alpha-clip-mast test case, caused + by an incorrect assumption that a deferred clear can be skipped. + (Bug #84330) + +* Fix crash when dealing with an XShmGetImage() failure, caused by a + double free in _get_image_surface(). (Bug #91967) + +* Fix invalid execution of ASCII85 data by the PS interpreter that the + image operator didn't use, by flushing the extraneous data after + drawing the image. (Bug #84811) + +* Fix decoding of Adobe Photoshop's inverted CMYK JPEG files in PDF + export. + +* Fix unbounded surface assertion in win32-print code. + +* Fix a data race in freed_pool discovered by Firefox's cairo usage. + The patch adads atomic int load and store functions, with relaxed + memory ordering. (Bug #90318) + +* Cleanup debugging text sent to stdout instead of log. (Bug #95227) + +* Fix build issue when using non-GNU strings utility. (Bug #88639) + +* Fix build of cairo modules as regular modules, not as versioned shared + libraries. (Bug #29319) + +* Fix build on win32 using gcc 5.4. + +* Fix build of script backend to require zlib. + +* Update test suite reference images using Debian Jessie 64-bit and + poppler current as of June, 2016. + +* Various improvements to documentation and tests, compiler warning + fixes, and an assortment of code refactoring and cleanup. + + +Release 1.15.2 (2015-12-10 Bryce Harrington ) +======================================================================== +This release is largely a rollup to include a variety of fixes that +didn't make the cut for the stable 1.14.2 and 1.14.4 releases, as well +as all the fixes from those releases. Notably this includes a highly +requested new API for Win32 surfaces. + +For a complete log of changes since the last release, please see: + + https://cairographics.org/releases/ChangeLog.1.15.2 + +Features +-------- +None + +API Changes +----------- + + cairo_win32_surface_create_with_format + + Added a cairo API to set up Win32 surfaces for HDC with alpha channels. + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* All the bug fixes from 1.14.2, 1.14.4, and 1.14.6 + +* Fix xcb/xlib compilation and calls. Make image boxes behave when SHM + is not available. + +* Fix various issues with printing and transparent images on Win32. + +* Fix thin lines that don't show up when printing in Inkscape due to + overly aggressive culling. + (Bug #77298) + +* Fix broken printing via pdf when glyph 0 is used for rendering, + resulting in missing spaces and letters. + (Bug #89082) + +* Fix crash for certain glyphs in opentype fonts. + (Bug #91902) + +* Fix incorrect rendering of SVG paths with more than one subpath. If + more than one trap is passed in then it's guaranteed that the returned + traps will have their left edge to the left of their right edge, but + if only one trap is passed in then the function always returns without + doing anything. + (Bug #90984) + +* Improve rendering with Quartz to better match pixman's blending and + filtering behavior. + + +Release 1.14.6 (2015-12-09 Bryce Harrington ) +======================================================================== +Simple bugfix release to fix one Windows issue. + +For a complete log of changes since 1.14.4, please see: + + https://cairographics.org/releases/ChangeLog.1.14.6 + +Features +-------- +None + +API Changes +----------- +None + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Fix failure on Windows due to reference of the function + cairo_win32_surface_create_with_format(), which isn't included in the + 1.14.4 release. (Bug #92771) + + +Release 1.14.4 (2015-10-28 Bryce Harrington ) +======================================================================== +Just in time for Halloween we see another bug-fix release for Cairo. +This brings a few dozen straightforward bug fixes with no API changes. + +In addition, this includes a typical assortment of fixes to tests, +cleanup of warnings and memory leaks, correction of misspellings, +updates to documentation, etc. + +For a complete log of changes since 1.14.2, please see: + + https://cairographics.org/releases/ChangeLog.cairo-1.14.4 + +Features +-------- +None + +API Changes +----------- +None + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- +None + +Bug Fixes +--------- +* Avoid appending empty slots to user data arrays. Fixes a memory + consumption regression since commit 9341c254a. + +* Return a better error (file-not-found) when setting up pango on + devices where the font files don't have read permissions. + +* Fix regression in the font size of canvas text in Inkscape when + compiled with the Quartz backend. (Bug #84324) + +* Fix _cairo_gl_shader_bind_matrix() to maintain compatibility with + OpenGL ES 2.0. Manually transpose the matrix. + +* Fix incorrect font descriptor conversion when the font matrix yy is + negative. (Bug #90538) + +* Fix crash when using a complex path for clip and stroke due to + discarding the intersection exactly at the top edge. + (Bug #74779) + +* Fix cairo_get_locale_decimal_point() on Android + +* Fix compilation problem on AIX due to conflicting usage of symbol + 'jmpbuf'. (Bug #89339) + +* Fix broken rendering with XCB due to snapshotting of uploaded part of + surfaces. (Bug #67505) + +* Fix loss of alpha when copying a mask for a cairo recording surface, + resulting in a double copy. (Bugs #73038, #73901) + +* Fix incorrect recording of certain paths with script surfaces. + (Bug #91054) + +* Fix typo in definition of MAYBE_WARN in configure script. + (Bug #89750) + +* Fix use of filename variable after it's been freed. + (Bug #91206) + +* Fix out of bounds access when printing pattern. + (Bug #91266) + +* Fix incorrect size calculation in glyph cache unlocking for Cairo GL + compositor. + (Bug #91321) + +* Fix memory leak in _cairo_gl_pattern_texture_setup() + (Bug #91537) + +* Fix transparent images in win32-print. + (Bug #91835) + +* Fix _put_shm_image_boxes and _put_image_boxes when no SHM available + with XCB. + + +Release 1.14.2 (2015-03-09 Bryce Harrington ) +==================================================================== +This release provides collected bug fixes, along with one feature +enhancement for the xcb backend, and a small performance improvement for +fonts. + +The running theme of the bug fixes is platform-specific issues, both +build and run-time. Platforms with fixes include Sparc, AIX, Windows +(mingw), and Windows (MSVC8). Memory leaks, valgrind issues, and PDF +issues round out our list. + +It's come to light that changes in cairo 1.14 resulted in breakage on +MacOS X 10.4. We've not yet determined whether to fix up the support, +or excise the 10.4-specific code and support only OS X 10.5 or newer. +Meantime, we'll only advertise cairo as working on OS X 10.5. + +Features +-------- + * Improve xcb's handling of per-screen subpixel ordering. If no + Xft.rgba property is specified, default to the screen's subpixel + order. + +API Changes +----------- +None + +Dependency Changes +------------------ +None + +Performance Optimizations +------------------------- + * Improve performance of cpu_to_be32 and be32_to_cpu, making truetype + subsetting of large fonts run about 15% faster. + +Bug Fixes +--------- + * Fix unaligned access on sparc with the compact font format (CFF). + Unlike truetype, all data in CFF is not aligned. + (Debian bug #712836) + * Fix unaligned access on sparc with tor-scan-converter's memory pool. + * Fix crash when loading a PDF with a transformed image. + (fdo bug #85151) + * Fix regression on mingw for bigendian test due to removal of file + extension for executables. + (fdo bug #85120) + * Fix handling of backslash in PDF interpreter + (fdo bug #85662) + * Fix crash in xlib and xcb renderers when swapping a 0-sized glyph + * Fix bug with RTL text in PDF operators + (fdo bug #86461) + * Fix compilation 'cairo-path-stroke-traps.c' with MSVC8 + (fdo bug #84908) + * Fix crash in _fill_xrgb32_lerp_opaque_spans when a span length is + negative. + * Fix valgrind error by releasing pattern created by + cairo_pattern_create_rgb(). + * Fix valgrind errors when running cairo-test-suite. + * Fix memory leak in recording surface replays + (fdo bug #87898) + * Fix destruction of fonts in api-special-cases test. + (fdo bug #87567) + * Fix duplicated surface push on similar-image, preventing trivial GTK3 + program traces from being replayable, with an error message about + invalid values for the size of the input. + (fdo bug #73580) + * Fix crash when win32 surface's image size does not cover the entire + surface. + (fdo bug #53121) + * Fix crash due to obsolete CGFontGetGlyphPath call + (fdo bug #84324) + * Fix several build issues on AIX + (fdo bugs #89338, #89340, #89356, #89354) + * Fix various documentation warnings and errors + + +Release 1.14.0 (2014-10-13 Bryce Harrington ) +==================================================================== +Hard to believe it's been over a year since our last release, but it's +not for lack of activity. This release includes contributions of a wide +assortment of bug fixes, build system improvements, warnings cleanups, +codebase refactoring, test suite repairs, and static analysis work. + +This release is lighter on features (compared with 1.12.10) but includes +a highly demanded rehaul of our image downscaling functionality, which +solves a serious problem experienced by Inkscape users when shrinking +embedded bitmaps in SVG files. The new scaling algorithms are used by +the image backend and by other backends as needed for fallbacks. + + +Features +-------- + + Filtering improvements for the image backend, in particular + down-scaling of images produces filtered images that depend on all the + pixels of the source. When using the image backend you get the + following settings: + + CAIRO_FILTER_GOOD: uses a box filter for scales less than .75 in + either direction. For scales larger than this, the same filter as + CAIRO_FILTER_BILINEAR is used. + + CAIRO_FILTER_BEST: uses a Catmull-Rom filter always. When upscaling + more than 2x this will produce anti-aliased square pixels, similar + to OS/X. + + CAIRO_FILTER_GAUSSIAN: uses PIXMAN_FILTER_BEST, which in current + pixman is the same as BILINEAR. (This is subject to change in the + future). + + xlib and xcb also use the image fallback for GOOD/BEST filters, but + note that other backends do not implement these filtering fixes yet, + however other actions may cause them to use an image fallback which + will cause these filters to be used. + + Improve handling of device transformation and scaling, allowing Cairo + to now support scaling at a device level, permitting easier, more + transparent HiDPI support. + + Support JBIG2 mime data in PDF. This allows embedding of more + compressed JPEG formats within PDF, rather than including the full + uncompressed image. Also, reduce the number of transparency groups + used by PDF to keep the file size small and viewing/printing of the + PDF fast. + + Expand the embedding section to include stencil mask support. + + Reorder font declarations to be in natural order. + + Update the Skia backend to build against current Skia (as of June + 2014). + + Drop Link-Time Optimization (LTO) support from build system. This + seems to have caused much trouble for unclear benefit, and most + distros are reverting or disabling it anyway. + + Optimize VBO size on GL to 1M and to 16k for EGL. This improves + (theoretical) performance for desktop GLX use cases while avoiding + hitting VBO memory size limitations on embedded devices. + +API Changes +----------- + + cairo_surface_set_device_scale, cairo_surface_get_device_scale: + + Sets a scale that is multiplied to the device coordinates + determined 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. + + cairo_egl_device_get_display, cairo_egl_device_get_context: + + Support get/set of EGLContext and EGLDisplay for egl-based cairo + devices, similar to GLX. + +Dependency Changes +------------------ + + Cairo now requires glib 2.14 for its gobject helper functions, + and pixman 0.30 for downscaling. + + +Bug fixes +--------- + + Don't embed CMYK Jpeg images in svg. + + Fix tests to place output in proper location. + + Fix determination of alpha for all surfaces when recording. + + Extend oversize check to cairo_gl_surface_create_for_texture, so an + error surface is returned if the texture is too large to render to. + + Fix embedding of mime data in PDF and PS files. + + Remove useless error handling in *_reply() functions in XCB. + + Fix a double-free exposed by multithreaded apps creating and + destroying the same font concurrently. + https://bugs.freedesktop.org/show_bug.cgi?id=69470 + + Fix corrupt stacks produced by bugs in operand emission for trace. + + Fix out of bounds array access in format cache for xlib + + Don't rename glyphs used by seac operator. This can cause certain + combined characters to use their decorations (e.g. umlauts on ö) to be + lost during printing of PDFs using evince. + https://bugs.freedesktop.org/show_bug.cgi?id=70364 + + Fix crash on calling cairo_create with a finished surface + + Fix SSIZE_T definition problem when making with MSYS on Windows7 + + Fix one off issue in gl context cleanup + + Fix usage of CAIRO_STACK_ARRAY_LENGTH + + Fix rectangle stroke with non rectilinear pen + + Fix imagemask with pattern source failure on some printers. This bug + could cause files converted using pdftops to fail for example on Ricoh + printers, or opening in Adobe Distiller on Windows. + https://bugs.freedesktop.org/show_bug.cgi?id=69485 + + Fix whitespace in font names + + Fix page size in generated PDFs. When printing using pdftocairo on + larger page sizes, such as 11x17, the image would be cropped to letter + size. + https://bugs.freedesktop.org/show_bug.cgi?id=73452 + + Fix path-currentpoint test by preserving current-point in + copy_path()/append_path() sequence + + Fix generation of HTML in code docs for + cairo-format-stride-for-width. Raw HTML code was being passed + to the browser, instead of displaying normally. + https://bugs.freedesktop.org/show_bug.cgi?id=63257 + + Fix spelling of "tessellator" throughout code. We're using the + American rather than British spelling of this word. + https://bugs.freedesktop.org/show_bug.cgi?id=50411 + + Fix crash in pixman_image_composite32 + + Fix crash when trying to modify a (const) all-clipped cairo_clip_t + https://bugs.freedesktop.org/show_bug.cgi?id=75819 + + Add check_composite method to all compositors, to fix crashes in the + test suite. + + Fix crash in Firefox when scrolling on certain pages. + + Fix memory leaks found by static analysis. + + Fix build of any2ppm if fork is not available. + + Fix broken build for Qt backend, due to missing libstdc++. + + Fix typo in two cairo_uint128 functions. Fixes potential build issues + on systems without a uint128 type. + + Fix build when --enable-pdf=no + + Fix cache_frozen assertions for Win32 print. + + Correctly check for xcb image surface for inplace upload + + Fix webkit-based web browser crashes due to empty boxes by skipping + over them when tesselating. + + Make pixman, libpng, and zlib paths commandline configurable for win32 + builds. + + Fix image scale on Win32 when GDI scale is not identity. + + Fix float endian configure test when using clang -O4 + + Fix compilation with Android bionic libc + + Don't try to build util/sphinx on Windows + + Fix loss of precision when emitting joins. This was caused by + discrepancies in line gradients when passing trapezoids around. + + Fix loss of precision and associated rendering issues in + cairo-tor-scan-converter from projection onto sample grid. + + Fix pixman oversampling of neighbouring edges within a cell by + eliminating self-intersections for the pixman traps compositor. + + Fix multi-line string splitting in PDFs + + Various cleanups and fixes to warnings, documentation, tests, and + build system. Improve error handling and return value checks. + Cleanup XFAIL tests and reference images. Cover recently added + functionality. + + +Release 1.12.16 (2013-08-21 Chris Wilson ) +=================================================================== +Thanks to everybody who reported a bug and helped us develop a fix, +we have amassed quite a few bug fixes. There are still more outstanding +bugs that seek attention and a little bit of TLC, but this release has +been delayed long enough... + +Bug fixes +--------- + + Set the correct orientation for simple boxes with a negative scale + factor. + + Fix the creation of the shading dictionary in PDF. + + Fix a crash in PDF when incorporating an image with CAIRO_EXTEND_PAD. + https://bugs.freedesktop.org/show_bug.cgi?id=61451 + + Avoid upscaling bitmap fonts if possible. + + Fix an assertion failure within the mempool allocator for shared memory. + + Fix allocation size for CFF subsets. + + Export cairo_matrix_t for GObject bindings. + + Fix a double free in the Quartz backend. + https://bugs.freedesktop.org/show_bug.cgi?id=62885 + + Fix origin of GDI StretchBlits for the Windows backend + https://bugs.freedesktop.org/show_bug.cgi?id=61876 + + Fix error propagation for requests to create a similar surface with + negative size. + https://bugs.freedesktop.org/show_bug.cgi?id=63196 + + Fix complex clipping of trapezoids with regions + https://bugzilla.gnome.org/show_bug.cgi?id=697357 + + Stop leaking the image data when loading PNGs + + Fix unbounded operations with a clip mask through the span compositor + https://bugs.freedesktop.org/show_bug.cgi?id=61592 + + Add missing checks before rendering to a finished surface - so we return + an error rather than hit an assert. + https://bugs.freedesktop.org/show_bug.cgi?id=68014 + + Prevent an assertion failure when creating similar GL surfaces larger + than supported by hardware. + + Prevent a double free of a similar image under Windows. + https://bugs.freedesktop.org/show_bug.cgi?id=63787 + + +Release 1.12.14 (2013-02-10 Chris Wilson ) +=================================================================== +In the last week we had a few more bugs reported and promptly resolved. +As these are a combination of regressions and stability issues, it is +time for a prompt update and release. Many thanks to everyone for +testing and reporting issues, and helping to make Cairo better. + +Bug fixes +--------- + + Prevent user callbacks accessing user-data during destroy to prevent + use-after-free bugs. + https://bugzilla.mozilla.org/show_bug.cgi?id=722975 + + Use standard names for glyphs in subset fonts (PDF). + https://bugs.freedesktop.org/show_bug.cgi?id=60248 + + Fix detection of Win98. The logic for detecting Win98 (and its broken + AlphaBlend()) was inverted, disabling AlphaBlend() for everyone. + + Prevent numeric overflow from extrapolating polygon edges to the clip + boundary and causing severe render artifacts. + https://bugs.freedesktop.org/show_bug.cgi?id=60489 + + Fix computation of glyph string coordinates when breaking up runs + for xlib. + + Fix an assertion in the win32 backend for failing to clear its + similar-images. + https://bugs.freedesktop.org/show_bug.cgi?id=60519 + + +Release 1.12.12 (2013-01-31 Chris Wilson ) +=================================================================== +The goal of this release is to fix the synchronisation problems that +were exhibited in the SHM transport for cairo-xlib. This cropped up +any place that tried to rapidly push fresh pixel data to the X server +through an ordinary image surface, such as gimp-2.9 and evince. + +Bug fixes +--------- + + Avoid replacing the entire image when uploading subimages + https://bugs.freedesktop.org/show_bug.cgi?id=59635 + + Force synchronisation for scratch SHM image buffers, so that we do + not overwrite data as it is being read by X. + https://bugs.freedesktop.org/show_bug.cgi?id=59635 (also) + + Fix typos in detecting multisampling for the GL (MSAA) backend. + + Fix a memory leak in the GL (MSAA) backend. + + Fix a reference counting bug when mapping a GL surface to an image. + + +Release 1.12.10 (2013-01-16 Chris Wilson ) +=================================================================== +A heap of bug fixes everywhere, and the gradual completion of the MSAA +backend for cairo-gl. Perhaps the most noteworthy set of the bugfixes +was the crusage lead by Behdad Eshfabod to make font handling by +pango/cairo/fontconfig fully threadsafe. This testing revealed a couple +of races that needed fixing in Cairo's scaled-font and glyph cache. + +Bug fixes +--------- + + Append coincident elements to the recording's surface bbtree so that + the list is not corrupted and the overlapping elements lost. + + Fix cairo-trace to correctly record map-to-image/unmap-image and then + replay them. + + Ignore MappingNotifies when running the XCB testsuite as they are sent + to all clients when the keyboard changes. The testsuite would detect + the unexpected event and complain. + + Handle very large images in the XCB backend. + + Fix a memory leak in the xlib/shm layer, and prevent use of the SHM + surfaces after the display is closed. + https://bugs.freedesktop.org/show_bug.cgi?id=58253 + + Handle resizing of bitmap fonts, in preparation for a fix to + fontconfig to correctly pass on the user request for scaling. + + Always include subroutine 4 (hint replacement idion) when subsetting + type 1 fonts in order to prevent a crash in cgpdftops on Mac OS/X + + Fix a couple of typos in the cairo-gobject.h header files for + introspection. + + Prevent a mutex deadlock when freeing a scaled-glyph containing a + recording-surface that itself references another scaled-glyph. + https://bugs.freedesktop.org/show_bug.cgi?id=54950 + + Make scaled-font cache actually thread-safe and prevent + use-after-frees. + + Restore support for older versions of XRender. A couple of typos and a + few forgotten chunks prevented the xlib compositor from running + correctly with XRender < 0.10. Note that there are still a few + regressions remaining. + + +Release 1.12.8 (2012-11-24 Chris Wilson ) +=================================================================== +Another couple of weeks and a few more bugs have been found and fixed, +it is time to push the next point release. Many thanks to everyone who +reported their issues and helped us track down the bugs and helped +testing the fixes. + +Bug fixes +--------- + + Expand the sanity checking for broken combinations of XSendEvent and + ShmCompletionEvent. + + Notice that "The X.Org Foundation" sometimes also identifies itself + as "The Xorg Foundation". + + Handle various ages of libXext and its Shm headers. + + Fix the invalid clipping of the source drawable when using SHM + transport to upload images. + https://bugs.freedesktop.org/show_bug.cgi?id=56547 + + Handle all Type1 postscript operators for better font compatibility. + https://bugs.freedesktop.org/show_bug.cgi?id=56265 + + Fix a couple of memory leaks in Type1 font subsetting + https://bugs.freedesktop.org/show_bug.cgi?id=56566 + + Tighten the evaluation of the start/stop pen vertices, and catch a few + instances where we would use a fan instead of a bevel. + https://bugs.freedesktop.org/show_bug.cgi?id=56432 + + Fix assumption that geometric clipping always succeeds with the + span-compositor. + https://bugs.freedesktop.org/show_bug.cgi?id=56574 + + Fix call to spline intersection when evaluating whether a stoke is + visible. + + Remember to copy inferior sources when using SHM to readback the + surface for use as a source. + +Release 1.12.6 (2012-10-22 Chris Wilson ) +=================================================================== +Thanks to everyone who download cairo-1.12.4 and gave us their feedback. +It truly was invaluable and has helped us to fix many portability issues +that crept in with some of the new features. This release aims to fix +those stability issues and run on a wider range of systems. + +Bug fixes +--------- + + Fix the recording surface to actually snapshot the source and so fix + PDF drawing. + + Calling XSendEvent with an XShmCompletionEvent is incompatabile with + older Xorg servers. + + Reorder CloseDisplay chain so that XShm is not reinstantiated after + shutdown, causing a potential crash if the Display was immediately + recreated using the same memory address. + + Make sure that the Xserver has attached to the SHM segment before + deleting it from the global namespace on systems that do not support + deferred deletion. + + Type1 subsetting support for PDF (and PS) was once again improved to + work with a larger number of PDF readers. + + GLESv2 build fixes and improved support for embedded GPUs. + + Tweak the invisible pen detection for applications that are currently + using too large values for geometric tolerance. + + A build fix for older freetype libraries. + + +Release 1.12.4 (2012-10-05 Chris Wilson ) +=================================================================== +More bugs, and more importantly, more fixes. On the cairo-gl side, we +have refinements to the MSAA compositor which enables hardware +acceleration of comparatively low-quality antialiasing - which is useful +in animations and on very high density screens. For cairo-xlib, we have +finally enabled SHM transport for image transfers to and from the X +server. A long standing required feature, SHM transport offers a notable +reduction in rendering latency by reducing the number of copies +required to upload image data - given hardware and driver support, +cairo-xlib can now perform zero copy uploads onto the GPU. And as usual +Adrian Johnson has been very busy fixing many different corner cases in +cairo-pdf, improving opacity groups and font subsetting. Last, but not +least, for cairo-image Søren Sandmann Pedersen added support for +rendering glyphs to pixman and using that from within cairo. The new +glyph rendering facility reduces the overhead for setting up the +compositing operation, improving glyph thoughput for the image backend +by a factor of about 4. And before he did so, he also fixed up a few +bugs in the existing glyph rendering code. So many thanks to Andrea +Canciani, Adrian Johnson, Chuanbo Weng, Dongyeon Kim, Henry Song, Martin +Robinson, Søren Sandmann Pedersen and Uli Schlachter for their +contributions, finding and fixing bugs. + +Bug fixes +--------- + + Interior boxes were being dropped when amalgamating regions during + tessellation. + https://bugs.freedesktop.org/show_bug.cgi?id=49446 + + Allow building without gtk-doc installed + + Invalid edge generation whilst reducing complex polygons. + https://bugs.freedesktop.org/show_bug.cgi?id=50852 + + Stroking around tight cusps + + Use locale correct formats for reading font subsetting and valid + buffers. + https://bugs.freedesktop.org/show_bug.cgi?id=51443 + + Ensure that the type1 subset includes all the glyph encodings + https://bugs.freedesktop.org/show_bug.cgi?id=53040 + + Upload the whole source for a repeating pattern. + https://bugs.freedesktop.org/show_bug.cgi?id=51910 + + Fix damage tracking to handle continuation chunks correctly and so + prevent crashes on win32. + https://bugs.freedesktop.org/show_bug.cgi?id=53384 + + Avoid emitting miter joins for degenerate line segments + https://bugzilla.mozilla.org/show_bug.cgi?id=407107 + + Convert the relative path semgents into the backend coordinates + and then back again to user coordinates (cairo_copy_path, + cairo_append_path) + https://bugs.freedesktop.org/show_bug.cgi?id=54732 + + Fix extents computations for a degenerate path consisting only of a + move-to + https://bugs.freedesktop.org/show_bug.cgi?id=54549 + + Prevent crashing on a degenerate project edge after polygon + intersection + https://bugs.freedesktop.org/show_bug.cgi?id=54822 + + + +Release 1.12.2 (2012-04-29 Chris Wilson ) +=================================================================== +After such a long gestation period for the release of Cairo 1.12, we +inevitably accumulated a few bugs that were flushed out by broadening the +test base. Thanks to everybody who tried the release, apologies to any one +unfortunate enough to encounter a bug and many thanks for reporting it. As +a result Adrian Johnson, Alexandros Frantzis, Andrea Canciani, Kalev +Lember, Maarten Bosman, Marcus Meissner, Nis Martensen and Uli Schlachter +have squashed many more bugs and improved the documentation. I would +strongly recommend everyone to upgrade to cairo-1.12.2. +-Chris + +Bug fixes +--------- + + Allow applications to create 0x0 xlib surfaces, such as used by LibreOffice. + https://bugs.freedesktop.org/show_bug.cgi?id=49118 + + Trim composite extents for SOURCE/CLEAR operators to the mask. + + Use fallback fonts in PDF for unhandled computed glyph widths + https://bugs.freedesktop.org/show_bug.cgi?id=48349 + + Handle snapshots of recording surfaces for analysing pattern extents. + Fixes a regression of reporting the PDF bounding box as being the page size. + + Fix allocation size for PDF pattern ids. + Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=49089 + + Fix emission of rectilinear dashed segments, with and without scaling, and + application of degenerate line joins. + + Clamp unbounded fixup polygons to the clip extents. + + Prevent infinite loop due to rounding errors whilst incrementing along dashes. + + Prevent overflow for inline a8 span filling. + + Miscellaneous build fixes for Cygwin on Windows and Solaris. + +Release 1.12.0 (2012-03-23 Chris Wilson ) +=================================================================== +It's taken over 18 months, but the wait is finally over. A new cairo release! +We are pleased to announce a new stable release of Cairo that brings many +new features and performance improvements, all whilst maintaining +compatibility with cairo-1.0 and all releases since. We recommend anyone +using a previous release of Cairo to upgrade to 1.12.0. + +The major feature of this release is the introduction of a new procedural +pattern; the mesh gradient. This, albeit complex, gradient is constructed +from a set of cubic Bezier patches and is a superset of all other gradient +surfaces which allows for the construction of incredibily detailed patterns. +In PDF parlance, the mesh gradient corresponds with type 7 patterns. Many +thanks to Andrea Canciani for bringing this to Cairo, and for his work on +making gradient handling robust. + +Not content with just adding another procedural pattern, Cairo 1.12 also +adds new API to create a callback pattern, +cairo_pattern_create_raster_source, that allows the application to +provide the pixel data for the region of interest at the time of +rendering. This can be used for instance, by an application to decode +compressed images on demand and to keep a cache of those decompressed +images, independently of Cairo. When combined with the recording +surface, it should form a useful basis for a deferred renderer. + +With the release of cairo-1.12, we also introduce a new supported +backend for interoperating with X using XCB. Uli Schlachter, also +maintainer of awesome and contributor to libxcb, has volunteered to +maintain cairo-xcb for us. Thanks Uli! + +For cairo-1.12, we have also added some common API to address any +surface as an image and so allow direct modification of the raster data. +Previously, only the Quartz and Win32 backends supported a very narrow +interface to allow for efficient pixel upload. Now with +cairo_surface_create_similar_image, cairo_surface_map_to_image, and +cairo_surface_unmap_image, Cairo exports a consistent method for +treating those surfaces as an image and so allow modification inplace. +These are the same routines used internally, and should support +efficient transfer or direct mapping of the target surfaces as +applicable. + +Another focus over the past year has been to address many performance +issues, without sacrificing the composition model. To accomplish the +goal, once again the rasterisation pipeline was overhauled and made +explicit, giving the backends the freedom to implement their own +specific pipeline whilst also providing a library of common routines +from which to build the pipeline. For instance, this allows the image +backend and the gl backend to composite scan line primitives inplace, +and to then implement custom fallbacks to catch the corner cases that do +not map onto their fastest paths. Similarly, this allows for the Xlib +backend to implement trapezoidation without compromising the other +backends, yet still allow for the pipeline to be used elsewhere for +testing and fallbacks. Clipping was once again overhauled, so that the +common cases for the raster pipelines could be captured and processed +with fast paths with the emphasis on performing geometric clipping to +reduce the frequency of using multi-pass clipmasks. Stroking was made +faster, both by providing specialised fast-paths for simple, yet frequent, +cases (such as stroking around a rectangle) and by reducing the number +of edges generated by the general stroker. + +As part of the focus on performance, Cairo 1.12 introduces some +antialias hints (NONE, FAST, GOOD, BEST) that are interpolated by the +rasterisers to fine tune their performance versus quality. Cairo 1.12 +also introduces a new observation architecture, +cairo_surface_observer_t, which can be used to analyse the amount of +time consumed by drawing commands and help identify inefficiencies in +both Cairo and the application. + +Last, but by no means least, the OpenGL backend has seen significant +work including the port to GLESv2 and the exploitation of advanced +hardware features. Interesting times. + +As always, I would like to thank everyone who contributed to Cairo, +not only through writing code, but also submitting documentation, bug +reports, suggestions and generally having fun with Cairo! In particular +though this release could not have happened without the efforts of +Adrian Johnson, Alexandros Frantiz, Andrea Canicani, Martin Robinson, +Nis Martensen, and Uli Schlachter. Thanks. +-Chris + +Snapshot 1.11.4 (2012-13-12) +============================ +The cairo community is pleased to finally announce the long aniticpated +release candidate for 1.12, 1.11.4, of the cairo graphics library. This +is the first major update to cairo in over a year and brings a large +number of new features; undoubtably a few bugs as well. + +While many people have contributed and have helped to test the release, +providing feedback on 1.10 and suggesting improvements, this release +is the result of a few persevering souls who deserve recognition for their +outstanding contributions: Andrea Canciani (all round bug fixing, +performance tuning and master of the gradients), Adrian Johnson (PDF +supremo) and Uli Schlachter (who stepped forward as maintainer for the +XCB backend). + +Major additions since 1.11.2: + + * cairo_surface_map_to_image API for pixel level access to any surface + + * New antialias hints to control the trade-off between speed and quality + + * A callback pattern, cairo_pattern_create_raster_source, for lazy + decoding of image data. + + * cairo_surface_observer_t, a new type of surface to gather performance + statistics + + * XCB as a supported backend + + * A rewritten compositor pipeline for performance improvements for, but not + limited to, the xlib and image backends. + From ION and PineView through to SandyBridge, every machine I have shows + across the board performance improvement on the cairo-traces: + + i5-2520m gnome-system-monitor: 5.97x speedup + pnv gnome-system-monitor: 4.86x speedup + i5-2520m firefox-asteroids: 4.66x speedup + pnv firefox-asteroids: 4.43x speedup + image firefox-canvas: 3.82x speedup + i5-2520m firefox-canvas-alpha: 3.49x speedup + image firefox-asteroids: 2.87x speedup + pnv firefox-talos-svg: 2.83x speedup + ion grads-heat-map: 2.75x speedup + pnv firefox-canvas-alpha: 2.66x speedup + image gnome-system-monitor: 2.66x speedup + image swfdec-giant-steps: 2.46x speedup + image firefox-canvas-alpha: 2.14x speedup + i5-2520m firefox-talos-svg: 2.03x speedup + image grads-heat-map: 2.02x speedup + ion gnome-system-monitor: 2.00x speedup + pnv firefox-particles: 1.99x speedup + i5-2520m grads-heat-map: 1.96x speedup + pnv firefox-canvas: 1.92x speedup + ion firefox-particles: 1.80x speedup + image poppler-reseau: 1.77x speedup + pnv xfce4-terminal-a1: 1.72x speedup + image firefox-talos-svg: 1.65x speedup + pnv grads-heat-map: 1.63x speedup + i5-2520m firefox-canvas: 1.63x speedup + pnv swfdec-youtube: 1.62x speedup + image ocitysmap: 1.59x speedup + i5-2520m firefox-fishbowl: 1.56x speedup + i5-2520m poppler-reseau: 1.50x speedup + i5-2520m evolution: 1.50x speedup + i5-2520m midori-zoomed: 1.43x speedup + pnv firefox-planet-gnome: 1.42x speedup + i5-2520m firefox-talos-gfx: 1.41x speedup + i5-2520m gvim: 1.41x speedup + pnv ocitysmap: 1.37x speedup + image poppler: 1.31x speedup + ion firefox-canvas-alpha: 1.35x speedup + ion firefox-talos-svg: 1.34x speedup + i5-2520m ocitysmap: 1.32x speedup + pnv poppler-reseau: 1.31x speedup + i5-2520m firefox-planet-gnome: 1.31x speedup + pnv firefox-fishbowl: 1.30x speedup + pnv evolution: 1.28x speedup + image gvim: 1.27x speedup + i5-2520m swfdec-youtube: 1.25x speedup + pnv gnome-terminal-vim: 1.27x speedup + pnv gvim: 1.25x speedup + image firefox-planet-gnome: 1.25x speedup + image swfdec-youtube: 1.25x speedup + ... + +And a plethora of minor improvements everywhere! +-Chris + +Snapshot 1.11.2 (2011-01-23) +=========================== + +In this first snapshot along the way to cairo-1.12.0, we are very excited +to announce the introduction of Bezier surface gradients, known as type +6/7 gradients in PS/PDF parlance. This is the culmination of much work by +the dynamic duo: Adrian Johnson and Andrea Canciani. Thanks guys! + +Also, I want to warmly welcome Uli Schlachter who recently joined the +Cairo community on a mission. That mission is to make cairo-xcb a +supported backend for 1.12. And for this snapshot he has made great +strides in fixing all the bugs I had left behind. Thanks Uli! + +And we have also seen a new contributor, Alexandros Frantzis, who has +begun bringing up cairo-gl for GLESv2 devices. Thanks Alex! + +And lastly, I must also thank Adrian and Andrea for the vast numbers of +bugs that they have tackled between them, fixing all those little corner +cases that lie hidden until too late. + +API additions: + +The ability to construct piece-wise Bezier surface gradients: + + cairo_pattern_create_mesh + + constructs a pattern of type CAIRO_PATTERN_TYPE_MESH using + + cairo_pattern_mesh_begin_patch + cairo_pattern_mesh_end_patch + cairo_pattern_mesh_curve_to + cairo_pattern_mesh_line_to + cairo_pattern_mesh_move_to + cairo_pattern_mesh_set_control_point + cairo_pattern_mesh_set_corner_color_rgb + cairo_pattern_mesh_set_corner_color_rgba + cairo_pattern_mesh_get_patch_count + cairo_pattern_mesh_get_path + cairo_pattern_mesh_get_corner_color_rgba + cairo_pattern_mesh_get_control_point + +The introduction of a unique ID accessible via the mime data type: + CAIRO_MIME_TYPE_UNIQUE_ID + + + + + +Release 1.10.2 (2010-12-25 Chris Wilson ) +=================================================================== +The cairo community is pleased to announce the 1.10.2 release of the +cairo graphics library. This is the first update to cairo's stable 1.10 +series and contains a large number of bug fixes. + +While many people have contributed and have help to test the release, +2 people deserve special recognition for their efforts in tracking down +and fixing bugs, Andrea Canciani and Adrian Johnson. Thanks to their +tremendous efforts, and of all cairo contributors, it is much +appreciated. + +We recommend everyone upgrade to cairo 1.10.2 and hope that everyone +will continue to have lots of fun with cairo! + +-Chris + +Bug fixes +--------- + + Fix embedding of grayscale jpegs in PS. + https://bugs.freedesktop.org/show_bug.cgi?id=31632 + + Fix the reported path of extents containing a curve. + + Fix the compositing of unaligned boxes. + + Reset the clipper in PDF upon finish. + + Fix degenerates arcs to become a degenerate line. + + Build support for autoconf 2.67 + + Fix painting of transformed patterns in PS + + Fix the EPS bounding box for PS + https://bugs.freedesktop.org/show_bug.cgi?id=24688 + + Fix the missing content for EPS + https://bugs.freedesktop.org/show_bug.cgi?id=24688 + + Fix regression upon changing page size in PS/PDF + https://bugs.freedesktop.org/show_bug.cgi?id=24691 + + Only use ActualText with PDF-1.5 documents + + Fix the bbox for type1 fallbacks. + + Reset the color after ending the context in PDF + https://bugs.freedesktop.org/show_bug.cgi?id=31140 + + Fix the advance of subsetted type1 fonts + https://bugs.freedesktop.org/show_bug.cgi?id=31062 + + Fix handling of EXTEND_NONE gradients for PDF + + Restrict in-place optimisation for a8 image masks with SOURCE + + +Release 1.10.0 (2010-09-06 Chris Wilson ) +=================================================================== +The cairo community is astounded (and flabbergast) to finally announce +the 1.10.0 release of the cairo graphics library. This is a major update +to cairo, with new features and enhanced functionality which maintains +compatibility for applications written using any previous major cairo +release, (1.8, 1.6, 1.4, 1.2, or 1.0). We recommend that anybody using +a previous version of cairo upgrade to cairo 1.10.0. + +One of the more interesting departures for cairo for this release is the +inclusion of a tracing utility, cairo-trace. cairo-trace generates a +human-readable, replayable, compact representation of the sequences of +drawing commands made by an application. This can be used to inspecting +applications to understand issues and as a means for profiling +real-world usage of cairo. + +The traces generated by cairo-trace have been collected in + + git://git.cairographics.org/git/cairo-traces + +and have driven the performance tuning of cairo over the last couple of +years. In particular, the image backend is much faster with a new +polygon rasterisation and a complete overhaul of the tessellator. Not +only is this faster, but also eliminates visual artifacts from +self-intersecting strokes. Not only has cairo-trace been driving +performance improvements within cairo, but as a repeatable means of +driving complex graphics it has been used to tune OpenGL, DDX, and +pixman. + +Cairo's API has been extended to better support printing, notably +through the ability to include a single compressed representation of an +image for patterns used throughout a document, leading to dramatic file +size reductions. Also the meta-surface used to record the vector +commands compromising a drawing sequence is now exposed as a +CAIRO_SURFACE_TYPE_RECORDING, along with a new surface that is a child of a +larger surface, CAIRO_SURFACE_TYPE_SUBSURFACE. One typical usage of a +subsurface would be as a source glyph in a texture atlas, or as a +restricted subwindow within a canvas. + +Cairo's API has also resurrected the RGB16 format from the past as +the prevalence of 16-bit framebuffers has not diminished and is a +fore-taste of the extended format support we anticipate in the future. +Increasing cairo's utility, we introduce the cairo_region_t for handling +sets of pixel aligned rectangles commonly used in graphics applications. +This is a merger of the GdkRegion and the pixman_region_t, hopefully +providing the utility of the former with the speed of the latter. + +Furthermore cairo has been reworked to interoperate more closely with +various acceleration architectures, gaining the ability to share +those hardware resources through the new cairo_device_t. For instance, +with the new OpenGL backend that supersedes the Glitz backend, hardware +and rendering operations can be shared between a classic OpenGL +application mixing libVA for the hardware assisted video decode with +cairo for high quality overlays all within the same OpenGL canvas. + +Many thanks for the hard work of Adrian Johnson, Andrea Canciani, Behdad +Esfahbod, Benjamin Otte, Carl Worth, Carlos Garcia Campos, Chris Wilson, +Eric Anholt, Jeff Muizelaar, Karl Tomlinson, M Joonas Pihlaja, Søren +Sandmann Pedersen and many others that have contributed over the last +couple of years to cairo. Thank you all! + +Snapshot 1.9.14 (2010-07-26) +============================ + + A quiet couple of weeks, hopefully Cairo is seeing widescale deployment and + we are being to see the results of the stabilisation effort. Clipping bugs + seems to have been the order of the last couple of weeks, with a couple + reported and duly fixed. Thank you Igor Nikitin and Karl Tomlinsion for + finding those regressions. At this point all that seems to remain to do is + to fix the outstanding regressions in the PDF backend... + +Bugs fixes +---------- + + Clip doesn't work for text on the image backend + https://bugs.freedesktop.org/show_bug.cgi?id=29008 + + Add explicit dependency for cxx + https://bugs.freedesktop.org/show_bug.cgi?id=29114 + + Fix regressions in reporting clip extents + https://bugs.freedesktop.org/show_bug.cgi?id=29120 + https://bugs.freedesktop.org/show_bug.cgi?id=29121 + https://bugs.freedesktop.org/show_bug.cgi?id=29122 + https://bugs.freedesktop.org/show_bug.cgi?id=29124 + https://bugs.freedesktop.org/show_bug.cgi?id=29125 + + +Snapshot 1.9.12 (2010-07-12) +============================ + + A couple of weeks spent fixing those annoying bugs and cleaning up the build + system; the list of outstanding tasks to complete for the stable release is + finally shrinking. The chief bug fixer has been Benjamin Otte who not only + made sure that the public API is consistent and being tested for its + consistency, but also ensured that the documentation was up-to-date and + spent time clarifying cases where even the Cairo developers have come + unstuck in the past. Many thanks, Benjamin. However, he was not alone, + as Andrea Canciani continued his fine work in isolating broken corner cases + and proceeding to fix them, and tidying up the quartz backend. And last, but + definitely not least, M Joonas Pihlaja tried building Cairo across a + perverse range of systems and fixed up all the loose bits of code that came + unravelled. Thanks everybody! + +API Changes +----------- + + cairo_surface_set_mime_data, cairo_surface_get_mime_data: + + The length parameter is now an unsigned long (as opposed to an unsigned + int). The parameter is intended to be an equivalent to a size_t without + requiring POSIX types and be large enough to store the size of the + largest possible allocation. + + cairo_gl_surface_create_for_texture: + + This a new surface constructor for cairo-gl that explicitly enables + render-to-texture for foreign, i.e. application, textures. + + cairo_region_xor, cairo_region_xor_rectangle + + A couple of utility routines add to the region handling interface for + the purpose of replacing existing GdkRegion functionality. + +Bugs fixes +---------- + + https://bugs.launchpad.net/ubuntu/+source/cairo/+bug/600622 + + Inkscape was caught in the act of attempting to modify a finished surface. + Unfortunately, we had the ordering of our guards and assertions wrong and + so an ordinary application error was triggering an assert in Cairo. This + lead Benjamin to add a test case to ensure that the entire public API + could handle erroneous input and then proceeded to fix a whole slew of + uncovered bugs. + + + https://bugs.freedesktop.org/show_bug.cgi?id=28888 + + A regression introduced by the special casing of uploading images to an + xlib surface in-place which was ignoring the translation applied to the + image. + + +Snapshot 1.9.10 (2010-06-26) +============================ + + The first "quick" snapshot in the run up to the stable release. The + last snapshot was picked up by the bleeding edge distributions and so the + bug reports have to started to roll in. The most frequent of these are the + introduction of rendering errors by applications that modify a surface + without subsequently calling cairo_surface_mark_dirty(). Make sure the + application developers are aware of increased reliance on strict use of the + Cairo API before 1.10 is released! + + The usual slew of bugs reported and we would like to thank Zoxc for + contributing the WGL interface for cairo-gl, and finding more build + failures on win32. And it just wouldn't be a 1.9 snapshot unless + Benjamin Otte improved the error handling within cairo-gl, as well as + isolating and fixing some more errors in the test suite. The biggest bug of + the snapshot turned out to be a major sign extension issue that had lain + hidden for many years and was suddenly exposed by incorrectly rounding + rectangles when performing non-antialiased rendering. Also to the relief + of many we have included the downstream patch to honour the user's LCD + filtering preferences for subpixel rendering of fonts. The interface + remains private for the time being, whilst the proposed public API is + finalized. + +API changes +----------- + None. + +Snapshot 1.9.8 (2010-06-12) +=========================== + + One major API changes since the last snapshot, and a whole slew of bugs + fixed and inconsistencies eliminated. Far too many bugs fixed to + individually identify. We need to thank Benjamin Otte for his fantastic + work on the cairo-gl backend making it faster and more robust, Andrea + Canciani for finding so many bugs and developing test cases for them, as + well fixing them. And last but not least we must all thank Adrian Johnson for + continuing to eliminate bugs and improving the PostScript and PDF backends. + + This snapshot represents almost 4 months of bug fixing, bringing Cairo to + a point where we consider it almost ready to be a candidate for release. + There are a few known bugs left to be fixed, being tracked in + https://bugs.freedesktop.org/show_bug.cgi?id=24384, so please give Cairo a + whirl and report any regressions. The plan is to release a new snapshot + every other week leading to a 1.10 release with a target date of + 2010-08-16. + +API additions +------------- + CAIRO_FORMAT_RGB16_565 + + 16 bit devices still remain popular, and so with great demand, + CAIRO_FORMAT_RGB16_565 has been restored enabling applications to create + and use 16 bit images as sources and render targets. + + cairo_surface_create_for_rectangle() + + It is common practice to cut an image up into many smaller pieces and use + each of those as a source - a technique called texture atlasing. + cairo_surface_create_for_rectangle() extends Cairo to directly support use + of these subregions of another cairo_surface_t both as a source and as a + render target. + + cairo_region_create() + cairo_region_create_rectangle() + cairo_region_create_rectangles() + cairo_region_copy() + cairo_region_reference() + cairo_region_destroy() + cairo_region_equal() + cairo_region_status() + cairo_region_get_extents() + cairo_region_num_rectangles() + cairo_region_get_rectangle() + cairo_region_is_empty() + cairo_region_contains_rectangle() + cairo_region_contains_point() + cairo_region_translate() + cairo_region_subtract() + cairo_region_subtract_rectangle() + cairo_region_intersect() + cairo_region_intersect_rectangle() + cairo_region_union() + cairo_region_union_rectangle() + + The Cairo region API was actually added a couple of snapshots ago, but we + forgot to mention it at the time. A simple API for the handling of + rectangular pixel-aligned regions by Soeren Sandmann. + + +Backend-specific improvements +----------------------------- +cairo-gl + + Benjamin Otte made more than 200 commits in which he refactored the cairo-gl + backend, reducing a lot of code duplication and enabled him to begin working + on improving performance by reducing state changes and associated overhead. + +cairo-xlib + + Access to the underlying connection to the Display is now thread-safe + enabling cairo-xlib to be used in a multi-threaded application without fear + of random corruption. Thanks Benjamin Otte! + + cairo-xlib will now attempt to use PolyModeImprecise when compositing + trapezoids (i.e. a fill or a stroke operation with a non-trivial path) which + should allow hardware drivers more scope for accelerating the operation at + the cost of potentially incurring minute rendering errors. The mode can be + forced back to PolyModePrecise by setting the antialias parameter to + CAIRO_ANTIALIAS_SUBPIXEL. + +cairo-svg + + A notable improvement was contributed by Alexander Shulgin to enable SVG to + reference external image through the use an extended MIME data type. + +Snapshot 1.9.6 (2010-02-19) +=========================== +API additions +------------- + Add cairo_device_t + + The device is a generic method for accessing the underlying interface + with the native graphics subsystem, typically the X connection or + perhaps the GL context. By exposing a cairo_device_t on a surface and + its various methods we enable finer control over interoperability with + external interactions of the device by applications. The use case in + mind is, for example, a multi-threaded gstreamer which needs to serialise + its own direct access to the device along with Cairo's across many + threads. + + Secondly, the cairo_device_t is a unifying API for the mismash of + backend specific methods for controlling creation of surfaces with + explicit devices and a convenient hook for debugging and introspection. + + The principal components of the API are the memory management of: + + cairo_device_reference(), + cairo_device_finish() and + cairo_device_destroy(); + + along with a pair of routines for serialising interaction: + + cairo_device_acquire() and + cairo_device_release() + + and a method to flush any outstanding accesses: + + cairo_device_flush(). + + The device for a particular surface may be retrieved using: + + cairo_surface_get_device(). + + The device returned is owned by the surface. + +API changes (to API new in the cairo 1.9.x series) +-------------------------------------------------- + cairo_recording_surface_create() + cairo_recording_surface_ink_extents() + + These are the replacement names for the functions previously named + cairo_meta_surface_create and cairo_meta_surface_ink_extents. + + cairo_surface_set_mime_data + + This interface is now changed such that the MIME data will be + detached if the surface is modified at all. This guarantees that + the MIME data will not become out of synch due to surface + modifications, and also means that for the MIME data to be useful, + it must be set after all modifications to the surface are + complete. + +API removal (of experiment API) +------------------------------- + The cairo-glitz backend is removed entirely, (in favor of the new + cairo-gl backend). See below for more on cairo-gl. + +Generic fixes +------------- + + Many improvements for drawing of dashed strokes + + Fix incorrect handling of negative offset + Faster computation of first dash (avoids near-infinite looping) + Approximate extremely fine dash patterns with appropriate alpha value + + Optimize spans-based renderers for repeated rows, (such as in a rounded rectangle) + +Backend-specific improvements +----------------------------- +cairo-drm + + This is a new, direct-rendering backend that supports Intel graphics + chipsets in the i915 and i965 families. It's still experimental and + will likely remain that way for a while. It's already got extremely + good performance on the hardware it supports, so if nothing else + provides a working proof and performance target for the cairo-gl + work for Intel graphics. + +cairo-gl + + Start using GLSL to accelerate many operations. Many thanks to Eric + Anholt and T. Zachary Laine for this work. For the first time, we + have what looks like what will be a very compelling OpenGL-based + backend for cairo (in terms of both quality and performance). + + See this writeup from Eric for more details on recent progress of + cairo-gl (which he presented at FOSDEM 2010): + + http://anholt.livejournal.com/42146.html + +cairo-image + + The image backend is made dramatically faster (3-5 times faster for + benchmarks consisting primarily of glyph rendering). + +cairo-quartz fixes: + + Many fixes from Robert O'Callahan and Andrea Canciani including: + + Fixed gradient pattern painting + Improved A8 image handling + Fixes for "unbounded" and other compositing operators + +cairo-pdf fixes: + + Improvements to embedding of JPEG and JPEG2000 data. + +cairo-ps fixes: + + Fix printing of rotated user fonts. + +Snapshot 1.9.4 (2009-10-15) +=========================== +API additions: + + cairo_meta_surface_create() + cairo_meta_surface_ink_extents() + + Finally exporting the internal meta-surface so that applications + have a method to record and replay a sequence of drawing commands. + + cairo_in_clip() + + Determines whether a given point is inside the current clip. + ??? Should this be called cairo_in_paint() instead? in-clip is the test + that is performed, but in-paint would be similar to in-fill and in-stroke. + +New utilities: + + cairo-test-trace + + A companion to cairo-perf-trace, this utility replays a trace against + multiple targets in parallel and looks for differences in the output, + and then records any drawing commands that cause a failure. + Future plans: + Further minimisation of the fail trace using "delta debugging". + More control over test/reference targets. + +Backend improvements: + + xlib + + Server-side gradients. The theory is that we can offload computation + of gradients to the GPU and avoid pushing large images over the + connection. Even if the driver has to fallback and use pixman to render + a temporary source, it should be able to do so in a more efficient manner + than Cairo itself. However, cairo-perf suggests otherwise: + + On tiny, Celeron/i915: + + before: firefox-20090601 211.585 + after: firefox-20090601 270.939 + + and on tiger, CoreDuo/nvidia: + + before: firefox-20090601 70.143 + after: firefox-20090601 87.326 + + In particular, looking at tiny: + + xlib-rgba paint-with-alpha_linear-rgba_over-512 47.11 (47.16 0.05%) -> 123.42 (123.72 0.13%): 2.62x slowdown + █▋ + xlib-rgba paint-with-alpha_linear3-rgba_over-512 47.27 (47.32 0.04%) -> 123.78 (124.04 0.13%): 2.62x slowdown + █▋ + + +New experimental backends: + + QT + + OpenVG - The initial work was done by Øyvind Kolås, and made ready for + inclusion by Pierre Tardy. + + OpenGL - An advanced OpenGL compositor. The aim is to write a integrate + directed rendering using OpenGL at a high-level into Cairo. In + contrast to the previous attempt using Glitz which tried to + implement the RENDER protocol on top of OpenGL, using the + high-level interface should permit greater flexibility and + more offloading onto the GPU. + The initial work on the backend was performed by Eric Anholt. + +Long standing bugs fixed: + + Self-intersecting strokes. + + A long standing bug where the coverage from overlapping semi-opaque + strokes (including neighbouring edges) was simply summed in lieu of + a costly global calculation has been fixed (by performing the costly + global calculation!) In order to mitigate the extra cost, the + tessellator has been overhauled and tune, which handles the fallback + for when we are unable to use the new span rasteriser on the stroke + (e.g. when using the current RENDER protocol). The large number of + pixel artifacts that implementing self-intersection elimination + removes is ample justification for the potential performance + regression. If you unfortunately do suffer a substantial performance + regression in your application, please consider obtaining a + cairo-trace and submitting it to us for analysis and inclusion into + our performance suite. + +Special thanks: + + To the AuroraUX team for providing access to one of their OpenSolaris + machines for cairo and pixman development. http://www.auroraux.org/ + +Snapshot 1.9.2 (2009-06-12) +=========================== +API additions: + + cairo_surface_set_mime_data() + cairo_surface_get_mime_data() + + Should this take unsigned int, unsigned long or size_t for the length + parameter? (Some datasets may be >4GiB in size.) + + Associate an alternate, compressed, representation for a surface. + Currently: + "image/jp2" (JPEG2000) is understood by PDF >= 1.5 + "image/jpeg" is understood by PDF,PS,SVG,win32-printing. + "image/png" is understood by SVG. + + cairo_pdf_version_t + cairo_pdf_surface_restrict_to_version() + cairo_pdf_get_versions() + cairo_pdf_version_to_string() + + Similar to restrict to version and level found in SVG and PS, + these limit the features used in the output to comply with the PDF + specification for that version. + + CAIRO_STATUS_INVALID_SIZE + Indicates that the request surface size is not supported by the + backend. This generally indicates that the request is too large. + + CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED + Indicates that a required callback for a user-font was not implemented. + + CAIRO_STATUS_LAST_STATUS + This is a special value to indicate the number of status values enumerated + at compile time. (This may differ to the number known at run-time.) + + The built-in twin font is now called "@cairo:" and supports a limited set + of options like "@cairo:mono". Where are these specified? + + cairo_in_fill() now uses HTML Canvas semantics, all edges are inside. + +New experimental backends: + + CairoScript + +New utility: + + cairo-trace and cairo-perf-trace + + cairo-trace generates a human-readable, replayable, compact(-ish!) + representation of the sequences of drawing commands made by an + application. + + Under the util/cairo-script directory is a library to replay traces. + + perf/cairo-perf-trace replays traces against multiple backends + and makes useful benchmark reports. This is integrated with + 'make perf'. You may collect your own traces or take advantage + of traces collected by the community: + + git://git.cairographics.org/git/cairo-traces + + (Put this into perf/cairo-traces to run these as part of "make perf".) + + There is additional WIP in building a debugging tool for cairo applications + based on CairoScript (currently very preliminary, mostly serves to show + that GtkSourceView is too slow) : + + people.freedesktop.org:~ickle/sphinx + +Test suite overhaul: + + The test suite is undergoing an overhaul, primarily to improve its speed + and utility. (Expect more changes in the near future to improve XFAIL + handling.) + +Optimisations: + polygon rasterisation! Joonas implemented the Tor polygon scan converter, + on typical geometry is about 30% faster for the image backend. + + Bovine Polaroids! For those not in on the joke, this is the long + awaited "copy-on-write snapshot" or "COW snapshot" support. The + user-visible feature is that including the same image multiple times + into a PDF file should result in only a single instance of that + image in the final output. This is unlike previous versions of cairo + which would generate very large PDF files with multiple copies of + the same image. Adrian says that the PDF is not quite working as + well as it should yet, so we hope for further improvements before + cairo 1.10. + +Bug fixes: + + EXTEND_PAD. + + Better handling of large scale-factors on image patterns. + + Emit /Interpolate for PS,PDF images. + + Global glyph cache - cap on the total number of inactive glyphs, + should prove fairer for fonts with larger glyph sets. + + Compilation without fontconfig + + Improved handling of low-bitdepth sources (e.g. copying the contents + of 16-bit xserver windows) + +Regressions: + + cairo_traps_extract_region >10x slower. Fix pending. + +Still to come: + + Region tracking API (ssp) for damage tracking, hit testing etc + mime-surface + + An expiremental OpenGL backend? + + Tweaks to tessellator, allocations of patterns, delayed + initialisation of the xlib backend (reduce the cairo overhead of + render_bench by ~80%). + + +Release 1.8.8 (2009-06-16 Chris Wilson ) +================================================================== +The cairo community is pleased to announce the 1.8.8 release of the +cairo graphics library. This is the fourth update to cairo's stable +1.8 series and contains a small number of bug fixes (in particular a +few corrections to the documentation and a few fixes in the FreeType font +backend). This is being released just over six months after cairo 1.8.6. + +We recommend that everyone using cairo upgrade to 1.8.8. + +-Chris + +Build fixes +----------- +There were reports of incompatibilities with the autotools bundled in with +the 1.8.6 tarball. This release has been built with automake-1.10.2 and +autoconf-2.63. + +The configure check for FreeType has been improved: + + typo in check for version of freetype in configure script + https://bugs.freedesktop.org/show_bug.cgi?id=19283 + +Compilation on 64-bit MacOS/X fixes: + + Cannot build cairo_quartz_font_face_create_for_atsu_font_id on 64-bit Mac OS X + https://bugs.freedesktop.org/show_bug.cgi?id=15702 + +Bug fixes +--------- +Uninitialised status return within _cairo_clip_intersect_mask(). This caused +random crashes and general mayhem as an error could be generated causing all +rendering to the context to stop. + +Avoid transforming nearly-degenerate matrices into degenerate matrices: + + Painting stops in this case, using -moz-transform: scale, rotate and video + https://bugzilla.mozilla.org/show_bug.cgi?id=467423 + +A few FreeType font handling bugs were fixed: + + Rendering with PANGO_GRAVITY_EAST leads to different results with image and pdf + https://bugs.freedesktop.org/show_bug.cgi?id=21985 + + Don't call FT_Done_Face() on faces we did not create + + zombie ft_font_face / ft_unscaled_font mutual referencing problems + https://bugs.freedesktop.org/show_bug.cgi?id=21706 + +Ensure win32 font backend sets the return value to -1 (indicating the absent +glyph) if the font index lookup for the unicode character fails. And +similarly fix a bug where a fatal error was raised for an invalid glyph. + + cairo_scaled_font_glyph_extents breaks with invalid glyph id + https://bugs.freedesktop.org/show_bug.cgi?id=20255 + +Various improvements to the documentation, reported by Truc Troung: + + https://bugs.freedesktop.org/show_bug.cgi?id=20095 + https://bugs.freedesktop.org/show_bug.cgi?id=20154 + https://bugs.freedesktop.org/show_bug.cgi?id=20180 + https://bugs.freedesktop.org/show_bug.cgi?id=20183 + https://bugs.freedesktop.org/show_bug.cgi?id=20182 + https://bugs.freedesktop.org/show_bug.cgi?id=20441 + + +Release 1.8.6 (2008-12-13 Chris Wilson ) +================================================================== +The cairo community is pleased to announce the 1.8.6 release of the +cairo graphics library. This is the third update to cairo's stable +1.8 series and contains a small number of bug fixes (in particular a +few fixes for failures of cairo 1.8.4 on Quartz and PDF, and build fixes for +a couple of backends). This is being released just under a month after +cairo 1.8.4. + +We recommend that everyone using cairo upgrade to 1.8.6. + +-Chris + +Build fixes +----------- +Fix build of DirectFB backend with debugging enabled: + + Bug in _cairo_directfb_surface_release_source_image function + https://bugs.freedesktop.org/show_bug.cgi?id=18322 + +Fix build on OS/2. + +Bug fixes +--------- +Workaround a mis-compilation of cairo_matrix_invert() that generated invalid +matrices and triggered assertion failures later. The issue was reported by +Peter Hercek. + +Invalid computation of the modulus: + + https://bugzilla.mozilla.org/show_bug.cgi?id=466258 + +Invalid referencing of patterns in the Quartz backend: + + Failed assertion `CAIRO_REFERENCE_COUNT_HAS_REFERENCE + (&pattern->ref_count)' when using cairo quartz backend + https://bugs.freedesktop.org/show_bug.cgi?id=18632 + +Invalid references to glyphs after early culling, causing segmentation faults +in the PDF backend: + + https://lists.cairographics.org/archives/cairo/2008-December/015976.html + +Check for XRender in the XCB backend, or else we may attempt an invalid memory +access: + + XCB backend fails with missing render. + https://bugs.freedesktop.org/show_bug.cgi?id=18588 + +Release 1.8.4 (2008-11-14 Carl Worth ) +========================================================= +The cairo community is pleased to announce the 1.8.4 release of the +cairo graphics library. This is the second update to cairo's stable +1.8 series and contains a small number of bug fixes, (in particular a +few fixes for build failures of cairo 1.8.2 on various systems). This +is being released just over two weeks after cairo 1.8.2. + +We recommend that everyone using cairo upgrade to 1.8.4. + +-Carl + +Build fixes +----------- +Fix build with older XRender that doesn't define RepeatNone: + + Build of xlib backend fails against old XRender (RepeatNone undeclared) + https://bugs.freedesktop.org/show_bug.cgi?id=18385 + +Fix build with bash version <= 3.0: + + doltlibtool broken on linux with bash 3.00.0 + https://bugs.freedesktop.org/show_bug.cgi?id=18363 + +Bug fixes +--------- +Avoid triggering a bug in X.org server 6.9 resulting in a hung machine +requiring a reboot: + + https://bugs.freedesktop.org/show_bug.cgi?id=15628#c2 + +Fix display of user fonts as exercised by proposed support for type3 +fonts in poppler (unsigned promotion fixes): + + Use cairo user-font for Type 3 fonts + https://lists.freedesktop.org/archives/poppler/2008-October/004181.html + +Avoid miscomputing size of fallback images required when rendering +with CLEAR, IN, or SOURCE operator to vector surfaces, (PS, PDF, SVG, +etc.). + +Be more tolerant of broken fonts when subsetting type1 fonts: + + Error handling in cairo_type1_font_subset_get_glyph_names_and_widths + https://lists.cairographics.org/archives/cairo/2008-October/015569.html + +Fix cairo_fill_extents, cairo_stroke_extents, cairo_path_extents, to +correctly allow NULL parameters as documented. + +Fix potential crash on emitting a type3 glyph after having drawn text +paths from the same font, (for example with cairo_text_path). + +Release 1.8.2 (2008-10-29 Carl Worth ) +========================================================= +The cairo community is pleased to announce the 1.8.2 release of the +cairo graphics library. This is the first update to cairo's stable 1.8 +series and contains a large number of bug fixes. It is being released +just over one month since cairo 1.8.0. + +This release consists primarily of bug fixes, but there is one notable +new feature, (the ability to build cairo without an external font +backend), and there are a few optimizations as well. See below for +details on these changes and the most important bug fixes. + +While many people have contributed to this release, Chris Wilson +deserves particular mention. He has contributed well over twice as +many changes to cairo since 1.8.0 than everyone else combined. We +greatly appreciate the tremendous efforts of Chris and all cairo +contributors. + +We recommend everyone upgrade to cairo 1.8.2 and hope that everyone +will have lots of fun with cairo! + +-Carl + +New feature +----------- +It is now possible to build cairo without any font backend, (such as +freetype, win32 or quartz). This is most useful when the application +provides custom font rendering through the user-font API. But in the +case where no external font backend is available, and no user-font is +provided, cairo will render with a failsafe font, (a stroked font +covering visible ASCII character). (Behdad Esfahbod) + +Optimizations +------------- +Dramatically speed up compilation with dolt (removes much of the +libtool overhead) (Behdad Esfahbod with thanks to Josh Triplett). + +Several minor optimizations to tessellator (special-cased comparisons, +faster insert for skiplist, etc.) (Chris Wilson). + +Optimize away fractional translation component when doing +EXTEND_NEAREST filtering, (for better performance). + +General bug fixes +----------------- +Allow cloning sub-regions of similar surfaces to fix this bug +(Chris Wilson): + + Crafted gif file will crash firefox + [XError: 'BadAlloc (insufficient resources for operation)'] + https://bugzilla.mozilla.org/show_bug.cgi?id=424333 + +Fix some matrix confusion to fix this regression (Chris Wilson): + + Translucent star exports in a wrong way to PDF + https://bugs.launchpad.net/inkscape/+bug/234546 + +Fix some long-standing bugs with respect to properly computing the +extents of transformed, filtered surfaces (Owen Taylor, Carl Worth, +and Chris Wilson): + + Bad clipping with EXTEND_NONE + https://bugs.freedesktop.org/show_bug.cgi?id=15349 + + Improve filtering handling in cairo-pattern.c + https://bugs.freedesktop.org/show_bug.cgi?id=15367 + + Many thanks to Chris Wilson for digging out and cleaning up + these fixes. + +Fix compilation on Solaris 10 (Chris Wilson): + + Cairo requires -DREENTRANT (along with -D_POSIX_THREAD_SEMANTICS) + to compile on Solaris 10 with pthreads + https://bugs.freedesktop.org/show_bug.cgi?id=18010 + +Fix very old bug causing dashes to be rendered at the wrong length in +fallback images (Adrian Johnson) + + Dashed strokes too long in fallback images + https://bugs.freedesktop.org/show_bug.cgi?id=9189 + +Fix broken dashing when a dashed path starts outside the clip region +(Chris Wilson). + +Avoid range overflow when computing large patterns (Benjamin Otte and +Chris Wilson). + +Avoid crashing due to an invalid font with an incorrect entry in its +CMAP table (Adrian Johnson). + +Fix bugs in computing maximum size of text requests that can be sent +with the Render extension, (avoiding potential crashes when rendering +large amounts of text) (Behdad Esfahbod and Chris Wilson). + +Fix rendering of operators unbounded by the mask (Chris Wilson). + +Fix compilation on systems without compiler support for a native +64-bit type (Chris Wilson). + +Fix several cases of missing error-status propagation. (Chris Wilson, +doing the work he seems to never tire of). + +Fix several locking issues found with the lockdep valgrind skin (Chris +Wilson). + +Backend-specific bug fixes +-------------------------- +xlib: Avoid crash due to attempting XRender calls on pixmaps with +formats not supported by the Render extension (Chris Wilson): + + XRender crashes due to NULL pointer from Cairo on SGI O2 + https://bugs.freedesktop.org/show_bug.cgi?id=11734 + +xlib: Add support for XImages with depth of 4, 20, 24, or 28 bits +(Chris Wilson): + + cairo doesn't support 24 bits per pixel mode on X11 + https://bugs.freedesktop.org/show_bug.cgi?id=9102 + +xlib: Avoid mistakenly considering two surfaces as similar just +because their depths match (while their Render formats do not) (Karl +Tomlinson). + +ps: Fix slight mis-scaling of bitmapped fonts (Adrian Johnson) + +svg: Correctly emit comp-op for paint, mask, and show_glyphs +operations (Emmanuel Pacaud). + +svg: Use finer-grained fallbacks for SVG 1.2 (as PS and PDF backends +have been doing since 1.6.0) (Chris Wilson). + +win32: Fallback to DIB if DDB create fails for +cairo_surface_create_similar (Vladimir Vukicevic). + +win32: Fix compatibility with Windows Mobile (Vladimir Vukicevic). + +win32: Fix static builds to not do __declspec(dllimport) on public +functions. This requires the user to set a CAIRO_WIN32_STATIC_BUILD +environment variable when compiling (Behdad Esfahbod). + +Release 1.8.0 (2008-09-25 Carl Worth ) +========================================================= +The cairo community is happy (and relieved) to announce the 1.8.0 +release of the cairo graphics library. This is a major update to +cairo, with new features and enhanced functionality which maintains +compatibility for applications written using any previous major cairo +release, (1.6, 1.4, 1.2, or 1.0). We recommend that anybody using a +previous version of cairo upgrade to cairo 1.8.0. + +The dominant theme of this release is improvements to cairo's ability +to handle text. The highlights include a new "user fonts" feature as +well as a new cairo_show_text_glyphs API which allows glyphs to be +embedded in PDF output along with their original text, (for searching, +selection, and copy-and-paste). Another major feature is a revamp of +cairo's build system making it much easier to build cairo on various +platforms. + +See below for more details. + +User fonts +---------- +This new API allows the user of cairo API to provide drawings for +glyphs in a font. A common use for this is implementing fonts in +non-standard formats, like SVG fonts and Flash fonts. This API can +also be used by applications to provide custom glyph shapes for fonts +while still getting access to cairo's glyph caches. See +test/user-font.c and test/user-font-proxy.c for usage examples. This +is based on early work by Kristian Høgsberg. Thanks Kristian! + +This new API consists of the following functions (and corresponding +_get functions): + + cairo_user_font_face_create + + cairo_user_font_face_set_init_func + cairo_user_font_face_set_render_glyph_func + cairo_user_font_face_set_text_to_glyphs_func + cairo_user_font_face_set_unicode_to_glyph_func + +An additional, new API is + + cairo_scaled_font_text_to_glyphs + +We were previously reluctant to provide this function as +text-to-glyphs support in cairo was limited to "toy" font +functionality, not really interesting for real-world text +processing. However, with user fonts landing, this API is needed to +expose full access to how user fonts convert text to glyphs. This is +expected to be used by text toolkits like Pango, as well as "proxy" +user-font implementations. + +cairo_show_text_glyphs +---------------------- +This new API allows the caller of cairo to provide text data +corresponding to glyphs being drawn. The PDF backend implements this +new API so that complex text can be copied out of cairo's PDF output +correctly and reliably, (assuming the user of cairo calls +cairo_show_text_glyphs). The cairo_show_text_glyphs API is definitely +the most daunting API to debut in cairo. It is anticipated that pango +(and similar high-level text libraries) will be the primary users of +this API. In fact, pango 1.22 already uses cairo_show_text_glyphs. +Behdad was the architect and implementor of this effort. Thanks, +Behdad! + +The cairo_show_text_glyphs API includes the following new functions: + + cairo_show_text_glyphs + + cairo_glyph_allocate + cairo_glyph_free + + cairo_text_cluster_allocate + cairo_text_cluster_free + + cairo_surface_has_show_text_glyphs + +Build system revamp +------------------- +The primary goal of the revamp is to make the build system less +fragile, (particularly for non-Linux platforms). For example, now +people building on win32 will no longer need to maintain a +platform-specific list of files to be built. See the new README.win32 +for details. Also, the .so file will now be installed with a different +naming scheme, (for example, 1.7.6 will install with a .10800 +suffix). Many thanks to Behdad and his small army of helpers! + +Assorted API additions +---------------------- +For API completeness, several missing "getter" functions were added: + + cairo_scaled_font_get_scale_matrix + + cairo_surface_get_fallback_resolution + + cairo_toy_font_face_create + cairo_toy_font_face_get_family + cairo_toy_font_face_get_slant + cairo_toy_font_face_get_weight + +The new cairo_toy_font_face functions provide access to functionality +and settings provided by cairo_select_font_face(). Thanks Behdad! + +cairo-ps/cairo-pdf: More efficient output +----------------------------------------- +Adrian Johnson has been busy fixing all kinds of bugs in PS and PDF +backends, as well making them generate much more compact output by +avoiding things like re-emitting the color or linestyle on every +drawing operation. Thanks Adrian! + +cairo-xlib: dithering +--------------------- +Dithering: Cairo now does simple dithering when rendering to legacy X +servers. This is most visible with 8-bit visuals. Thanks Behdad! + +cairo-xlib: Avoid rendering glyphs out of surface bounds +-------------------------------------------------------- +This seemingly harmless optimization exposed a bug in OpenOffice.org 3 +versions where OO.o was passing bogus surface extents to cairo, +resulting in no text rendered in OO.o. Please contact your +distribution's OO.o maintainers if you see this bug and point them to +the following URL: + + https://bugs.freedesktop.org/show_bug.cgi?id=16209 + +cairo-xlib: Improved performance with X server without Render +------------------------------------------------------------- +Cairo now performs better on remote X servers that lack the Render +extension by being smarter about using X core protocol facilities +instead of falling back to doing all rendering on the client side. + +cairo-ft: respecting FC_FT_FACE +------------------------------- +Previously it was impossible to instruct cairo to do emboldening on a +font face object created from an FT_Face. Cairo now respects and uses +the FC_FT_FACE fontconfig pattern element, so emboldening can be +achieved by using cairo_ft_font_face_create_for_pattern() and a +carefully crafted pattern using FC_FT_FACE and FC_EMBOLDEN. Thanks +Behdad! + +cairo-directfb: backend improvements +------------------------------------ +The directfb backend, though still unsupported, has seen a good deal +of improvements. Thanks Vlad! + +Bug fixing and optimizations +---------------------------- +xlib: Faster bookkeeping (Karl Tomlinson) + https://bugzilla.mozilla.org/show_bug.cgi?id=453199#c5 + +PS: Fix gradients with non-constant alpha (Chris Wilson) + +Fix deadlock in user-font code (Richard Hughes and Behdad Esfahbod) + https://bugs.freedesktop.org/show_bug.cgi?id=16819 + +Countless other bugs have been fixed and optimizations made, many of +them thanks to Chris Wilson. Thanks Chris and others! + +Note also that the code that had been in cairo 1.7.x calling into +freetype's optional lcd_filter function was removed from cairo before +the 1.8.0 release. We do expect this code to come back in some form in +the future. + +Snapshot 1.7.6 (2008-09-17 Carl Worth ) +========================================================== +The cairo community is happy to announce the 1.7.6 snapshot of the +cairo graphics library. This is a "release candidate" for the upcoming +1.8.0 release, so we will greatly appreciate any reports of problems +in this release, and no major changes are currently planned before +1.8. + +Notable changes in 1.7.6 +------------------------ +The largest number of changes since 1.7.4 did not change the +implementation of cairo itself, but instead revamped cairo's build +system. The primary goal of the revamp is to make the build system +less fragile, (particularly for non-Linux platforms). For example, now +people building on win32 will no longer need to maintain a +platform-specific list of files to be built. Also, the .so file will +now be installed with a different naming scheme, (for example, 1.7.6 +will install with a .10706 suffix). Much thanks, Behdad! + +And, as usual, Chris Wilson has made another large round of robustness +improvements, (eliminating dead code, fixing propagation of error +status values, test suite improvements, etc. etc.). Thanks as always, +Chris! + +API changes since 1.7.4 +----------------------- +There have been a few changes of API that was new during the 1.7 +series: + +* Remove cairo_font_options_set_lcd_filter + and cairo_font_options_get_lcd_filter + + Motivation: At the Cairo Summit, this API was determined to be too + specific to the freetype font backend to be in the general + API. A similar API with a cairo_ft prefix might be introduced + in the future. Note that cairo will still respect the + corresponding fontconfig settings for these options. + +* Replace cairo_has_show_glyphs + with cairo_surface_has_show_glyphs + + Motivation: This really is a surface-specific interface, and the + convenience function on the cairo_t is not obviously + necessary. An application can easily call: + + cairo_surface_has_show_glyphs (cairo_get_target (cr)); + + as needed. + +* Add cairo_text_cluster_flags_t + to cairo_show_text_glyphs + cairo_scaled_font_text_to_glyphs + cairo_user_scaled_font_text_to_glyphs_func_t + + Motivation: This flag, (and specifically the + CAIRO_TEXT_CLUSTER_FLAG_BACKWARD value), replaces the + cairo_bool_t backward argument in each of the above + interfaces. This leads to more readable user code, and also + allows future extensibility. + +As always, there are no changes to any API from any major cairo +release, (1.0.x, 1.2.x, 1.4.x, 1.6.x). Cairo maintains the same +compatibility promise it always has. + +Bug fixes since 1.7.4 +--------------------- +xlib: Faster bookkeeping (Karl Tomlinson) + https://bugzilla.mozilla.org/show_bug.cgi?id=453199#c5 + +PS: Fix gradients with non-constant alpha (Chris Wilson) + +Fix deadlock in user-font code (Richard Hughes and Behdad Esfahbod) + https://bugs.freedesktop.org/show_bug.cgi?id=16819 + +Several other minor fixes. + +Snapshot 1.7.4 (2008-08-11 Behdad Esfahbod ) +=============================================================== +The cairo community is embarrassed to announce availability of the 1.7.4 +snapshot of the cairo graphics library. This is a followup release to the +1.7.2 snapshot to ship a tarball that can actually be built. The only +change since 1.7.4 is including the missing header file +cairo-user-font-private.h in the distribution. + +Snapshot 1.7.2 (2008-08-11 Behdad Esfahbod ) +=============================================================== +The cairo community is finally ready to announce availability of the 1.7.2 +snapshot of the cairo graphics library. This is embarrassingly the first +snapshot in the 1.7 unstable series of cairo, leading to the eventual release +of cairo 1.8, currently planned for late September. + +This snapshot comes four months after the 1.6.4 release. We have done a +really bad job on getting development snapshots out this cycle, but +hopefully all the API changes for 1.8 are now finished and the remaining +weeks will be spent on bug-fixing. There is more than 400 commits worth +of changes in this snapshot, and those can use some testing. Read on! + +Text, text, and more text! +-------------------------- +The dominant theme of this release, and 1.8 in general, is improvements +around cairo text API. Here is a high-level list of changes with text +handling: + +User fonts +---------- +This is new API allowing the user of cairo API to provide drawings for glyphs +in a font. This is most useful in implementing fonts in non-standard formats, +like SVG fonts and Flash fonts, but can also be used by games and other +applications to draw "funky" fonts. See test/user-font.c and +test/user-font-proxy.c for usage examples. This is based on early work by +Kristian Høgsberg. Thanks Kristian! + +show_text_glyphs +---------------- +This new API allows the caller of cairo to mark text glyphs with their +original text. The PDF backend implements this new API and latest Pango +master uses it. The result is (when bugs are fixed) that complex text can be +copied out of pangocairo's PDF output correctly and reliably. There are bugs +to fix though. A few poppler bugs, and some more in cairo and pango. + +To test show_text_glyph, just grab pango master and this cairo snapshot and +print text in gedit. Open in acroread or evince, select all, copy, paste +in gedit and compare. The Arabic text with diacritic marks is particularly +showing bad. Try with pango/pango-view/HELLO.txt if you are brave +enough. The Indic text is showing improvements, but is still coming out +buggy. + +LCD subpixel filtering using FreeType +------------------------------------- +FreeType 2.3.5 added support for various LCD subpixel filtering, and +fontconfig 2.6.0 added support for configuring LCD filter on a font by font +basis. Cairo now relies on FreeType and fontconfig for subpixel filtering. +This work is based on David Turner's original patch to cairo, maintained +and tested by Sylvain Pasche and others. Thanks all! + +Toy font face constructor and getter +------------------------------------ +Mostly for API completion, but also useful for higher level (like Pango) to +hook into what the user has set using cairo_select_font_face(), making that +toy API a bit more useful. + +FreeType: respecting FC_FT_FACE +------------------------------- +Previously it was impossible to instruct cairo to do emboldening on a font +face object created from an FT_Face. Cairo now respects and uses the +FC_FT_FACE fontconfig pattern element, so emboldening can be achieved by +using cairo_ft_font_face_create_for_pattern() and a carefully crafted pattern +using FC_FT_FACE and FC_EMBOLDEN. + + +PS/PDF: More efficient output +----------------------------- +Adrian Johnson has been busy fixing all kinds of bugs in PS and PDF +backends, as well making them generate much more compact output by avoiding +things like re-emitting the color or linestyle on every drawing operation. +Thanks Adrian! + + +Xlib: Dithering +--------------- +Cairo now does simple dithering when rendering to legacy X servers. This is +mostly visible with 8-bit visuals. + +Xlib: Avoid rendering glyphs out of surface bounds +-------------------------------------------------- +This seemingly harmless change manifested a bug with OpenOffice.org 3 versions +where OO.o was passing bogus surface extents to cairo, resulting in no text +rendered in OO.o. Please contact your distro's OO.o maintainers if you see +this bug and point them to the following URL: + + https://bugs.freedesktop.org/show_bug.cgi?id=16209 + +Xlib: Improved performance with Xrender-less X servers +------------------------------------------------------ +Cairo now performs better on remote, Xrender-less X servers by being smarter +about using X core protocol facilities instead of falling back to doing all +rendering on the client side. + + +Directfb: backend improvements +------------------------------ +The directfb backend, though still unsupported, has seen a good deal of +improvements. Thanks Vlad! + + +Bug fixing and optimizations +---------------------------- +Countless bugs have been fixed and optimizations made, many of them thanks to +Chris Wilson. Thanks Chris! + + +API additions +------------- + +cairo_show_text_glyphs + + This is a new text rendering API. Being a more advanced version of + cairo_show_glyphs(), it is aimed for use by higher-level text toolkits like + Pango, and enables better text extraction from output generated by backends + like PDF and SVG. The PDF backend already implements it, and the upcoming + Pango release will use it. + + To make that API work, a bunch of other additions were made: + +cairo_glyph_allocate +cairo_glyph_free +cairo_text_cluster_t +cairo_text_cluster_allocate +cairo_text_cluster_free +cairo_surface_has_show_text_glyphs + + +cairo_user_font_face_create + + This is the "user" font face constructor, accompanied by a variety of method + signatures, getters, and setters for a callback-based font backend: + +CAIRO_FONT_TYPE_USER +cairo_user_scaled_font_init_func_t +cairo_user_scaled_font_render_glyph_func_t +cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_set_init_func +cairo_user_font_face_set_render_glyph_func +cairo_user_font_face_set_text_to_glyphs_func +cairo_user_font_face_set_unicode_to_glyph_func +cairo_user_font_face_get_init_func +cairo_user_font_face_get_render_glyph_func +cairo_user_font_face_get_text_to_glyphs_func +cairo_user_font_face_get_unicode_to_glyph_func + + +cairo_scaled_font_text_to_glyphs + + We were previously reluctant to provide this function as text-to-glyphs + support in cairo was limited to "toy" font functionality, not really + interesting for real-world text processing. However, with user-fonts + landing, this API is needed to expose full access to how user-fonts + convert text to glyphs. This is expected to be used by text toolkits like + Pango, as well as "proxy" user-font implementations. + + +cairo_lcd_filter_t +cairo_font_options_set_lcd_filter +cairo_font_options_get_lcd_filter + + These add the possibility to choose between various available LCD subpixel + filters. The available filter values are modelled after what FreeType + provides. + + +cairo_toy_font_face_create +cairo_toy_font_face_get_family +cairo_toy_font_face_get_slant +cairo_toy_font_face_get_weight + + These provide access to functionality and settings provided by + cairo_select_font_face(). + + +cairo_scaled_font_get_scale_matrix +cairo_surface_get_fallback_resolution + + For API completeness. + + +Various new values for cairo_status_t enum + + +Known issues: + +- Type3 fonts generated by cairo's PDF backend may show up in poppler/Evince + in a different color than expected. This is fixed in poppler master branch. + This mostly affects cairo user fonts. The test case test/user-font.c + demonstrates this. + +- User fonts using other fonts in their rendering are currently embedded in + PDF as fallback bitmap glyphs. This will be (hopefully) fixed before 1.8. + The test case test/user-font-proxy.c demonstrates this. + + Release 1.6.4 (2008-04-11 Carl Worth ) ========================================================= The cairo community is wildly embarrassed to announce the 1.6.4 @@ -50,10 +3103,10 @@ This fix has been tested to resolve the bugs posted here, (for both Xerox and Dell printers): Printing some PDFs from evince is crashing our Xerox printer - http://bugs.freedesktop.org/show_bug.cgi?id=15348 + https://bugs.freedesktop.org/show_bug.cgi?id=15348 Cairo-generated postscript blocks Dell 5100cn - http://bugs.freedesktop.org/show_bug.cgi?id=15445 + https://bugs.freedesktop.org/show_bug.cgi?id=15445 Add missing locking in cairo-xlib --------------------------------- @@ -81,7 +3134,7 @@ New dependency on external pixman library (Thanks, Søren!) As of cairo 1.6, cairo now depends on the pixman library, for which the latest release can be obtained alongside cairo: - http://cairographics.org/releases/pixman-0.10.0.tar.gz + https://cairographics.org/releases/pixman-0.10.0.tar.gz This library provides all software rendering for cairo, (the implementation of the image backend as well as any image fallbacks @@ -119,7 +3172,7 @@ to, (and we could use help from users that have access to misbehaving printers). This bug is being tracked here: Printing some PDFs from evince is crashing our Xerox printer - http://bugs.freedesktop.org/show_bug.cgi?id=15348 + https://bugs.freedesktop.org/show_bug.cgi?id=15348 New support for arbitrary X server visuals (Thanks, Keith and Behdad!) ---------------------------------------------------------------------- @@ -187,7 +3240,7 @@ Rendering with CAIRO_ANTIALIAS_NONE has been fixed to be more predictable, (previously image rendering and geometry rendering would be slightly misaligned with respect to each other). -The reference manual at http://cairographics.org/manual now documents +The reference manual at https://cairographics.org/manual now documents 100% of the functions and types in cairo's public API. API additions @@ -424,7 +3477,7 @@ general Cairo now depends on pixman 0.10.0 which was recently released. The latest pixman release can always be found alongside cairo releases at: - http://cairographics.org/releases + https://cairographics.org/releases Increase the precision of color stops for gradients. This fixes a regression in gradient rendering that had been present since the @@ -534,7 +3587,7 @@ Rename ATSUI font backend to Quartz font backend. This affects the following usage: --enable-atsui -> --enable-quartz-font - CAIRO_HAS_ATSUI_FONT -> CAIRO_HAS_QUARTZ_FONT + CAIRO_HAS_ATSUI_FONT -> CAIRO_HAS_QUARTZ_FONT CAIRO_FONT_TYPE_ATSUI -> CAIRO_FONT_TYPE_QUARTZ cairo_atsui_font_face_create_for_atsu_font_id -> @@ -586,7 +3639,7 @@ cairo-svg --------- Fix stroke of path with a non-solid-color source pattern: - http://bugs.freedesktop.org/show_bug.cgi?id=14556 + https://bugs.freedesktop.org/show_bug.cgi?id=14556 cairo-quartz ------------ @@ -597,7 +3650,7 @@ cairo_show_text()/cairo_show_glyphs(). Correctly handle gradients with non-identity transformations: - Fixes http://bugs.freedesktop.org/show_bug.cgi?id=14248 + Fixes https://bugs.freedesktop.org/show_bug.cgi?id=14248 Add native implementation of REPEAT and REFLECT extend modes for gradients. @@ -724,7 +3777,7 @@ Add new API for efficiently using image data as a source: For full documentation, see: - http://cairographics.org/manual/cairo-Quartz-Surfaces.html#cairo-quartz-image-surface-create + https://cairographics.org/manual/cairo-Quartz-Surfaces.html#cairo-quartz-image-surface-create Several fixes for cairo_mask(). @@ -1074,7 +4127,7 @@ in cairo_quartz_surface_create. Fix to correctly handle 0x0 sized surfaces. -Optimize drawing of ExtendMode::REPEAT patterns for OS X 10.5. +Optimize drawing of EXTEND_REPEAT patterns for OS X 10.5. Snapshot 1.5.2 (2007-10-30 Carl Worth ) ========================================================== @@ -1099,14 +4152,14 @@ So users will need to acquire and build pixman before being able to build cairo. The current release is 0.9.6 and can be obtained from here: - http://cairographics.org/releases/pixman-0.9.6.tar.gz + https://cairographics.org/releases/pixman-0.9.6.tar.gz which can be verified with: - http://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1 + https://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1 66f01a682c64403a3d7a855ba5aa609ed93bcb9e pixman-0.9.6.tar.gz - http://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1.asc + https://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1.asc (signed by Carl Worth) Major PDF/PostScript improvements @@ -1347,8 +4400,8 @@ complain that some cleanup work is already done, but there you have it.) This fixes the bug causing OpenOffice.org to crash as described here: - XError on right click menus in OOo. - https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=243811 + XError on right click menus in OOo. + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=243811 Use IncludeInferiors when using xlib surface as a source (Ryan Lortie) ---------------------------------------------------------------------- @@ -1412,7 +4465,7 @@ bugs had perhaps never been hit by any users. But at least one was hit by the gnome-about program which resulted in dozens of duplicated bug reports against that program: - http://bugzilla.gnome.org/show_bug.cgi?id=431990 + https://bugzilla.gnome.org/show_bug.cgi?id=431990 We were very pleasantly surprised to see this bug get fixed as a side-effect of Chris's work. Well done, Chris! @@ -1528,16 +4581,16 @@ notes on using it: To build, do: - make malloc-stats.so + make malloc-stats.so inside util/, and to use, run: - LD_PRELOAD=malloc-stats.so some-program + LD_PRELOAD=malloc-stats.so some-program For binaries managed by libtool, eg, cairo-perf, do: - ../libtool --mode=execute /bin/true ./cairo-perf - LD_PRELOAD="../util/malloc-stats.so" .libs/lt-cairo-perf + ../libtool --mode=execute /bin/true ./cairo-perf + LD_PRELOAD="../util/malloc-stats.so" .libs/lt-cairo-perf Finally, the cairo-perf-diff-files utility was enhanced to allow for generating performance reports from several runs of the same backend @@ -1609,7 +4662,7 @@ PDF: Johnson) • Fix glyph positioning bug when glyphs are not horizontal - http://lists.freedesktop.org/archives/cairo/2007-April/010337.html + https://lists.freedesktop.org/archives/cairo/2007-April/010337.html win32: • Fix crash when rendering with bitmap fonts (Carl Worth) @@ -1660,7 +4713,7 @@ Critical fixes • Fix a crash due to a LOCK vs. UNLOCK typo (M. Drochner fixing Carl Worth's embarrassing typo). - http://bugs.freedesktop.org/show_bug.cgi?id=10235 + https://bugs.freedesktop.org/show_bug.cgi?id=10235 • Fix potential buffer overflow, which on some systems with a checking variant of snprintf would lead to a crash (Adrian Johnson, Stanislav @@ -1705,7 +4758,7 @@ Other bug fixes PDF output in some viewers. (Adrian Johnson, Adam Goode, and MenTaLguY). - http://lists.freedesktop.org/archives/cairo/2006-November/008551.html + https://lists.freedesktop.org/archives/cairo/2006-November/008551.html • win32: Return correct metrics when hinting is off, and fix font descent computation (Behdad Esfahbod). @@ -2422,7 +5475,7 @@ several improvements. The bug fixes in this snapshot include: mentioned here: CAIRO_BO_GUARD_BITS and coordinate space? - http://lists.freedesktop.org/archives/cairo/2006-December/008743.html + https://lists.freedesktop.org/archives/cairo/2006-December/008743.html * Fix several regressions in new tessellator (M Joonas Pihlaja) @@ -2482,7 +5535,7 @@ performance improvements are as follows: His own writeup of the work he did is quite thorough, but more than can be quoted here. Please see his post for the interesting details: - http://lists.freedesktop.org/archives/cairo/2006-November/008483.html + https://lists.freedesktop.org/archives/cairo/2006-November/008483.html (Though note that this snapshot also includes some additional, significant improvements that were only sketched out in that @@ -2797,7 +5850,7 @@ Release 1.2.6 (2006-11-02 Behdad Esfahbod ) This is the third bug fix release in the 1.2 series, coming less than two months after the 1.2.4 release made on August 18. -The 1.2.4 release turned out to be a pretty solid one, except for a crasher +The 1.2.4 release turned out to be a pretty solid one, except for a crash bug when forwarding an X connection where the client and the server have varying byte orders, eg. from a PPC to an i686. Other than that, various other small bugs have been fixed. @@ -2866,54 +5919,54 @@ here for such a short time period. Rendering fixes --------------- Fix image surfaces to not be clipped when used as a source (Vladimir Vukicevic) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=72e25648c4c4bc82ddd938aa4e05887a293f0d8b +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=72e25648c4c4bc82ddd938aa4e05887a293f0d8b Fix a couple of corner cases in dashing degenerate paths (Jeff Muizelaar) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=fbb1758ba8384650157b2bbbc93d161b0c2a05f0 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=fbb1758ba8384650157b2bbbc93d161b0c2a05f0 Fix support for type1 fonts on win32 (Adrian Johnson) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=da1019c9138695cb838a54f8b871bbfd0e8996d7 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=da1019c9138695cb838a54f8b871bbfd0e8996d7 Fix assertion failure when rotating bitmap fonts (Carl Worth) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=0bfa6d4f33b8ddb5dc55bbe419c15df4af856ff9 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=0bfa6d4f33b8ddb5dc55bbe419c15df4af856ff9 Fix assertion failure when calling cairo_text_path with bitmap fonts (Carl Worth) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9878a033531e6b96b5f27e69e10e90dee7440cd9 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=9878a033531e6b96b5f27e69e10e90dee7440cd9 Fix mis-handling of cairo_close_path in some situations (Tim Rowley, Carl Worth) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=53f74e59faf1af78f2f0741ccf1f23aa5dad4efc +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=53f74e59faf1af78f2f0741ccf1f23aa5dad4efc Respect font_matrix translation in _cairo_gstate_glyph_path (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=f183b835b111d23e838889178aa8106ec84663b3 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=f183b835b111d23e838889178aa8106ec84663b3 Fix vertical metrics adjustment to work with non-identity shapes (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7bc263842a798d657a95e539e1693372448837f +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7bc263842a798d657a95e539e1693372448837f [PS] Set correct ImageMatrix in _cairo_ps_surface_emit_bitmap_glyph_data (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=d47388ad759b0a1a0869655a87d9b5eb6ae2445d +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=d47388ad759b0a1a0869655a87d9b5eb6ae2445d Build system fixes ------------------ Fix xlib detection to prefer pkg-config to avoid false libXt dependency (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=0e78e7144353703cbd28aae6a67cd9ca261f1d68 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=0e78e7144353703cbd28aae6a67cd9ca261f1d68 Fix typos causing win32 build problem with PS,PDF, and SVG backends (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=aea83b908d020e26732753830bb3056e6702a774 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=aea83b908d020e26732753830bb3056e6702a774 Fix configure cache to not use stale results (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6d0e3260444a2d5b6fb0cb223ac79f1c0e7b3a6e +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=6d0e3260444a2d5b6fb0cb223ac79f1c0e7b3a6e Fix to not pass unsupported warning options to the compiler (Jens Granseuer) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=97524a8fdb899de1ae4a3e920fb7bda6d76c5571 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=97524a8fdb899de1ae4a3e920fb7bda6d76c5571 Fix to allow env. variables such as png_REQUIRES to override configure detection (Jens Granseuer) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=abd16e47d6331bd3811c908e524b4dcb6bd23bf0 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=abd16e47d6331bd3811c908e524b4dcb6bd23bf0 Fix test suite to not use an old system cairo when converting svg2png (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6122cc85c8f71b1ba2df3ab86907768edebe1781 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=6122cc85c8f71b1ba2df3ab86907768edebe1781 Fix test suite to not require signal.h to be present (Behdad Esfahbod) -http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6f8cf53b1e1ccdbe1ab6a275656b19c6e5120e40 +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=6f8cf53b1e1ccdbe1ab6a275656b19c6e5120e40 Code cleanups ------------- @@ -2939,7 +5992,7 @@ affected X servers that do not provide the Render extension and that provide a visual with BGR rather than RGB channel order. report: https://bugs.freedesktop.org/show_bug.cgi?id=7294 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9ae66174e774b57f16ad791452ed44efc2770a59 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=9ae66174e774b57f16ad791452ed44efc2770a59 Fix the "disappearing text" bug ------------------------------- @@ -2953,7 +6006,7 @@ was also exacerbated by a KDE migration bug that caused antialiasing to be disabled more than desired. report: https://bugs.freedesktop.org/show_bug.cgi?id=7494 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=456cdb3058f3b416109a9600167cd8842300ae14 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=456cdb3058f3b416109a9600167cd8842300ae14 see also: Xorg: https://bugs.freedesktop.org/show_bug.cgi?id=7681 KDE: http://qa.mandriva.com/show_bug.cgi?id=23990 @@ -2969,7 +6022,7 @@ than 72.0 would lead to incorrect results, (larger values would lead to increasingly shrunken output). report: https://bugs.freedesktop.org/show_bug.cgi?id=7533 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=1feb4291cf7813494355459bb547eec604c54ffb +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=1feb4291cf7813494355459bb547eec604c54ffb Fix inadvertent semantic change of font matrix translation (Behdad Esfahbod) ---------------------------------------------------------------------------- @@ -2983,7 +6036,7 @@ practice, and it was not intentional to introduce a semantic change. With 1.2.2 we return to the 1.0 semantics, with a much better implementation that provides correct glyph metrics. -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=84840e6bba6e72aa88fad7a0ee929e8955ba9051 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=84840e6bba6e72aa88fad7a0ee929e8955ba9051 Fix create_similar to preserve fallback resolution and font options (Behdad Esfahbod) ------------------------------------------------------------------------------------- @@ -2995,8 +6048,8 @@ destination surface would not be preserved to the intermediate fallbacks, for example. report: https://bugs.freedesktop.org/show_bug.cgi?id=4106 -fixes: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9fcb3c32c1f16fe6ab913e27eb54d18b7d9a06b0 - http://gitweb.freedesktop.org/?p=cairo;a=commit;h=bdb4e1edadb78a2118ff70b28163f8bd4317f1ec +fixes: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=9fcb3c32c1f16fe6ab913e27eb54d18b7d9a06b0 + https://gitweb.freedesktop.org/?p=cairo;a=commit;h=bdb4e1edadb78a2118ff70b28163f8bd4317f1ec xlib: Fix text performance regression from 1.0 to 1.2.0 (Vladimir Vukicevic) ---------------------------------------------------------------------------- @@ -3008,7 +6061,7 @@ to the X server, (such as a remote X server over an ssh connection). The slowdown was identified and fixed in 1.2.2. report: https://bugs.freedesktop.org/show_bug.cgi?id=7514 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7191885c88068dad57d68ced69a752d1162b12c +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7191885c88068dad57d68ced69a752d1162b12c PDF: Eliminate dependency on FreeType library dependency (Adrian Johnson) ------------------------------------------------------------------------- @@ -3020,16 +6073,16 @@ freetype library is not required to use the pdf backend on the win32 platform. report: https://bugs.freedesktop.org/show_bug.cgi?id=7538 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=a0989f427be87c60415963dd6822b3c5c3781691 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=a0989f427be87c60415963dd6822b3c5c3781691 PDF: Fix broken output on amd64 (Adrian Johnson) ------------------------------------------------ -report: http://bugzilla.gnome.org/show_bug.cgi?id=349826 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=f4b12e497b7ac282b2f6831b8fb68deebc412e60 +report: https://bugzilla.gnome.org/show_bug.cgi?id=349826 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=f4b12e497b7ac282b2f6831b8fb68deebc412e60 PS: Fix broken output for truetype fonts > 64k (Adrian Johnson) --------------------------------------------------------------- -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=067d97eb1793a6b0d0dddfbd0b54117844511a94 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=067d97eb1793a6b0d0dddfbd0b54117844511a94 PDF: Fix so that dashing doesn't get stuck on (Kent Worsnop) ------------------------------------------------------------ @@ -3037,26 +6090,26 @@ Kent notices that with the PDF backend in cairo 1.2.0 as soon as a stroke was performed with dashing, all subsequent strokes would also be dashed. There was no way to turn dashing off again. -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=778c4730a86296bf0a71080cf7008d7291792256 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=778c4730a86296bf0a71080cf7008d7291792256 Fix memory leaks in failure paths in gradient creation (Alfred Peng) -------------------------------------------------------------------- -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=db06681b487873788b51a6766894fc619eb8d8f2 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=db06681b487873788b51a6766894fc619eb8d8f2 Fix memory leak in _cairo_surface_show_glyphs (Chris Wilson) ------------------------------------------------------------ report: https://bugs.freedesktop.org/show_bug.cgi?id=7766 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=e2fddcccb43d06486d3680a19cfdd5a54963fcbd +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=e2fddcccb43d06486d3680a19cfdd5a54963fcbd Solaris: Add definition of cairo_private for some Sun compilers (Alfred Peng) ----------------------------------------------------------------------------- report: https://bugzilla.mozilla.org/show_bug.cgi?id=341874 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=04757a3aa8deeff3265719ebe01b021638990ec6 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=04757a3aa8deeff3265719ebe01b021638990ec6 Solaris: Change version number of Sun's Xorg server with buggy repeat (Brian Cameron) ------------------------------------------------------------------------------------- report: https://bugs.freedesktop.org/show_bug.cgi?id=7483 -fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=e0ad1aa995bcec4246c0b8ab0d5a5a79871ce235 +fix: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=e0ad1aa995bcec4246c0b8ab0d5a5a79871ce235 Various memory leak fixes ------------------------- @@ -3105,13 +6158,13 @@ And at least the following bugs have been fixed: * PS/PDF: Fix broken placement for vertical glyphs * PS: Fix to not draw BUTT-capped zero-length dash segments * Do device offset before float->fixed conversion - http://bugzilla.gnome.org/show_bug.cgi?id=332266 + https://bugzilla.gnome.org/show_bug.cgi?id=332266 * PS: Fix source surfaces with transformations - * PS: Fix to not draw BUTT-capped degnerate sub-paths + * PS: Fix to not draw BUTT-capped degenerate sub-paths * PS: Don't walk off end of array when printing "~>" * Fix some memory leaks in the test suite rig * SVG: Fix memory leak when using cairo_mask - * Fix ExtendMode::REFLECT and EXTEND_PAD to not crash (though these are + * Fix EXTEND_REFLECT and EXTEND_PAD to not crash (though these are still not yet fully implemented for surface patterns). This has been a tremendous effort by everyone, and I'm proud to have @@ -3272,7 +6325,7 @@ This is the third in a series of snapshots working toward the imminent 1.2 release of cairo. For a list of items still needing work on the cairo 1.2 roadmap, please see: - http://cairographics.org/ROADMAP + https://cairographics.org/ROADMAP As can be seen in that list, there are no longer any API additions left on the roadmap. Instead, there is a feature (PDF type 3 fonts) a @@ -3367,7 +6420,7 @@ This is the second in a series of snapshots working toward the upcoming 1.2 release of cairo. For a list of items still needing work on the cairo 1.2 roadmap, please see: - http://cairographics.org/ROADMAP + https://cairographics.org/ROADMAP The items included in this snapshot (since the 1.1.2 snapshot) are described below. @@ -3380,9 +6433,9 @@ support this use case, we have added 4 new function calls that are specific to the PostScript backend: cairo_ps_surface_set_size - cairo_ps_surface_dsc_comment - cairo_ps_surface_dsc_begin_setup - cairo_ps_surface_dsc_begin_page_setup + cairo_ps_surface_dsc_comment + cairo_ps_surface_dsc_begin_setup + cairo_ps_surface_dsc_begin_page_setup These functions allow variation of the page size/orientation from one page to the next in the PostScript output. They also allow the toolkit @@ -3589,7 +6642,7 @@ General bug fixes (Richard Stellingwerff, Owen Taylor) * 4231 - * 4298 - Accomodate gentoo and Mandriva versions in X server vendor string check + * 4298 - Accommodate gentoo and Mandriva versions in X server vendor string check (Billy Biggs, Frederic Crozat, Owen Taylor) win32-specific fixes @@ -3700,7 +6753,7 @@ Bug fixes * Negative dash offsets have been fixed to work correctly. - * The stroking of paths with mutiple subpaths has now been fixed to + * The stroking of paths with multiple subpaths has now been fixed to apply caps to all subpaths rather than just the last one. * Many build fixes for better portability on various systems. @@ -4534,7 +7587,7 @@ Owen Taylor has converted cairo's documentation system to gtk-doc and has begun some long-needed work on the documentation, which can now be viewed online here: - http://cairographics.org/manual/ + https://cairographics.org/manual/ New backend: win32 ------------------ @@ -4566,7 +7619,7 @@ This is the first snapshot to include a functional win32 backend, And see also the documentation at: -http://cairographics.org/manual/cairo-Microsoft-Windows-Backend.html +https://cairographics.org/manual/cairo-Microsoft-Windows-Backend.html Disabled backend: quartz ------------------------ @@ -4595,14 +7648,14 @@ resolution. Further changes are described below. Added cairo_font_extents and cairo_font_glyph_extents. See documentation for details: - http://cairographics.org/manual/cairo-cairo-t.html#cairo-font-extents + https://cairographics.org/manual/cairo-cairo-t.html#cairo-font-extents cairo-ft.h ---------- The cairo_ft_font API changed considerably. Please see the documentation for details: - http://cairographics.org/manual/cairo-FreeType-Fonts.html + https://cairographics.org/manual/cairo-FreeType-Fonts.html Performance ----------- @@ -4668,23 +7721,23 @@ Major API changes cc `pkg-config --cflags --libs cairo` -o foo foo.c IMPORTANT: Users with old versions of cairo installed will need to - manually remove cairo.h and cairo-features.h from the - system include directories in order to prevent the old - headers from being used in preference to the new ones. + manually remove cairo.h and cairo-features.h from the + system include directories in order to prevent the old + headers from being used in preference to the new ones. 2) The backend-specific portions of the old monolithic cairo.h have been split out into individual public header files. The new files are: cairo-atsui.h - cairo-ft.h - cairo-glitz.h - cairo-pdf.h - cairo-png.h - cairo-ps.h + cairo-ft.h + cairo-glitz.h + cairo-pdf.h + cairo-png.h + cairo-ps.h cairo-quartz.h - cairo-xcb.h - cairo-xlib.h + cairo-xcb.h + cairo-xlib.h Applications will need to be modified to explicitly include the new header files where appropriate. @@ -4753,7 +7806,7 @@ is a port of Keith Packard's fdclock program originally written for the xlib backend. A screenshot of this program running on Mac OS X is available here: - http://cairographics.org/~cworth/images/fdclock-quartz.png + https://cairographics.org/~cworth/images/fdclock-quartz.png ATSUI font backend ------------------ @@ -5048,10 +8101,10 @@ Better text support This snapshot provides much better text support by implementing the following four functions: - cairo_text_extents - cairo_glyph_extents - cairo_text_path - cairo_glyph_path + cairo_text_extents + cairo_glyph_extents + cairo_text_path + cairo_glyph_path The text/glyph_extents functions can be used to determine the bounding box (and advance) for text as if drawn by show_text/glyphs. @@ -5118,4 +8171,4 @@ Carl Worth wrote the first lines of Xr, after Keith Packard proposed the plan for a stateful drawing library in C providing a PostScript-like rendering model. - LocalWords: mutex BeOS extraordinaire + LocalWords: mutex BeOS extraordinaire distro's URL lcd bool tarball diff --git a/gfx/cairo/cairo/README b/gfx/cairo/cairo/README index efca44cda29e..0bcf140121a8 100644 --- a/gfx/cairo/cairo/README +++ b/gfx/cairo/cairo/README @@ -1,13 +1,13 @@ Cairo - Multi-platform 2D graphics library -http://cairographics.org +https://cairographics.org What is cairo ============= Cairo is a 2D graphics library with support for multiple output devices. Currently supported output targets include the X Window -System, win32, and image buffers, as well as PDF, PostScript, and SVG -file output. Experimental backends include OpenGL (through glitz), -Quartz, XCB, BeOS, OS/2, and DirectFB. +System (via both Xlib and XCB), quartz, win32, and image buffers, +as well as PDF, PostScript, and SVG file output. Experimental backends +include OpenGL, BeOS, OS/2, and DirectFB. Cairo is designed to produce consistent output on all output media while taking advantage of display hardware acceleration when available @@ -35,25 +35,25 @@ Where to get more information about cairo ========================================= The primary source of information about cairo is: - http://cairographics.org/ + https://cairographics.org/ The latest versions of cairo can always be found at: - http://cairographics.org/download + https://cairographics.org/download Documentation on using cairo and frequently-asked questions: - http://cairographics.org/documentation - http://cairographics.org/FAQ + https://cairographics.org/documentation + https://cairographics.org/FAQ Mailing lists for contacting cairo users and developers: - http://cairographics.org/lists + https://cairographics.org/lists Roadmap and unscheduled things to do, (please feel free to help out): - http://cairographics.org/roadmap - http://cairographics.org/todo + https://cairographics.org/roadmap + https://cairographics.org/todo Dependencies ============ @@ -67,29 +67,31 @@ backends. Further, the supported backends can be divided into the "platform" backends which depend on some underlying platform-specific system, (such as the X Window System or some other window system). -As an example, for a standard Linux build, (with image, png, pdf, -PostScript, svg, and xlib surface backends, and the freetype font -backend), the following sample commands will install necessary -dependencies: +As an example, for a standard Linux build similar to what's shipped by +your distro, (with image, png, pdf, PostScript, svg, and xlib surface +backends, and the freetype font backend), the following sample commands +will install necessary dependencies: Debian (and similar): - apt-get install libpng12-dev libz-dev libxrender-dev libfontconfig1-dev + apt-get build-dep cairo Fedora (and similar): yum install libpng-devel zlib-devel libXrender-devel fontconfig-devel -(Those commands intentionally don't install pixman from a distribution -package since if you're manually compiling cairo, then you likely want -to grab pixman from the same place at the same time and compile it as -well.) +Technically you probably don't need pixman from the distribution since +if you're manually compiling Cairo you probably want an updated pixman +as well. However, if you follow the default settings and install pixman +to /usr/local, your Cairo build should properly use it in preference to +the system pixman. + Supported, "standard" surface backends ------------------------------------ image backend (required) ------------------------ - pixman >= 0.10.0 http://cairographics.org/releases + pixman >= 0.30.0 https://cairographics.org/releases png support (can be left out if desired, but many ----------- applications expect it to be present) @@ -111,20 +113,24 @@ Supported, "platform" surface backends ----------------------------------- xlib backend ------------ - X11 http://freedesktop.org/Software/xlibs + X11 https://freedesktop.org/Software/xlibs xlib-xrender backend -------------------- - Xrender >= 0.6 http://freedesktop.org/Software/xlibs + Xrender >= 0.6 https://freedesktop.org/Software/xlibs quartz backend -------------- - MacOS X >= 10.4 with Xcode >= 2.4 + MacOS X >= 10.4 with Xcode >= 2.5 win32 backend ------------- Microsoft Windows 2000 or newer[*]. + xcb backend + ----------- + XCB https://xcb.freedesktop.org + Font backends (required to have at least one) --------------------------------------------- freetype font backend @@ -163,14 +169,6 @@ Font backends (required to have at least one) Experimental surface backends ----------------------------- - glitz - ------------- - glitz >= 0.4.4 http://freedesktop.org/Software/glitz - - xcb backend - ----------- - XCB http://xcb.freedesktop.org - beos backend ------------ No dependencies in itself other than an installed BeOS system, but cairo @@ -183,10 +181,12 @@ Experimental surface backends packages and developer dependencies are available at Netlabs: ftp://ftp.netlabs.org/pub/cairo + Compiling ========= See the INSTALL document for build instructions. + History ======= Cairo was originally developed by Carl Worth and diff --git a/gfx/cairo/cairo/README.win32 b/gfx/cairo/cairo/README.win32 new file mode 100644 index 000000000000..ff962b72a689 --- /dev/null +++ b/gfx/cairo/cairo/README.win32 @@ -0,0 +1,66 @@ +Building Cairo on Windows +========================= +There are two primary ways to build Cairo on Windows. You can use a +UNIX emulation based setup, such as Cygwin or MSYS, with the +conventional configure script shipped with Cairo releases. In this +configuration, you will build with GCC and (implicitly) libtool. In +the Cygwin case you end up with a DLL that depends on Cygwin and +should be used only from Cygwin applications. In the MSYS case you end +up with a "normal" Win32 DLL that can be used either from GCC- or +Microsoft Visual C++-compiled code. In theory, this technique is no +different than the ordinary build process for the Cairo library. In +practise there are lots of small details that can go wrong. + +The second way is to use a GNU-compatible make, but build using +Microsoft's Visual C++ compiler to produce native libraries. This is +the setup this README.win32 is written for. Also the DLL produced this +way is usable either from GCC- or MSVC-compiled code. + +Tools required +============== +You will need GNU make, version 3.80 or later. Earlier versions or +other modern make implementations may work, but are not guaranteed to. + +You will also need Microsoft Visual C++. Version 7 has been most +heavily tested, but other versions are likely to work fine. + +Libraries required +================== +Cairo requires a compatible version of the pixman library. Full build +instructions are beyond the scope of this document; however, using the +same tools, it should be possible to build pixman simply by entering +the pixman/src directory and typing: + + make -f Makefile.win32 CFG=release + +Depending on your feature set, you may also need zlib and libpng. + +Building +======== +There are a few files that you will need to edit. First, you must +determine which features will be built. Edit +build/Makefile.win32.features and set the features as desired. Note +that most features have external dependencies; specifically, +CAIRO_HAS_PNG_FUNCTIONS requires libpng to be present, and +CAIRO_HAS_PS_SURFACE and CAIRO_HAS_PDF_SURFACE both require zlib. + +To ensure that the compiler can find all dependencies, you may need to +edit build/Makefile.win32.common. In particular, ensure that +PIXMAN_CFLAGS contains a -I parameter pointing to the location of +your pixman header files and that PIXMAN_LIBS points to the actual +location of your pixman-1.lib file. You may also need to edit the +various occurrences of CAIRO_LIBS to point to other libraries +correctly. Note also that if you wish to link statically with zlib, +you should replace zdll.lib with zlib.lib. + +Finally, from the top Cairo directory, type: + + make -f Makefile.win32 CFG=release + +If this command succeeds, you will end up with src/release/cairo.dll. +To successfully use Cairo from your own programs, you will probably +want to move this file to some central location. You will also +probably want to copy the Cairo header files. These should be placed +in a cairo subdirectory (for instance, c:/code/common/include/cairo). +The exact set to copy depends on your features and is reported to you +at the end of the build. diff --git a/gfx/cairo/cairo/src/Makefile.am.analysis b/gfx/cairo/cairo/src/Makefile.am.analysis new file mode 100644 index 000000000000..fab4cf7a5145 --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.am.analysis @@ -0,0 +1,35 @@ + +SPARSE = sparse +sparse: + @echo Checking enabled sources with sparse checker + @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_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) $(enabled_cairo_cxx_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/gfx/cairo/cairo/src/Makefile.am.features b/gfx/cairo/cairo/src/Makefile.am.features new file mode 100644 index 000000000000..47c95db1f915 --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.am.features @@ -0,0 +1,657 @@ +# Generated by configure. Do not edit. + +include $(top_srcdir)/src/Makefile.sources + +supported_cairo_headers = $(cairo_headers) +unsupported_cairo_headers = +all_cairo_headers = $(cairo_headers) +all_cairo_private = $(cairo_private) +all_cairo_cxx_sources = $(cairo_cxx_sources) +all_cairo_sources = $(cairo_sources) + +enabled_cairo_headers = $(cairo_headers) +enabled_cairo_private = $(cairo_private) +enabled_cairo_cxx_sources = $(cairo_cxx_sources) +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_cxx_sources += $(cairo_xlib_cxx_sources) +all_cairo_sources += $(cairo_xlib_sources) +if CAIRO_HAS_XLIB_SURFACE +enabled_cairo_headers += $(cairo_xlib_headers) +enabled_cairo_private += $(cairo_xlib_private) +enabled_cairo_cxx_sources += $(cairo_xlib_cxx_sources) +enabled_cairo_sources += $(cairo_xlib_sources) +endif +all_cairo_pkgconf += cairo-xlib.pc +if CAIRO_HAS_XLIB_SURFACE +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_cxx_sources += $(cairo_xlib_xrender_cxx_sources) +all_cairo_sources += $(cairo_xlib_xrender_sources) +if CAIRO_HAS_XLIB_XRENDER_SURFACE +enabled_cairo_headers += $(cairo_xlib_xrender_headers) +enabled_cairo_private += $(cairo_xlib_xrender_private) +enabled_cairo_cxx_sources += $(cairo_xlib_xrender_cxx_sources) +enabled_cairo_sources += $(cairo_xlib_xrender_sources) +endif +all_cairo_pkgconf += cairo-xlib-xrender.pc +if CAIRO_HAS_XLIB_XRENDER_SURFACE +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_cxx_sources += $(cairo_xcb_cxx_sources) +all_cairo_sources += $(cairo_xcb_sources) +if CAIRO_HAS_XCB_SURFACE +enabled_cairo_headers += $(cairo_xcb_headers) +enabled_cairo_private += $(cairo_xcb_private) +enabled_cairo_cxx_sources += $(cairo_xcb_cxx_sources) +enabled_cairo_sources += $(cairo_xcb_sources) +endif +all_cairo_pkgconf += cairo-xcb.pc +if CAIRO_HAS_XCB_SURFACE +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_cxx_sources += $(cairo_xlib_xcb_cxx_sources) +all_cairo_sources += $(cairo_xlib_xcb_sources) +if CAIRO_HAS_XLIB_XCB_FUNCTIONS +enabled_cairo_headers += $(cairo_xlib_xcb_headers) +enabled_cairo_private += $(cairo_xlib_xcb_private) +enabled_cairo_cxx_sources += $(cairo_xlib_xcb_cxx_sources) +enabled_cairo_sources += $(cairo_xlib_xcb_sources) +endif +all_cairo_pkgconf += cairo-xlib-xcb.pc +if CAIRO_HAS_XLIB_XCB_FUNCTIONS +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_cxx_sources += $(cairo_xcb_shm_cxx_sources) +all_cairo_sources += $(cairo_xcb_shm_sources) +if CAIRO_HAS_XCB_SHM_FUNCTIONS +enabled_cairo_headers += $(cairo_xcb_shm_headers) +enabled_cairo_private += $(cairo_xcb_shm_private) +enabled_cairo_cxx_sources += $(cairo_xcb_shm_cxx_sources) +enabled_cairo_sources += $(cairo_xcb_shm_sources) +endif +all_cairo_pkgconf += cairo-xcb-shm.pc +if CAIRO_HAS_XCB_SHM_FUNCTIONS +enabled_cairo_pkgconf += cairo-xcb-shm.pc +endif + +unsupported_cairo_headers += $(cairo_qt_headers) +all_cairo_headers += $(cairo_qt_headers) +all_cairo_private += $(cairo_qt_private) +all_cairo_cxx_sources += $(cairo_qt_cxx_sources) +all_cairo_sources += $(cairo_qt_sources) +if CAIRO_HAS_QT_SURFACE +enabled_cairo_headers += $(cairo_qt_headers) +enabled_cairo_private += $(cairo_qt_private) +enabled_cairo_cxx_sources += $(cairo_qt_cxx_sources) +enabled_cairo_sources += $(cairo_qt_sources) +endif +all_cairo_pkgconf += cairo-qt.pc +if CAIRO_HAS_QT_SURFACE +enabled_cairo_pkgconf += cairo-qt.pc +endif + +supported_cairo_headers += $(cairo_quartz_headers) +all_cairo_headers += $(cairo_quartz_headers) +all_cairo_private += $(cairo_quartz_private) +all_cairo_cxx_sources += $(cairo_quartz_cxx_sources) +all_cairo_sources += $(cairo_quartz_sources) +if CAIRO_HAS_QUARTZ_SURFACE +enabled_cairo_headers += $(cairo_quartz_headers) +enabled_cairo_private += $(cairo_quartz_private) +enabled_cairo_cxx_sources += $(cairo_quartz_cxx_sources) +enabled_cairo_sources += $(cairo_quartz_sources) +endif +all_cairo_pkgconf += cairo-quartz.pc +if CAIRO_HAS_QUARTZ_SURFACE +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_cxx_sources += $(cairo_quartz_font_cxx_sources) +all_cairo_sources += $(cairo_quartz_font_sources) +if CAIRO_HAS_QUARTZ_FONT +enabled_cairo_headers += $(cairo_quartz_font_headers) +enabled_cairo_private += $(cairo_quartz_font_private) +enabled_cairo_cxx_sources += $(cairo_quartz_font_cxx_sources) +enabled_cairo_sources += $(cairo_quartz_font_sources) +endif +all_cairo_pkgconf += cairo-quartz-font.pc +if CAIRO_HAS_QUARTZ_FONT +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_cxx_sources += $(cairo_quartz_image_cxx_sources) +all_cairo_sources += $(cairo_quartz_image_sources) +if CAIRO_HAS_QUARTZ_IMAGE_SURFACE +enabled_cairo_headers += $(cairo_quartz_image_headers) +enabled_cairo_private += $(cairo_quartz_image_private) +enabled_cairo_cxx_sources += $(cairo_quartz_image_cxx_sources) +enabled_cairo_sources += $(cairo_quartz_image_sources) +endif +all_cairo_pkgconf += cairo-quartz-image.pc +if CAIRO_HAS_QUARTZ_IMAGE_SURFACE +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_cxx_sources += $(cairo_win32_cxx_sources) +all_cairo_sources += $(cairo_win32_sources) +if CAIRO_HAS_WIN32_SURFACE +enabled_cairo_headers += $(cairo_win32_headers) +enabled_cairo_private += $(cairo_win32_private) +enabled_cairo_cxx_sources += $(cairo_win32_cxx_sources) +enabled_cairo_sources += $(cairo_win32_sources) +endif +all_cairo_pkgconf += cairo-win32.pc +if CAIRO_HAS_WIN32_SURFACE +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_cxx_sources += $(cairo_win32_font_cxx_sources) +all_cairo_sources += $(cairo_win32_font_sources) +if CAIRO_HAS_WIN32_FONT +enabled_cairo_headers += $(cairo_win32_font_headers) +enabled_cairo_private += $(cairo_win32_font_private) +enabled_cairo_cxx_sources += $(cairo_win32_font_cxx_sources) +enabled_cairo_sources += $(cairo_win32_font_sources) +endif +all_cairo_pkgconf += cairo-win32-font.pc +if CAIRO_HAS_WIN32_FONT +enabled_cairo_pkgconf += cairo-win32-font.pc +endif + +unsupported_cairo_headers += $(cairo_os2_headers) +all_cairo_headers += $(cairo_os2_headers) +all_cairo_private += $(cairo_os2_private) +all_cairo_cxx_sources += $(cairo_os2_cxx_sources) +all_cairo_sources += $(cairo_os2_sources) +if CAIRO_HAS_OS2_SURFACE +enabled_cairo_headers += $(cairo_os2_headers) +enabled_cairo_private += $(cairo_os2_private) +enabled_cairo_cxx_sources += $(cairo_os2_cxx_sources) +enabled_cairo_sources += $(cairo_os2_sources) +endif +all_cairo_pkgconf += cairo-os2.pc +if CAIRO_HAS_OS2_SURFACE +enabled_cairo_pkgconf += cairo-os2.pc +endif + +unsupported_cairo_headers += $(cairo_beos_headers) +all_cairo_headers += $(cairo_beos_headers) +all_cairo_private += $(cairo_beos_private) +all_cairo_cxx_sources += $(cairo_beos_cxx_sources) +all_cairo_sources += $(cairo_beos_sources) +if CAIRO_HAS_BEOS_SURFACE +enabled_cairo_headers += $(cairo_beos_headers) +enabled_cairo_private += $(cairo_beos_private) +enabled_cairo_cxx_sources += $(cairo_beos_cxx_sources) +enabled_cairo_sources += $(cairo_beos_sources) +endif +all_cairo_pkgconf += cairo-beos.pc +if CAIRO_HAS_BEOS_SURFACE +enabled_cairo_pkgconf += cairo-beos.pc +endif + +unsupported_cairo_headers += $(cairo_drm_headers) +all_cairo_headers += $(cairo_drm_headers) +all_cairo_private += $(cairo_drm_private) +all_cairo_cxx_sources += $(cairo_drm_cxx_sources) +all_cairo_sources += $(cairo_drm_sources) +if CAIRO_HAS_DRM_SURFACE +enabled_cairo_headers += $(cairo_drm_headers) +enabled_cairo_private += $(cairo_drm_private) +enabled_cairo_cxx_sources += $(cairo_drm_cxx_sources) +enabled_cairo_sources += $(cairo_drm_sources) +endif +all_cairo_pkgconf += cairo-drm.pc +if CAIRO_HAS_DRM_SURFACE +enabled_cairo_pkgconf += cairo-drm.pc +endif + +unsupported_cairo_headers += $(cairo_gallium_headers) +all_cairo_headers += $(cairo_gallium_headers) +all_cairo_private += $(cairo_gallium_private) +all_cairo_cxx_sources += $(cairo_gallium_cxx_sources) +all_cairo_sources += $(cairo_gallium_sources) +if CAIRO_HAS_GALLIUM_SURFACE +enabled_cairo_headers += $(cairo_gallium_headers) +enabled_cairo_private += $(cairo_gallium_private) +enabled_cairo_cxx_sources += $(cairo_gallium_cxx_sources) +enabled_cairo_sources += $(cairo_gallium_sources) +endif +all_cairo_pkgconf += cairo-gallium.pc +if CAIRO_HAS_GALLIUM_SURFACE +enabled_cairo_pkgconf += cairo-gallium.pc +endif + +supported_cairo_headers += $(cairo_png_headers) +all_cairo_headers += $(cairo_png_headers) +all_cairo_private += $(cairo_png_private) +all_cairo_cxx_sources += $(cairo_png_cxx_sources) +all_cairo_sources += $(cairo_png_sources) +if CAIRO_HAS_PNG_FUNCTIONS +enabled_cairo_headers += $(cairo_png_headers) +enabled_cairo_private += $(cairo_png_private) +enabled_cairo_cxx_sources += $(cairo_png_cxx_sources) +enabled_cairo_sources += $(cairo_png_sources) +endif +all_cairo_pkgconf += cairo-png.pc +if CAIRO_HAS_PNG_FUNCTIONS +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_cxx_sources += $(cairo_gl_cxx_sources) +all_cairo_sources += $(cairo_gl_sources) +if CAIRO_HAS_GL_SURFACE +enabled_cairo_headers += $(cairo_gl_headers) +enabled_cairo_private += $(cairo_gl_private) +enabled_cairo_cxx_sources += $(cairo_gl_cxx_sources) +enabled_cairo_sources += $(cairo_gl_sources) +endif +all_cairo_pkgconf += cairo-gl.pc +if CAIRO_HAS_GL_SURFACE +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_cxx_sources += $(cairo_glesv2_cxx_sources) +all_cairo_sources += $(cairo_glesv2_sources) +if CAIRO_HAS_GLESV2_SURFACE +enabled_cairo_headers += $(cairo_glesv2_headers) +enabled_cairo_private += $(cairo_glesv2_private) +enabled_cairo_cxx_sources += $(cairo_glesv2_cxx_sources) +enabled_cairo_sources += $(cairo_glesv2_sources) +endif +all_cairo_pkgconf += cairo-glesv2.pc +if CAIRO_HAS_GLESV2_SURFACE +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_cxx_sources += $(cairo_glesv3_cxx_sources) +all_cairo_sources += $(cairo_glesv3_sources) +if CAIRO_HAS_GLESV3_SURFACE +enabled_cairo_headers += $(cairo_glesv3_headers) +enabled_cairo_private += $(cairo_glesv3_private) +enabled_cairo_cxx_sources += $(cairo_glesv3_cxx_sources) +enabled_cairo_sources += $(cairo_glesv3_sources) +endif +all_cairo_pkgconf += cairo-glesv3.pc +if CAIRO_HAS_GLESV3_SURFACE +enabled_cairo_pkgconf += cairo-glesv3.pc +endif + +unsupported_cairo_headers += $(cairo_cogl_headers) +all_cairo_headers += $(cairo_cogl_headers) +all_cairo_private += $(cairo_cogl_private) +all_cairo_cxx_sources += $(cairo_cogl_cxx_sources) +all_cairo_sources += $(cairo_cogl_sources) +if CAIRO_HAS_COGL_SURFACE +enabled_cairo_headers += $(cairo_cogl_headers) +enabled_cairo_private += $(cairo_cogl_private) +enabled_cairo_cxx_sources += $(cairo_cogl_cxx_sources) +enabled_cairo_sources += $(cairo_cogl_sources) +endif +all_cairo_pkgconf += cairo-cogl.pc +if CAIRO_HAS_COGL_SURFACE +enabled_cairo_pkgconf += cairo-cogl.pc +endif + +unsupported_cairo_headers += $(cairo_directfb_headers) +all_cairo_headers += $(cairo_directfb_headers) +all_cairo_private += $(cairo_directfb_private) +all_cairo_cxx_sources += $(cairo_directfb_cxx_sources) +all_cairo_sources += $(cairo_directfb_sources) +if CAIRO_HAS_DIRECTFB_SURFACE +enabled_cairo_headers += $(cairo_directfb_headers) +enabled_cairo_private += $(cairo_directfb_private) +enabled_cairo_cxx_sources += $(cairo_directfb_cxx_sources) +enabled_cairo_sources += $(cairo_directfb_sources) +endif +all_cairo_pkgconf += cairo-directfb.pc +if CAIRO_HAS_DIRECTFB_SURFACE +enabled_cairo_pkgconf += cairo-directfb.pc +endif + +unsupported_cairo_headers += $(cairo_vg_headers) +all_cairo_headers += $(cairo_vg_headers) +all_cairo_private += $(cairo_vg_private) +all_cairo_cxx_sources += $(cairo_vg_cxx_sources) +all_cairo_sources += $(cairo_vg_sources) +if CAIRO_HAS_VG_SURFACE +enabled_cairo_headers += $(cairo_vg_headers) +enabled_cairo_private += $(cairo_vg_private) +enabled_cairo_cxx_sources += $(cairo_vg_cxx_sources) +enabled_cairo_sources += $(cairo_vg_sources) +endif +all_cairo_pkgconf += cairo-vg.pc +if CAIRO_HAS_VG_SURFACE +enabled_cairo_pkgconf += cairo-vg.pc +endif + +supported_cairo_headers += $(cairo_egl_headers) +all_cairo_headers += $(cairo_egl_headers) +all_cairo_private += $(cairo_egl_private) +all_cairo_cxx_sources += $(cairo_egl_cxx_sources) +all_cairo_sources += $(cairo_egl_sources) +if CAIRO_HAS_EGL_FUNCTIONS +enabled_cairo_headers += $(cairo_egl_headers) +enabled_cairo_private += $(cairo_egl_private) +enabled_cairo_cxx_sources += $(cairo_egl_cxx_sources) +enabled_cairo_sources += $(cairo_egl_sources) +endif +all_cairo_pkgconf += cairo-egl.pc +if CAIRO_HAS_EGL_FUNCTIONS +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_cxx_sources += $(cairo_glx_cxx_sources) +all_cairo_sources += $(cairo_glx_sources) +if CAIRO_HAS_GLX_FUNCTIONS +enabled_cairo_headers += $(cairo_glx_headers) +enabled_cairo_private += $(cairo_glx_private) +enabled_cairo_cxx_sources += $(cairo_glx_cxx_sources) +enabled_cairo_sources += $(cairo_glx_sources) +endif +all_cairo_pkgconf += cairo-glx.pc +if CAIRO_HAS_GLX_FUNCTIONS +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_cxx_sources += $(cairo_wgl_cxx_sources) +all_cairo_sources += $(cairo_wgl_sources) +if CAIRO_HAS_WGL_FUNCTIONS +enabled_cairo_headers += $(cairo_wgl_headers) +enabled_cairo_private += $(cairo_wgl_private) +enabled_cairo_cxx_sources += $(cairo_wgl_cxx_sources) +enabled_cairo_sources += $(cairo_wgl_sources) +endif +all_cairo_pkgconf += cairo-wgl.pc +if CAIRO_HAS_WGL_FUNCTIONS +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_cxx_sources += $(cairo_script_cxx_sources) +all_cairo_sources += $(cairo_script_sources) +if CAIRO_HAS_SCRIPT_SURFACE +enabled_cairo_headers += $(cairo_script_headers) +enabled_cairo_private += $(cairo_script_private) +enabled_cairo_cxx_sources += $(cairo_script_cxx_sources) +enabled_cairo_sources += $(cairo_script_sources) +endif +all_cairo_pkgconf += cairo-script.pc +if CAIRO_HAS_SCRIPT_SURFACE +enabled_cairo_pkgconf += cairo-script.pc +endif + +supported_cairo_headers += $(cairo_ft_headers) +all_cairo_headers += $(cairo_ft_headers) +all_cairo_private += $(cairo_ft_private) +all_cairo_cxx_sources += $(cairo_ft_cxx_sources) +all_cairo_sources += $(cairo_ft_sources) +if CAIRO_HAS_FT_FONT +enabled_cairo_headers += $(cairo_ft_headers) +enabled_cairo_private += $(cairo_ft_private) +enabled_cairo_cxx_sources += $(cairo_ft_cxx_sources) +enabled_cairo_sources += $(cairo_ft_sources) +endif +all_cairo_pkgconf += cairo-ft.pc +if CAIRO_HAS_FT_FONT +enabled_cairo_pkgconf += cairo-ft.pc +endif + +supported_cairo_headers += $(cairo_fc_headers) +all_cairo_headers += $(cairo_fc_headers) +all_cairo_private += $(cairo_fc_private) +all_cairo_cxx_sources += $(cairo_fc_cxx_sources) +all_cairo_sources += $(cairo_fc_sources) +if CAIRO_HAS_FC_FONT +enabled_cairo_headers += $(cairo_fc_headers) +enabled_cairo_private += $(cairo_fc_private) +enabled_cairo_cxx_sources += $(cairo_fc_cxx_sources) +enabled_cairo_sources += $(cairo_fc_sources) +endif +all_cairo_pkgconf += cairo-fc.pc +if CAIRO_HAS_FC_FONT +enabled_cairo_pkgconf += cairo-fc.pc +endif + +supported_cairo_headers += $(cairo_ps_headers) +all_cairo_headers += $(cairo_ps_headers) +all_cairo_private += $(cairo_ps_private) +all_cairo_cxx_sources += $(cairo_ps_cxx_sources) +all_cairo_sources += $(cairo_ps_sources) +if CAIRO_HAS_PS_SURFACE +enabled_cairo_headers += $(cairo_ps_headers) +enabled_cairo_private += $(cairo_ps_private) +enabled_cairo_cxx_sources += $(cairo_ps_cxx_sources) +enabled_cairo_sources += $(cairo_ps_sources) +endif +all_cairo_pkgconf += cairo-ps.pc +if CAIRO_HAS_PS_SURFACE +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_cxx_sources += $(cairo_pdf_cxx_sources) +all_cairo_sources += $(cairo_pdf_sources) +if CAIRO_HAS_PDF_SURFACE +enabled_cairo_headers += $(cairo_pdf_headers) +enabled_cairo_private += $(cairo_pdf_private) +enabled_cairo_cxx_sources += $(cairo_pdf_cxx_sources) +enabled_cairo_sources += $(cairo_pdf_sources) +endif +all_cairo_pkgconf += cairo-pdf.pc +if CAIRO_HAS_PDF_SURFACE +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_cxx_sources += $(cairo_svg_cxx_sources) +all_cairo_sources += $(cairo_svg_sources) +if CAIRO_HAS_SVG_SURFACE +enabled_cairo_headers += $(cairo_svg_headers) +enabled_cairo_private += $(cairo_svg_private) +enabled_cairo_cxx_sources += $(cairo_svg_cxx_sources) +enabled_cairo_sources += $(cairo_svg_sources) +endif +all_cairo_pkgconf += cairo-svg.pc +if CAIRO_HAS_SVG_SURFACE +enabled_cairo_pkgconf += cairo-svg.pc +endif + +all_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) +all_cairo_cxx_sources += $(cairo_test_surfaces_cxx_sources) +all_cairo_sources += $(cairo_test_surfaces_sources) +if CAIRO_HAS_TEST_SURFACES +enabled_cairo_private += $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) +enabled_cairo_cxx_sources += $(cairo_test_surfaces_cxx_sources) +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_cxx_sources += $(cairo_image_cxx_sources) +all_cairo_sources += $(cairo_image_sources) +enabled_cairo_headers += $(cairo_image_headers) +enabled_cairo_private += $(cairo_image_private) +enabled_cairo_cxx_sources += $(cairo_image_cxx_sources) +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_cxx_sources += $(cairo_mime_cxx_sources) +all_cairo_sources += $(cairo_mime_sources) +enabled_cairo_headers += $(cairo_mime_headers) +enabled_cairo_private += $(cairo_mime_private) +enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources) +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_cxx_sources += $(cairo_recording_cxx_sources) +all_cairo_sources += $(cairo_recording_sources) +enabled_cairo_headers += $(cairo_recording_headers) +enabled_cairo_private += $(cairo_recording_private) +enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources) +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_cxx_sources += $(cairo_observer_cxx_sources) +all_cairo_sources += $(cairo_observer_sources) +enabled_cairo_headers += $(cairo_observer_headers) +enabled_cairo_private += $(cairo_observer_private) +enabled_cairo_cxx_sources += $(cairo_observer_cxx_sources) +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_cxx_sources += $(cairo_tee_cxx_sources) +all_cairo_sources += $(cairo_tee_sources) +if CAIRO_HAS_TEE_SURFACE +enabled_cairo_headers += $(cairo_tee_headers) +enabled_cairo_private += $(cairo_tee_private) +enabled_cairo_cxx_sources += $(cairo_tee_cxx_sources) +enabled_cairo_sources += $(cairo_tee_sources) +endif +all_cairo_pkgconf += cairo-tee.pc +if CAIRO_HAS_TEE_SURFACE +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_cxx_sources += $(cairo_xml_cxx_sources) +all_cairo_sources += $(cairo_xml_sources) +if CAIRO_HAS_XML_SURFACE +enabled_cairo_headers += $(cairo_xml_headers) +enabled_cairo_private += $(cairo_xml_private) +enabled_cairo_cxx_sources += $(cairo_xml_cxx_sources) +enabled_cairo_sources += $(cairo_xml_sources) +endif +all_cairo_pkgconf += cairo-xml.pc +if CAIRO_HAS_XML_SURFACE +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_cxx_sources += $(cairo_user_cxx_sources) +all_cairo_sources += $(cairo_user_sources) +enabled_cairo_headers += $(cairo_user_headers) +enabled_cairo_private += $(cairo_user_private) +enabled_cairo_cxx_sources += $(cairo_user_cxx_sources) +enabled_cairo_sources += $(cairo_user_sources) + +all_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers) +all_cairo_cxx_sources += $(cairo_pthread_cxx_sources) +all_cairo_sources += $(cairo_pthread_sources) +if CAIRO_HAS_PTHREAD +enabled_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers) +enabled_cairo_cxx_sources += $(cairo_pthread_cxx_sources) +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_cxx_sources += $(cairo_gobject_cxx_sources) +all_cairo_sources += $(cairo_gobject_sources) +if CAIRO_HAS_GOBJECT_FUNCTIONS +enabled_cairo_headers += $(cairo_gobject_headers) +enabled_cairo_private += $(cairo_gobject_private) +enabled_cairo_cxx_sources += $(cairo_gobject_cxx_sources) +enabled_cairo_sources += $(cairo_gobject_sources) +endif +all_cairo_pkgconf += cairo-gobject.pc +if CAIRO_HAS_GOBJECT_FUNCTIONS +enabled_cairo_pkgconf += cairo-gobject.pc +endif + +all_cairo_private += $(cairo_trace_private) $(cairo_trace_headers) +all_cairo_cxx_sources += $(cairo_trace_cxx_sources) +all_cairo_sources += $(cairo_trace_sources) +if CAIRO_HAS_TRACE +enabled_cairo_private += $(cairo_trace_private) $(cairo_trace_headers) +enabled_cairo_cxx_sources += $(cairo_trace_cxx_sources) +enabled_cairo_sources += $(cairo_trace_sources) +endif + +all_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers) +all_cairo_cxx_sources += $(cairo_interpreter_cxx_sources) +all_cairo_sources += $(cairo_interpreter_sources) +if CAIRO_HAS_INTERPRETER +enabled_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers) +enabled_cairo_cxx_sources += $(cairo_interpreter_cxx_sources) +enabled_cairo_sources += $(cairo_interpreter_sources) +endif + +all_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) +all_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources) +all_cairo_sources += $(cairo_symbol_lookup_sources) +if CAIRO_HAS_SYMBOL_LOOKUP +enabled_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) +enabled_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources) +enabled_cairo_sources += $(cairo_symbol_lookup_sources) +endif diff --git a/gfx/cairo/cairo/src/Makefile.am.hide b/gfx/cairo/cairo/src/Makefile.am.hide new file mode 100644 index 000000000000..23ba1861d41e --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.am.hide @@ -0,0 +1,117 @@ +# 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 + +if BUILD_CXX +cairo_cxx_lib = libcairo_cxx.la +else +cairo_cxx_lib = +endif + +noinst_LTLIBRARIES = $(cairo_cxx_lib) +libcairo_cxx_la_SOURCES = \ + $(enabled_cairo_headers) \ + $(enabled_cairo_private) \ + $(enabled_cairo_cxx_sources) \ + $(NULL) +libcairo_cxx_la_LDFLAGS = $(AM_LDFLAGS) $(export_symbols) +libcairo_cxx_la_LIBADD = $(CAIRO_LIBS) +libcairo_cxx_la_DEPENDENCIES = $(cairo_def_dependency) + + +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) \ + $(cairo_cxx_lib) +libcairo_la_DEPENDENCIES = $(cairo_def_dependency) $(cairo_cxx_lib) + +# 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/gfx/cairo/cairo/src/Makefile.in.hide b/gfx/cairo/cairo/src/Makefile.in.hide new file mode 100644 index 000000000000..ff2849120144 --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.in.hide @@ -0,0 +1,3703 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Note: All source files are listed in Makefile.sources. + +# Generated by configure. Do not edit. + +# 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. +# + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +EXTRA_PROGRAMS = +TESTS = $(am__EXEEXT_1) $(am__append_186) +check_PROGRAMS = check-link$(EXEEXT) +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_1 = $(cairo_xlib_headers) +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_2 = $(cairo_xlib_private) +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_3 = $(cairo_xlib_cxx_sources) +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_4 = $(cairo_xlib_sources) +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__append_5 = cairo-xlib.pc +@CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__append_6 = $(cairo_xlib_xrender_headers) +@CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__append_7 = $(cairo_xlib_xrender_private) +@CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__append_8 = $(cairo_xlib_xrender_cxx_sources) +@CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__append_9 = $(cairo_xlib_xrender_sources) +@CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__append_10 = cairo-xlib-xrender.pc +@CAIRO_HAS_XCB_SURFACE_TRUE@am__append_11 = $(cairo_xcb_headers) +@CAIRO_HAS_XCB_SURFACE_TRUE@am__append_12 = $(cairo_xcb_private) +@CAIRO_HAS_XCB_SURFACE_TRUE@am__append_13 = $(cairo_xcb_cxx_sources) +@CAIRO_HAS_XCB_SURFACE_TRUE@am__append_14 = $(cairo_xcb_sources) +@CAIRO_HAS_XCB_SURFACE_TRUE@am__append_15 = cairo-xcb.pc +@CAIRO_HAS_XLIB_XCB_FUNCTIONS_TRUE@am__append_16 = $(cairo_xlib_xcb_headers) +@CAIRO_HAS_XLIB_XCB_FUNCTIONS_TRUE@am__append_17 = $(cairo_xlib_xcb_private) +@CAIRO_HAS_XLIB_XCB_FUNCTIONS_TRUE@am__append_18 = $(cairo_xlib_xcb_cxx_sources) +@CAIRO_HAS_XLIB_XCB_FUNCTIONS_TRUE@am__append_19 = $(cairo_xlib_xcb_sources) +@CAIRO_HAS_XLIB_XCB_FUNCTIONS_TRUE@am__append_20 = cairo-xlib-xcb.pc +@CAIRO_HAS_XCB_SHM_FUNCTIONS_TRUE@am__append_21 = $(cairo_xcb_shm_headers) +@CAIRO_HAS_XCB_SHM_FUNCTIONS_TRUE@am__append_22 = $(cairo_xcb_shm_private) +@CAIRO_HAS_XCB_SHM_FUNCTIONS_TRUE@am__append_23 = $(cairo_xcb_shm_cxx_sources) +@CAIRO_HAS_XCB_SHM_FUNCTIONS_TRUE@am__append_24 = $(cairo_xcb_shm_sources) +@CAIRO_HAS_XCB_SHM_FUNCTIONS_TRUE@am__append_25 = cairo-xcb-shm.pc +@CAIRO_HAS_QT_SURFACE_TRUE@am__append_26 = $(cairo_qt_headers) +@CAIRO_HAS_QT_SURFACE_TRUE@am__append_27 = $(cairo_qt_private) +@CAIRO_HAS_QT_SURFACE_TRUE@am__append_28 = $(cairo_qt_cxx_sources) +@CAIRO_HAS_QT_SURFACE_TRUE@am__append_29 = $(cairo_qt_sources) +@CAIRO_HAS_QT_SURFACE_TRUE@am__append_30 = cairo-qt.pc +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__append_31 = $(cairo_quartz_headers) +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__append_32 = $(cairo_quartz_private) +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__append_33 = $(cairo_quartz_cxx_sources) +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__append_34 = $(cairo_quartz_sources) +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__append_35 = cairo-quartz.pc +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__append_36 = $(cairo_quartz_font_headers) +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__append_37 = $(cairo_quartz_font_private) +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__append_38 = $(cairo_quartz_font_cxx_sources) +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__append_39 = $(cairo_quartz_font_sources) +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__append_40 = cairo-quartz-font.pc +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__append_41 = $(cairo_quartz_image_headers) +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__append_42 = $(cairo_quartz_image_private) +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__append_43 = $(cairo_quartz_image_cxx_sources) +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__append_44 = $(cairo_quartz_image_sources) +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__append_45 = cairo-quartz-image.pc +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__append_46 = $(cairo_win32_headers) +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__append_47 = $(cairo_win32_private) +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__append_48 = $(cairo_win32_cxx_sources) +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__append_49 = $(cairo_win32_sources) +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__append_50 = cairo-win32.pc +@CAIRO_HAS_WIN32_FONT_TRUE@am__append_51 = $(cairo_win32_font_headers) +@CAIRO_HAS_WIN32_FONT_TRUE@am__append_52 = $(cairo_win32_font_private) +@CAIRO_HAS_WIN32_FONT_TRUE@am__append_53 = $(cairo_win32_font_cxx_sources) +@CAIRO_HAS_WIN32_FONT_TRUE@am__append_54 = $(cairo_win32_font_sources) +@CAIRO_HAS_WIN32_FONT_TRUE@am__append_55 = cairo-win32-font.pc +@CAIRO_HAS_OS2_SURFACE_TRUE@am__append_56 = $(cairo_os2_headers) +@CAIRO_HAS_OS2_SURFACE_TRUE@am__append_57 = $(cairo_os2_private) +@CAIRO_HAS_OS2_SURFACE_TRUE@am__append_58 = $(cairo_os2_cxx_sources) +@CAIRO_HAS_OS2_SURFACE_TRUE@am__append_59 = $(cairo_os2_sources) +@CAIRO_HAS_OS2_SURFACE_TRUE@am__append_60 = cairo-os2.pc +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__append_61 = $(cairo_beos_headers) +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__append_62 = $(cairo_beos_private) +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__append_63 = $(cairo_beos_cxx_sources) +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__append_64 = $(cairo_beos_sources) +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__append_65 = cairo-beos.pc +@CAIRO_HAS_DRM_SURFACE_TRUE@am__append_66 = $(cairo_drm_headers) +@CAIRO_HAS_DRM_SURFACE_TRUE@am__append_67 = $(cairo_drm_private) +@CAIRO_HAS_DRM_SURFACE_TRUE@am__append_68 = $(cairo_drm_cxx_sources) +@CAIRO_HAS_DRM_SURFACE_TRUE@am__append_69 = $(cairo_drm_sources) +@CAIRO_HAS_DRM_SURFACE_TRUE@am__append_70 = cairo-drm.pc +@CAIRO_HAS_GALLIUM_SURFACE_TRUE@am__append_71 = $(cairo_gallium_headers) +@CAIRO_HAS_GALLIUM_SURFACE_TRUE@am__append_72 = $(cairo_gallium_private) +@CAIRO_HAS_GALLIUM_SURFACE_TRUE@am__append_73 = $(cairo_gallium_cxx_sources) +@CAIRO_HAS_GALLIUM_SURFACE_TRUE@am__append_74 = $(cairo_gallium_sources) +@CAIRO_HAS_GALLIUM_SURFACE_TRUE@am__append_75 = cairo-gallium.pc +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__append_76 = $(cairo_png_headers) +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__append_77 = $(cairo_png_private) +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__append_78 = $(cairo_png_cxx_sources) +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__append_79 = $(cairo_png_sources) +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__append_80 = cairo-png.pc +@CAIRO_HAS_GL_SURFACE_TRUE@am__append_81 = $(cairo_gl_headers) +@CAIRO_HAS_GL_SURFACE_TRUE@am__append_82 = $(cairo_gl_private) +@CAIRO_HAS_GL_SURFACE_TRUE@am__append_83 = $(cairo_gl_cxx_sources) +@CAIRO_HAS_GL_SURFACE_TRUE@am__append_84 = $(cairo_gl_sources) +@CAIRO_HAS_GL_SURFACE_TRUE@am__append_85 = cairo-gl.pc +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__append_86 = $(cairo_glesv2_headers) +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__append_87 = $(cairo_glesv2_private) +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__append_88 = $(cairo_glesv2_cxx_sources) +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__append_89 = $(cairo_glesv2_sources) +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__append_90 = cairo-glesv2.pc +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__append_91 = $(cairo_glesv3_headers) +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__append_92 = $(cairo_glesv3_private) +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__append_93 = $(cairo_glesv3_cxx_sources) +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__append_94 = $(cairo_glesv3_sources) +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__append_95 = cairo-glesv3.pc +@CAIRO_HAS_COGL_SURFACE_TRUE@am__append_96 = $(cairo_cogl_headers) +@CAIRO_HAS_COGL_SURFACE_TRUE@am__append_97 = $(cairo_cogl_private) +@CAIRO_HAS_COGL_SURFACE_TRUE@am__append_98 = $(cairo_cogl_cxx_sources) +@CAIRO_HAS_COGL_SURFACE_TRUE@am__append_99 = $(cairo_cogl_sources) +@CAIRO_HAS_COGL_SURFACE_TRUE@am__append_100 = cairo-cogl.pc +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_101 = $(cairo_directfb_headers) +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_102 = $(cairo_directfb_private) +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_103 = $(cairo_directfb_cxx_sources) +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_104 = $(cairo_directfb_sources) +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__append_105 = cairo-directfb.pc +@CAIRO_HAS_VG_SURFACE_TRUE@am__append_106 = $(cairo_vg_headers) +@CAIRO_HAS_VG_SURFACE_TRUE@am__append_107 = $(cairo_vg_private) +@CAIRO_HAS_VG_SURFACE_TRUE@am__append_108 = $(cairo_vg_cxx_sources) +@CAIRO_HAS_VG_SURFACE_TRUE@am__append_109 = $(cairo_vg_sources) +@CAIRO_HAS_VG_SURFACE_TRUE@am__append_110 = cairo-vg.pc +@CAIRO_HAS_EGL_FUNCTIONS_TRUE@am__append_111 = $(cairo_egl_headers) +@CAIRO_HAS_EGL_FUNCTIONS_TRUE@am__append_112 = $(cairo_egl_private) +@CAIRO_HAS_EGL_FUNCTIONS_TRUE@am__append_113 = $(cairo_egl_cxx_sources) +@CAIRO_HAS_EGL_FUNCTIONS_TRUE@am__append_114 = $(cairo_egl_sources) +@CAIRO_HAS_EGL_FUNCTIONS_TRUE@am__append_115 = cairo-egl.pc +@CAIRO_HAS_GLX_FUNCTIONS_TRUE@am__append_116 = $(cairo_glx_headers) +@CAIRO_HAS_GLX_FUNCTIONS_TRUE@am__append_117 = $(cairo_glx_private) +@CAIRO_HAS_GLX_FUNCTIONS_TRUE@am__append_118 = $(cairo_glx_cxx_sources) +@CAIRO_HAS_GLX_FUNCTIONS_TRUE@am__append_119 = $(cairo_glx_sources) +@CAIRO_HAS_GLX_FUNCTIONS_TRUE@am__append_120 = cairo-glx.pc +@CAIRO_HAS_WGL_FUNCTIONS_TRUE@am__append_121 = $(cairo_wgl_headers) +@CAIRO_HAS_WGL_FUNCTIONS_TRUE@am__append_122 = $(cairo_wgl_private) +@CAIRO_HAS_WGL_FUNCTIONS_TRUE@am__append_123 = $(cairo_wgl_cxx_sources) +@CAIRO_HAS_WGL_FUNCTIONS_TRUE@am__append_124 = $(cairo_wgl_sources) +@CAIRO_HAS_WGL_FUNCTIONS_TRUE@am__append_125 = cairo-wgl.pc +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_126 = $(cairo_script_headers) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_127 = $(cairo_script_private) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_128 = $(cairo_script_cxx_sources) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_129 = $(cairo_script_sources) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__append_130 = cairo-script.pc +@CAIRO_HAS_FT_FONT_TRUE@am__append_131 = $(cairo_ft_headers) +@CAIRO_HAS_FT_FONT_TRUE@am__append_132 = $(cairo_ft_private) +@CAIRO_HAS_FT_FONT_TRUE@am__append_133 = $(cairo_ft_cxx_sources) +@CAIRO_HAS_FT_FONT_TRUE@am__append_134 = $(cairo_ft_sources) +@CAIRO_HAS_FT_FONT_TRUE@am__append_135 = cairo-ft.pc +@CAIRO_HAS_FC_FONT_TRUE@am__append_136 = $(cairo_fc_headers) +@CAIRO_HAS_FC_FONT_TRUE@am__append_137 = $(cairo_fc_private) +@CAIRO_HAS_FC_FONT_TRUE@am__append_138 = $(cairo_fc_cxx_sources) +@CAIRO_HAS_FC_FONT_TRUE@am__append_139 = $(cairo_fc_sources) +@CAIRO_HAS_FC_FONT_TRUE@am__append_140 = cairo-fc.pc +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_141 = $(cairo_ps_headers) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_142 = $(cairo_ps_private) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_143 = $(cairo_ps_cxx_sources) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_144 = $(cairo_ps_sources) +@CAIRO_HAS_PS_SURFACE_TRUE@am__append_145 = cairo-ps.pc +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_146 = $(cairo_pdf_headers) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_147 = $(cairo_pdf_private) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_148 = $(cairo_pdf_cxx_sources) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_149 = $(cairo_pdf_sources) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__append_150 = cairo-pdf.pc +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_151 = $(cairo_svg_headers) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_152 = $(cairo_svg_private) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_153 = $(cairo_svg_cxx_sources) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_154 = $(cairo_svg_sources) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__append_155 = cairo-svg.pc +@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_156 = $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) +@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_157 = $(cairo_test_surfaces_cxx_sources) +@CAIRO_HAS_TEST_SURFACES_TRUE@am__append_158 = $(cairo_test_surfaces_sources) +@CAIRO_HAS_TEE_SURFACE_TRUE@am__append_159 = $(cairo_tee_headers) +@CAIRO_HAS_TEE_SURFACE_TRUE@am__append_160 = $(cairo_tee_private) +@CAIRO_HAS_TEE_SURFACE_TRUE@am__append_161 = $(cairo_tee_cxx_sources) +@CAIRO_HAS_TEE_SURFACE_TRUE@am__append_162 = $(cairo_tee_sources) +@CAIRO_HAS_TEE_SURFACE_TRUE@am__append_163 = cairo-tee.pc +@CAIRO_HAS_XML_SURFACE_TRUE@am__append_164 = $(cairo_xml_headers) +@CAIRO_HAS_XML_SURFACE_TRUE@am__append_165 = $(cairo_xml_private) +@CAIRO_HAS_XML_SURFACE_TRUE@am__append_166 = $(cairo_xml_cxx_sources) +@CAIRO_HAS_XML_SURFACE_TRUE@am__append_167 = $(cairo_xml_sources) +@CAIRO_HAS_XML_SURFACE_TRUE@am__append_168 = cairo-xml.pc +@CAIRO_HAS_PTHREAD_TRUE@am__append_169 = $(cairo_pthread_private) $(cairo_pthread_headers) +@CAIRO_HAS_PTHREAD_TRUE@am__append_170 = $(cairo_pthread_cxx_sources) +@CAIRO_HAS_PTHREAD_TRUE@am__append_171 = $(cairo_pthread_sources) +@CAIRO_HAS_GOBJECT_FUNCTIONS_TRUE@am__append_172 = $(cairo_gobject_headers) +@CAIRO_HAS_GOBJECT_FUNCTIONS_TRUE@am__append_173 = $(cairo_gobject_private) +@CAIRO_HAS_GOBJECT_FUNCTIONS_TRUE@am__append_174 = $(cairo_gobject_cxx_sources) +@CAIRO_HAS_GOBJECT_FUNCTIONS_TRUE@am__append_175 = $(cairo_gobject_sources) +@CAIRO_HAS_GOBJECT_FUNCTIONS_TRUE@am__append_176 = cairo-gobject.pc +@CAIRO_HAS_TRACE_TRUE@am__append_177 = $(cairo_trace_private) $(cairo_trace_headers) +@CAIRO_HAS_TRACE_TRUE@am__append_178 = $(cairo_trace_cxx_sources) +@CAIRO_HAS_TRACE_TRUE@am__append_179 = $(cairo_trace_sources) +@CAIRO_HAS_INTERPRETER_TRUE@am__append_180 = $(cairo_interpreter_private) $(cairo_interpreter_headers) +@CAIRO_HAS_INTERPRETER_TRUE@am__append_181 = $(cairo_interpreter_cxx_sources) +@CAIRO_HAS_INTERPRETER_TRUE@am__append_182 = $(cairo_interpreter_sources) +@CAIRO_HAS_SYMBOL_LOOKUP_TRUE@am__append_183 = $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) +@CAIRO_HAS_SYMBOL_LOOKUP_TRUE@am__append_184 = $(cairo_symbol_lookup_cxx_sources) +@CAIRO_HAS_SYMBOL_LOOKUP_TRUE@am__append_185 = $(cairo_symbol_lookup_sources) +@CROSS_COMPILING_FALSE@am__append_186 = check-link$(EXEEXT) +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/build/aclocal.cairo.m4 \ + $(top_srcdir)/build/aclocal.compare.m4 \ + $(top_srcdir)/build/aclocal.enable.m4 \ + $(top_srcdir)/build/aclocal.float.m4 \ + $(top_srcdir)/build/aclocal.gtk-doc.m4 \ + $(top_srcdir)/build/aclocal.makefile.m4 \ + $(top_srcdir)/build/aclocal.pkg.m4 \ + $(top_srcdir)/build/libtool.m4 \ + $(top_srcdir)/build/ltoptions.m4 \ + $(top_srcdir)/build/ltsugar.m4 \ + $(top_srcdir)/build/ltversion.m4 \ + $(top_srcdir)/build/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/src/cairo-version.h \ + $(top_srcdir)/build/configure.ac.version \ + $(top_srcdir)/build/configure.ac.tools \ + $(top_srcdir)/build/configure.ac.features \ + $(top_srcdir)/build/configure.ac.warnings \ + $(top_srcdir)/build/configure.ac.system \ + $(top_srcdir)/build/configure.ac.analysis \ + $(top_srcdir)/build/configure.ac.noversion \ + $(top_srcdir)/build/configure.ac.pthread \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__cairoinclude_HEADERS_DIST) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = cairo.pc cairo-xlib.pc cairo-xlib-xrender.pc \ + cairo-xcb.pc cairo-xlib-xcb.pc cairo-xcb-shm.pc cairo-qt.pc \ + cairo-quartz.pc cairo-quartz-font.pc cairo-quartz-image.pc \ + cairo-win32.pc cairo-win32-font.pc cairo-os2.pc cairo-beos.pc \ + cairo-drm.pc cairo-gallium.pc cairo-png.pc cairo-gl.pc \ + cairo-glesv2.pc cairo-glesv3.pc cairo-cogl.pc \ + cairo-directfb.pc cairo-vg.pc cairo-egl.pc cairo-glx.pc \ + cairo-wgl.pc cairo-script.pc cairo-ft.pc cairo-fc.pc \ + cairo-ps.pc cairo-pdf.pc cairo-svg.pc cairo-tee.pc \ + cairo-xml.pc cairo-gobject.pc +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" \ + "$(DESTDIR)$(cairoincludedir)" "$(DESTDIR)$(cairoincludedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +@BUILD_CXX_TRUE@am__DEPENDENCIES_2 = libcairo_cxx.la +am__libcairo_la_SOURCES_DIST = cairo.h cairo-version.h \ + cairo-deprecated.h cairo-xlib.h cairo-xlib-xrender.h \ + cairo-xcb.h cairo-qt.h cairo-quartz.h cairo-quartz-image.h \ + cairo-win32.h cairo-os2.h cairo-beos.h cairo-drm.h cairo-gl.h \ + cairo-cogl.h cairo-directfb.h cairo-vg.h cairo-script.h \ + cairo-ft.h cairo-ps.h cairo-pdf.h cairo-svg.h cairo-tee.h \ + cairo-xml.h 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 \ + cairo-scaled-font-subsets-private.h \ + cairo-truetype-subset-private.h cairo-type1-private.h \ + cairo-type3-glyph-surface-private.h \ + cairo-pdf-operators-private.h cairo-pdf-shading-private.h \ + cairo-tag-attributes-private.h cairo-xlib-private.h \ + cairo-xlib-surface-private.h cairo-xlib-xrender-private.h \ + cairo-xcb-private.h cairo-quartz-private.h \ + win32/cairo-win32-private.h cairo-os2-private.h \ + drm/cairo-drm-private.h drm/cairo-drm-intel-private.h \ + drm/cairo-drm-intel-brw-defines.h \ + drm/cairo-drm-intel-brw-structs.h drm/cairo-drm-intel-brw-eu.h \ + drm/cairo-drm-intel-command-private.h \ + drm/cairo-drm-intel-ioctl-private.h \ + drm/cairo-drm-i915-private.h drm/cairo-drm-i965-private.h \ + drm/cairo-drm-radeon-private.h cairo-gl-private.h \ + cairo-gl-dispatch-private.h cairo-gl-ext-def-private.h \ + cairo-gl-gradient-private.h cairo-cogl-private.h \ + cairo-cogl-gradient-private.h cairo-script-private.h \ + cairo-ft-private.h cairo-ps-surface-private.h \ + cairo-pdf-surface-private.h cairo-tag-stack-private.h \ + cairo-svg-surface-private.h test-compositor-surface.h \ + test-compositor-surface-private.h \ + test-null-compositor-surface.h test-paginated-surface.h \ + cairo-tee-surface-private.h 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 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 \ + cairo-pdf-operators.c cairo-pdf-shading.c \ + cairo-tag-attributes.c cairo-deflate-stream.c \ + 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 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 \ + cairo-quartz-surface.c cairo-quartz-font.c \ + cairo-quartz-image-surface.c 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 win32/cairo-win32-font.c \ + cairo-os2-surface.c drm/cairo-drm.c drm/cairo-drm-bo.c \ + drm/cairo-drm-surface.c drm/cairo-drm-intel.c \ + drm/cairo-drm-intel-debug.c drm/cairo-drm-intel-surface.c \ + drm/cairo-drm-i915-surface.c drm/cairo-drm-i915-glyphs.c \ + drm/cairo-drm-i915-shader.c drm/cairo-drm-i915-spans.c \ + drm/cairo-drm-i965-surface.c drm/cairo-drm-i965-glyphs.c \ + drm/cairo-drm-i965-shader.c drm/cairo-drm-i965-spans.c \ + drm/cairo-drm-intel-brw-eu.c drm/cairo-drm-intel-brw-eu-emit.c \ + drm/cairo-drm-intel-brw-eu-util.c drm/cairo-drm-radeon.c \ + drm/cairo-drm-radeon-surface.c drm/cairo-drm-gallium-surface.c \ + cairo-png.c 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-cogl-surface.c \ + cairo-cogl-gradient.c cairo-directfb-surface.c \ + cairo-vg-surface.c cairo-egl-context.c cairo-glx-context.c \ + cairo-wgl-context.c cairo-script-surface.c cairo-ft-font.c \ + cairo-ps-surface.c cairo-pdf-surface.c cairo-pdf-interchange.c \ + cairo-tag-stack.c cairo-svg-surface.c \ + test-compositor-surface.c test-null-compositor-surface.c \ + test-base-compositor-surface.c test-paginated-surface.c \ + cairo-tee-surface.c cairo-xml-surface.c +am__objects_1 = +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__objects_2 = $(am__objects_1) +@CAIRO_HAS_XLIB_XRENDER_SURFACE_TRUE@am__objects_3 = $(am__objects_1) +@CAIRO_HAS_XCB_SURFACE_TRUE@am__objects_4 = $(am__objects_1) +@CAIRO_HAS_QT_SURFACE_TRUE@am__objects_5 = $(am__objects_1) +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__objects_6 = $(am__objects_1) +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__objects_7 = $(am__objects_1) +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__objects_8 = $(am__objects_1) +@CAIRO_HAS_OS2_SURFACE_TRUE@am__objects_9 = $(am__objects_1) +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__objects_10 = $(am__objects_1) +@CAIRO_HAS_DRM_SURFACE_TRUE@am__objects_11 = $(am__objects_1) +@CAIRO_HAS_GL_SURFACE_TRUE@am__objects_12 = $(am__objects_1) +am__objects_13 = $(am__objects_1) +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__objects_14 = $(am__objects_13) +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__objects_15 = $(am__objects_13) +@CAIRO_HAS_COGL_SURFACE_TRUE@am__objects_16 = $(am__objects_1) +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__objects_17 = $(am__objects_1) +@CAIRO_HAS_VG_SURFACE_TRUE@am__objects_18 = $(am__objects_1) +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__objects_19 = $(am__objects_1) +@CAIRO_HAS_FT_FONT_TRUE@am__objects_20 = $(am__objects_1) +@CAIRO_HAS_PS_SURFACE_TRUE@am__objects_21 = $(am__objects_1) +@CAIRO_HAS_PDF_SURFACE_TRUE@am__objects_22 = $(am__objects_1) +@CAIRO_HAS_SVG_SURFACE_TRUE@am__objects_23 = $(am__objects_1) +@CAIRO_HAS_TEE_SURFACE_TRUE@am__objects_24 = $(am__objects_1) +@CAIRO_HAS_XML_SURFACE_TRUE@am__objects_25 = $(am__objects_1) +am__objects_26 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) $(am__objects_1) $(am__objects_1) \ + $(am__objects_5) $(am__objects_6) $(am__objects_1) \ + $(am__objects_7) $(am__objects_8) $(am__objects_1) \ + $(am__objects_9) $(am__objects_10) $(am__objects_11) \ + $(am__objects_1) $(am__objects_1) $(am__objects_12) \ + $(am__objects_14) $(am__objects_15) $(am__objects_16) \ + $(am__objects_17) $(am__objects_18) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_19) \ + $(am__objects_20) $(am__objects_1) $(am__objects_21) \ + $(am__objects_22) $(am__objects_23) $(am__objects_24) \ + $(am__objects_25) $(am__objects_1) +am__objects_27 = $(am__objects_1) $(am__objects_1) +@CAIRO_HAS_TEST_SURFACES_TRUE@am__objects_28 = $(am__objects_1) +am__objects_29 = $(am__objects_27) $(am__objects_2) $(am__objects_1) \ + $(am__objects_4) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_6) $(am__objects_1) \ + $(am__objects_1) $(am__objects_8) $(am__objects_1) \ + $(am__objects_9) $(am__objects_1) $(am__objects_11) \ + $(am__objects_1) $(am__objects_1) $(am__objects_12) \ + $(am__objects_14) $(am__objects_15) $(am__objects_16) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_19) \ + $(am__objects_20) $(am__objects_1) $(am__objects_21) \ + $(am__objects_22) $(am__objects_23) $(am__objects_28) \ + $(am__objects_24) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) +am__objects_30 = cairo-cff-subset.lo cairo-scaled-font-subsets.lo \ + cairo-truetype-subset.lo cairo-type1-fallback.lo \ + cairo-type1-glyph-names.lo cairo-type1-subset.lo \ + cairo-type3-glyph-surface.lo +am__objects_31 = cairo-pdf-operators.lo cairo-pdf-shading.lo \ + cairo-tag-attributes.lo +am__objects_32 = cairo-deflate-stream.lo +am__objects_33 = cairo-analysis-surface.lo cairo-arc.lo cairo-array.lo \ + cairo-atomic.lo cairo-base64-stream.lo cairo-base85-stream.lo \ + cairo-bentley-ottmann-rectangular.lo \ + cairo-bentley-ottmann-rectilinear.lo cairo-bentley-ottmann.lo \ + cairo-botor-scan-converter.lo cairo-boxes-intersect.lo \ + cairo-boxes.lo cairo-cache.lo cairo-clip-boxes.lo \ + cairo-clip-polygon.lo cairo-clip-region.lo \ + cairo-clip-surface.lo cairo-clip-tor-scan-converter.lo \ + cairo-clip.lo cairo-color.lo cairo-composite-rectangles.lo \ + cairo-compositor.lo cairo-contour.lo cairo-damage.lo \ + cairo-debug.lo cairo-default-context.lo cairo-device.lo \ + cairo-error.lo cairo-fallback-compositor.lo cairo-fixed.lo \ + cairo-font-face-twin-data.lo cairo-font-face-twin.lo \ + cairo-font-face.lo cairo-font-options.lo cairo-freed-pool.lo \ + cairo-freelist.lo cairo-gstate.lo cairo-hash.lo cairo-hull.lo \ + cairo-image-compositor.lo cairo-image-info.lo \ + cairo-image-source.lo cairo-image-surface.lo cairo-line.lo \ + cairo-lzw.lo cairo-mask-compositor.lo cairo-matrix.lo \ + cairo-mempool.lo cairo-mesh-pattern-rasterizer.lo \ + cairo-misc.lo cairo-mono-scan-converter.lo cairo-mutex.lo \ + cairo-no-compositor.lo cairo-observer.lo \ + cairo-output-stream.lo cairo-paginated-surface.lo \ + cairo-path-bounds.lo cairo-path-fill.lo cairo-path-fixed.lo \ + cairo-path-in-fill.lo cairo-path-stroke-boxes.lo \ + cairo-path-stroke-polygon.lo cairo-path-stroke-traps.lo \ + cairo-path-stroke-tristrip.lo cairo-path-stroke.lo \ + cairo-path.lo cairo-pattern.lo cairo-pen.lo \ + cairo-polygon-intersect.lo cairo-polygon-reduce.lo \ + cairo-polygon.lo cairo-raster-source-pattern.lo \ + cairo-recording-surface.lo cairo-rectangle.lo \ + cairo-rectangular-scan-converter.lo cairo-region.lo \ + cairo-rtree.lo cairo-scaled-font.lo \ + cairo-shape-mask-compositor.lo cairo-slope.lo \ + cairo-spans-compositor.lo cairo-spans.lo cairo-spline.lo \ + cairo-stroke-dash.lo cairo-stroke-style.lo \ + cairo-surface-clipper.lo cairo-surface-fallback.lo \ + cairo-surface-observer.lo cairo-surface-offset.lo \ + cairo-surface-snapshot.lo cairo-surface-subsurface.lo \ + cairo-surface-wrapper.lo cairo-surface.lo cairo-time.lo \ + cairo-tor-scan-converter.lo cairo-tor22-scan-converter.lo \ + cairo-toy-font-face.lo cairo-traps-compositor.lo \ + cairo-traps.lo cairo-tristrip.lo cairo-unicode.lo \ + cairo-user-font.lo cairo-version.lo cairo-wideint.lo cairo.lo \ + $(am__objects_30) $(am__objects_31) $(am__objects_32) +am__objects_34 = cairo-xlib-display.lo cairo-xlib-core-compositor.lo \ + cairo-xlib-fallback-compositor.lo \ + cairo-xlib-render-compositor.lo cairo-xlib-screen.lo \ + cairo-xlib-source.lo cairo-xlib-surface.lo \ + cairo-xlib-surface-shm.lo cairo-xlib-visual.lo \ + cairo-xlib-xcb-surface.lo +@CAIRO_HAS_XLIB_SURFACE_TRUE@am__objects_35 = $(am__objects_34) +am__objects_36 = cairo-xcb-connection.lo cairo-xcb-connection-core.lo \ + cairo-xcb-connection-render.lo cairo-xcb-connection-shm.lo \ + cairo-xcb-screen.lo cairo-xcb-shm.lo cairo-xcb-surface.lo \ + cairo-xcb-surface-core.lo cairo-xcb-surface-render.lo \ + cairo-xcb-resources.lo +@CAIRO_HAS_XCB_SURFACE_TRUE@am__objects_37 = $(am__objects_36) +am__objects_38 = cairo-quartz-surface.lo +@CAIRO_HAS_QUARTZ_SURFACE_TRUE@am__objects_39 = $(am__objects_38) +am__objects_40 = cairo-quartz-font.lo +@CAIRO_HAS_QUARTZ_FONT_TRUE@am__objects_41 = $(am__objects_40) +am__objects_42 = cairo-quartz-image-surface.lo +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@am__objects_43 = \ +@CAIRO_HAS_QUARTZ_IMAGE_SURFACE_TRUE@ $(am__objects_42) +am__objects_44 = cairo-win32-debug.lo cairo-win32-device.lo \ + cairo-win32-gdi-compositor.lo cairo-win32-system.lo \ + cairo-win32-surface.lo cairo-win32-display-surface.lo \ + cairo-win32-printing-surface.lo +@CAIRO_HAS_WIN32_SURFACE_TRUE@am__objects_45 = $(am__objects_44) +am__objects_46 = cairo-win32-font.lo +@CAIRO_HAS_WIN32_FONT_TRUE@am__objects_47 = $(am__objects_46) +am__objects_48 = cairo-os2-surface.lo +@CAIRO_HAS_OS2_SURFACE_TRUE@am__objects_49 = $(am__objects_48) +am__objects_50 = cairo-drm.lo cairo-drm-bo.lo cairo-drm-surface.lo \ + cairo-drm-intel.lo cairo-drm-intel-debug.lo \ + cairo-drm-intel-surface.lo cairo-drm-i915-surface.lo \ + cairo-drm-i915-glyphs.lo cairo-drm-i915-shader.lo \ + cairo-drm-i915-spans.lo cairo-drm-i965-surface.lo \ + cairo-drm-i965-glyphs.lo cairo-drm-i965-shader.lo \ + cairo-drm-i965-spans.lo cairo-drm-intel-brw-eu.lo \ + cairo-drm-intel-brw-eu-emit.lo cairo-drm-intel-brw-eu-util.lo \ + cairo-drm-radeon.lo cairo-drm-radeon-surface.lo +@CAIRO_HAS_DRM_SURFACE_TRUE@am__objects_51 = $(am__objects_50) +am__objects_52 = cairo-drm-gallium-surface.lo +@CAIRO_HAS_GALLIUM_SURFACE_TRUE@am__objects_53 = $(am__objects_52) +am__objects_54 = cairo-png.lo +@CAIRO_HAS_PNG_FUNCTIONS_TRUE@am__objects_55 = $(am__objects_54) +am__objects_56 = cairo-gl-composite.lo cairo-gl-device.lo \ + cairo-gl-dispatch.lo cairo-gl-glyphs.lo cairo-gl-gradient.lo \ + cairo-gl-info.lo cairo-gl-msaa-compositor.lo \ + cairo-gl-operand.lo cairo-gl-shaders.lo cairo-gl-source.lo \ + cairo-gl-spans-compositor.lo cairo-gl-surface.lo \ + cairo-gl-traps-compositor.lo +@CAIRO_HAS_GL_SURFACE_TRUE@am__objects_57 = $(am__objects_56) +am__objects_58 = $(am__objects_56) +@CAIRO_HAS_GLESV2_SURFACE_TRUE@am__objects_59 = $(am__objects_58) +@CAIRO_HAS_GLESV3_SURFACE_TRUE@am__objects_60 = $(am__objects_58) +am__objects_61 = cairo-cogl-surface.lo cairo-cogl-gradient.lo +@CAIRO_HAS_COGL_SURFACE_TRUE@am__objects_62 = $(am__objects_61) +am__objects_63 = cairo-directfb-surface.lo +@CAIRO_HAS_DIRECTFB_SURFACE_TRUE@am__objects_64 = $(am__objects_63) +am__objects_65 = cairo-vg-surface.lo +@CAIRO_HAS_VG_SURFACE_TRUE@am__objects_66 = $(am__objects_65) +am__objects_67 = cairo-egl-context.lo +@CAIRO_HAS_EGL_FUNCTIONS_TRUE@am__objects_68 = $(am__objects_67) +am__objects_69 = cairo-glx-context.lo +@CAIRO_HAS_GLX_FUNCTIONS_TRUE@am__objects_70 = $(am__objects_69) +am__objects_71 = cairo-wgl-context.lo +@CAIRO_HAS_WGL_FUNCTIONS_TRUE@am__objects_72 = $(am__objects_71) +am__objects_73 = cairo-script-surface.lo +@CAIRO_HAS_SCRIPT_SURFACE_TRUE@am__objects_74 = $(am__objects_73) +am__objects_75 = cairo-ft-font.lo +@CAIRO_HAS_FT_FONT_TRUE@am__objects_76 = $(am__objects_75) +am__objects_77 = cairo-ps-surface.lo +@CAIRO_HAS_PS_SURFACE_TRUE@am__objects_78 = $(am__objects_77) +am__objects_79 = cairo-pdf-surface.lo cairo-pdf-interchange.lo \ + cairo-tag-stack.lo +@CAIRO_HAS_PDF_SURFACE_TRUE@am__objects_80 = $(am__objects_79) +am__objects_81 = cairo-svg-surface.lo +@CAIRO_HAS_SVG_SURFACE_TRUE@am__objects_82 = $(am__objects_81) +am__objects_83 = test-compositor-surface.lo \ + test-null-compositor-surface.lo \ + test-base-compositor-surface.lo test-paginated-surface.lo +@CAIRO_HAS_TEST_SURFACES_TRUE@am__objects_84 = $(am__objects_83) +am__objects_85 = cairo-tee-surface.lo +@CAIRO_HAS_TEE_SURFACE_TRUE@am__objects_86 = $(am__objects_85) +am__objects_87 = cairo-xml-surface.lo +@CAIRO_HAS_XML_SURFACE_TRUE@am__objects_88 = $(am__objects_87) +am__objects_89 = $(am__objects_33) $(am__objects_35) $(am__objects_1) \ + $(am__objects_37) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_39) $(am__objects_41) \ + $(am__objects_43) $(am__objects_45) $(am__objects_47) \ + $(am__objects_49) $(am__objects_1) $(am__objects_51) \ + $(am__objects_53) $(am__objects_55) $(am__objects_57) \ + $(am__objects_59) $(am__objects_60) $(am__objects_62) \ + $(am__objects_64) $(am__objects_66) $(am__objects_68) \ + $(am__objects_70) $(am__objects_72) $(am__objects_74) \ + $(am__objects_76) $(am__objects_1) $(am__objects_78) \ + $(am__objects_80) $(am__objects_82) $(am__objects_84) \ + $(am__objects_86) $(am__objects_88) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) +am_libcairo_la_OBJECTS = $(am__objects_26) $(am__objects_29) \ + $(am__objects_89) +nodist_libcairo_la_OBJECTS = +libcairo_la_OBJECTS = $(am_libcairo_la_OBJECTS) \ + $(nodist_libcairo_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libcairo_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libcairo_la_LDFLAGS) $(LDFLAGS) -o $@ +am__libcairo_cxx_la_SOURCES_DIST = cairo.h cairo-version.h \ + cairo-deprecated.h cairo-xlib.h cairo-xlib-xrender.h \ + cairo-xcb.h cairo-qt.h cairo-quartz.h cairo-quartz-image.h \ + cairo-win32.h cairo-os2.h cairo-beos.h cairo-drm.h cairo-gl.h \ + cairo-cogl.h cairo-directfb.h cairo-vg.h cairo-script.h \ + cairo-ft.h cairo-ps.h cairo-pdf.h cairo-svg.h cairo-tee.h \ + cairo-xml.h 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 \ + cairo-scaled-font-subsets-private.h \ + cairo-truetype-subset-private.h cairo-type1-private.h \ + cairo-type3-glyph-surface-private.h \ + cairo-pdf-operators-private.h cairo-pdf-shading-private.h \ + cairo-tag-attributes-private.h cairo-xlib-private.h \ + cairo-xlib-surface-private.h cairo-xlib-xrender-private.h \ + cairo-xcb-private.h cairo-quartz-private.h \ + win32/cairo-win32-private.h cairo-os2-private.h \ + drm/cairo-drm-private.h drm/cairo-drm-intel-private.h \ + drm/cairo-drm-intel-brw-defines.h \ + drm/cairo-drm-intel-brw-structs.h drm/cairo-drm-intel-brw-eu.h \ + drm/cairo-drm-intel-command-private.h \ + drm/cairo-drm-intel-ioctl-private.h \ + drm/cairo-drm-i915-private.h drm/cairo-drm-i965-private.h \ + drm/cairo-drm-radeon-private.h cairo-gl-private.h \ + cairo-gl-dispatch-private.h cairo-gl-ext-def-private.h \ + cairo-gl-gradient-private.h cairo-cogl-private.h \ + cairo-cogl-gradient-private.h cairo-script-private.h \ + cairo-ft-private.h cairo-ps-surface-private.h \ + cairo-pdf-surface-private.h cairo-tag-stack-private.h \ + cairo-svg-surface-private.h test-compositor-surface.h \ + test-compositor-surface-private.h \ + test-null-compositor-surface.h test-paginated-surface.h \ + cairo-tee-surface-private.h cairo-qt-surface.cpp \ + cairo-beos-surface.cpp +am__objects_90 = cairo-qt-surface.lo +@CAIRO_HAS_QT_SURFACE_TRUE@am__objects_91 = $(am__objects_90) +am__objects_92 = cairo-beos-surface.lo +@CAIRO_HAS_BEOS_SURFACE_TRUE@am__objects_93 = $(am__objects_92) +am__objects_94 = $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_91) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_93) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) \ + $(am__objects_1) $(am__objects_1) $(am__objects_1) +am_libcairo_cxx_la_OBJECTS = $(am__objects_26) $(am__objects_29) \ + $(am__objects_94) +libcairo_cxx_la_OBJECTS = $(am_libcairo_cxx_la_OBJECTS) +libcairo_cxx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libcairo_cxx_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_CXX_TRUE@am_libcairo_cxx_la_rpath = +check_link_SOURCES = check-link.c +check_link_OBJECTS = check-link.$(OBJEXT) +check_link_DEPENDENCIES = libcairo.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/cairo-analysis-surface.Plo \ + ./$(DEPDIR)/cairo-arc.Plo ./$(DEPDIR)/cairo-array.Plo \ + ./$(DEPDIR)/cairo-atomic.Plo \ + ./$(DEPDIR)/cairo-base64-stream.Plo \ + ./$(DEPDIR)/cairo-base85-stream.Plo \ + ./$(DEPDIR)/cairo-bentley-ottmann-rectangular.Plo \ + ./$(DEPDIR)/cairo-bentley-ottmann-rectilinear.Plo \ + ./$(DEPDIR)/cairo-bentley-ottmann.Plo \ + ./$(DEPDIR)/cairo-beos-surface.Plo \ + ./$(DEPDIR)/cairo-botor-scan-converter.Plo \ + ./$(DEPDIR)/cairo-boxes-intersect.Plo \ + ./$(DEPDIR)/cairo-boxes.Plo ./$(DEPDIR)/cairo-cache.Plo \ + ./$(DEPDIR)/cairo-cff-subset.Plo \ + ./$(DEPDIR)/cairo-clip-boxes.Plo \ + ./$(DEPDIR)/cairo-clip-polygon.Plo \ + ./$(DEPDIR)/cairo-clip-region.Plo \ + ./$(DEPDIR)/cairo-clip-surface.Plo \ + ./$(DEPDIR)/cairo-clip-tor-scan-converter.Plo \ + ./$(DEPDIR)/cairo-clip.Plo ./$(DEPDIR)/cairo-cogl-gradient.Plo \ + ./$(DEPDIR)/cairo-cogl-surface.Plo ./$(DEPDIR)/cairo-color.Plo \ + ./$(DEPDIR)/cairo-composite-rectangles.Plo \ + ./$(DEPDIR)/cairo-compositor.Plo ./$(DEPDIR)/cairo-contour.Plo \ + ./$(DEPDIR)/cairo-damage.Plo ./$(DEPDIR)/cairo-debug.Plo \ + ./$(DEPDIR)/cairo-default-context.Plo \ + ./$(DEPDIR)/cairo-deflate-stream.Plo \ + ./$(DEPDIR)/cairo-device.Plo \ + ./$(DEPDIR)/cairo-directfb-surface.Plo \ + ./$(DEPDIR)/cairo-drm-bo.Plo \ + ./$(DEPDIR)/cairo-drm-gallium-surface.Plo \ + ./$(DEPDIR)/cairo-drm-i915-glyphs.Plo \ + ./$(DEPDIR)/cairo-drm-i915-shader.Plo \ + ./$(DEPDIR)/cairo-drm-i915-spans.Plo \ + ./$(DEPDIR)/cairo-drm-i915-surface.Plo \ + ./$(DEPDIR)/cairo-drm-i965-glyphs.Plo \ + ./$(DEPDIR)/cairo-drm-i965-shader.Plo \ + ./$(DEPDIR)/cairo-drm-i965-spans.Plo \ + ./$(DEPDIR)/cairo-drm-i965-surface.Plo \ + ./$(DEPDIR)/cairo-drm-intel-brw-eu-emit.Plo \ + ./$(DEPDIR)/cairo-drm-intel-brw-eu-util.Plo \ + ./$(DEPDIR)/cairo-drm-intel-brw-eu.Plo \ + ./$(DEPDIR)/cairo-drm-intel-debug.Plo \ + ./$(DEPDIR)/cairo-drm-intel-surface.Plo \ + ./$(DEPDIR)/cairo-drm-intel.Plo \ + ./$(DEPDIR)/cairo-drm-radeon-surface.Plo \ + ./$(DEPDIR)/cairo-drm-radeon.Plo \ + ./$(DEPDIR)/cairo-drm-surface.Plo ./$(DEPDIR)/cairo-drm.Plo \ + ./$(DEPDIR)/cairo-egl-context.Plo ./$(DEPDIR)/cairo-error.Plo \ + ./$(DEPDIR)/cairo-fallback-compositor.Plo \ + ./$(DEPDIR)/cairo-fixed.Plo \ + ./$(DEPDIR)/cairo-font-face-twin-data.Plo \ + ./$(DEPDIR)/cairo-font-face-twin.Plo \ + ./$(DEPDIR)/cairo-font-face.Plo \ + ./$(DEPDIR)/cairo-font-options.Plo \ + ./$(DEPDIR)/cairo-freed-pool.Plo \ + ./$(DEPDIR)/cairo-freelist.Plo ./$(DEPDIR)/cairo-ft-font.Plo \ + ./$(DEPDIR)/cairo-gl-composite.Plo \ + ./$(DEPDIR)/cairo-gl-device.Plo \ + ./$(DEPDIR)/cairo-gl-dispatch.Plo \ + ./$(DEPDIR)/cairo-gl-glyphs.Plo \ + ./$(DEPDIR)/cairo-gl-gradient.Plo \ + ./$(DEPDIR)/cairo-gl-info.Plo \ + ./$(DEPDIR)/cairo-gl-msaa-compositor.Plo \ + ./$(DEPDIR)/cairo-gl-operand.Plo \ + ./$(DEPDIR)/cairo-gl-shaders.Plo \ + ./$(DEPDIR)/cairo-gl-source.Plo \ + ./$(DEPDIR)/cairo-gl-spans-compositor.Plo \ + ./$(DEPDIR)/cairo-gl-surface.Plo \ + ./$(DEPDIR)/cairo-gl-traps-compositor.Plo \ + ./$(DEPDIR)/cairo-glx-context.Plo ./$(DEPDIR)/cairo-gstate.Plo \ + ./$(DEPDIR)/cairo-hash.Plo ./$(DEPDIR)/cairo-hull.Plo \ + ./$(DEPDIR)/cairo-image-compositor.Plo \ + ./$(DEPDIR)/cairo-image-info.Plo \ + ./$(DEPDIR)/cairo-image-source.Plo \ + ./$(DEPDIR)/cairo-image-surface.Plo ./$(DEPDIR)/cairo-line.Plo \ + ./$(DEPDIR)/cairo-lzw.Plo \ + ./$(DEPDIR)/cairo-mask-compositor.Plo \ + ./$(DEPDIR)/cairo-matrix.Plo ./$(DEPDIR)/cairo-mempool.Plo \ + ./$(DEPDIR)/cairo-mesh-pattern-rasterizer.Plo \ + ./$(DEPDIR)/cairo-misc.Plo \ + ./$(DEPDIR)/cairo-mono-scan-converter.Plo \ + ./$(DEPDIR)/cairo-mutex.Plo \ + ./$(DEPDIR)/cairo-no-compositor.Plo \ + ./$(DEPDIR)/cairo-observer.Plo \ + ./$(DEPDIR)/cairo-os2-surface.Plo \ + ./$(DEPDIR)/cairo-output-stream.Plo \ + ./$(DEPDIR)/cairo-paginated-surface.Plo \ + ./$(DEPDIR)/cairo-path-bounds.Plo \ + ./$(DEPDIR)/cairo-path-fill.Plo \ + ./$(DEPDIR)/cairo-path-fixed.Plo \ + ./$(DEPDIR)/cairo-path-in-fill.Plo \ + ./$(DEPDIR)/cairo-path-stroke-boxes.Plo \ + ./$(DEPDIR)/cairo-path-stroke-polygon.Plo \ + ./$(DEPDIR)/cairo-path-stroke-traps.Plo \ + ./$(DEPDIR)/cairo-path-stroke-tristrip.Plo \ + ./$(DEPDIR)/cairo-path-stroke.Plo ./$(DEPDIR)/cairo-path.Plo \ + ./$(DEPDIR)/cairo-pattern.Plo \ + ./$(DEPDIR)/cairo-pdf-interchange.Plo \ + ./$(DEPDIR)/cairo-pdf-operators.Plo \ + ./$(DEPDIR)/cairo-pdf-shading.Plo \ + ./$(DEPDIR)/cairo-pdf-surface.Plo ./$(DEPDIR)/cairo-pen.Plo \ + ./$(DEPDIR)/cairo-png.Plo \ + ./$(DEPDIR)/cairo-polygon-intersect.Plo \ + ./$(DEPDIR)/cairo-polygon-reduce.Plo \ + ./$(DEPDIR)/cairo-polygon.Plo ./$(DEPDIR)/cairo-ps-surface.Plo \ + ./$(DEPDIR)/cairo-qt-surface.Plo \ + ./$(DEPDIR)/cairo-quartz-font.Plo \ + ./$(DEPDIR)/cairo-quartz-image-surface.Plo \ + ./$(DEPDIR)/cairo-quartz-surface.Plo \ + ./$(DEPDIR)/cairo-raster-source-pattern.Plo \ + ./$(DEPDIR)/cairo-recording-surface.Plo \ + ./$(DEPDIR)/cairo-rectangle.Plo \ + ./$(DEPDIR)/cairo-rectangular-scan-converter.Plo \ + ./$(DEPDIR)/cairo-region.Plo ./$(DEPDIR)/cairo-rtree.Plo \ + ./$(DEPDIR)/cairo-scaled-font-subsets.Plo \ + ./$(DEPDIR)/cairo-scaled-font.Plo \ + ./$(DEPDIR)/cairo-script-surface.Plo \ + ./$(DEPDIR)/cairo-shape-mask-compositor.Plo \ + ./$(DEPDIR)/cairo-slope.Plo \ + ./$(DEPDIR)/cairo-spans-compositor.Plo \ + ./$(DEPDIR)/cairo-spans.Plo ./$(DEPDIR)/cairo-spline.Plo \ + ./$(DEPDIR)/cairo-stroke-dash.Plo \ + ./$(DEPDIR)/cairo-stroke-style.Plo \ + ./$(DEPDIR)/cairo-surface-clipper.Plo \ + ./$(DEPDIR)/cairo-surface-fallback.Plo \ + ./$(DEPDIR)/cairo-surface-observer.Plo \ + ./$(DEPDIR)/cairo-surface-offset.Plo \ + ./$(DEPDIR)/cairo-surface-snapshot.Plo \ + ./$(DEPDIR)/cairo-surface-subsurface.Plo \ + ./$(DEPDIR)/cairo-surface-wrapper.Plo \ + ./$(DEPDIR)/cairo-surface.Plo \ + ./$(DEPDIR)/cairo-svg-surface.Plo \ + ./$(DEPDIR)/cairo-tag-attributes.Plo \ + ./$(DEPDIR)/cairo-tag-stack.Plo \ + ./$(DEPDIR)/cairo-tee-surface.Plo ./$(DEPDIR)/cairo-time.Plo \ + ./$(DEPDIR)/cairo-tor-scan-converter.Plo \ + ./$(DEPDIR)/cairo-tor22-scan-converter.Plo \ + ./$(DEPDIR)/cairo-toy-font-face.Plo \ + ./$(DEPDIR)/cairo-traps-compositor.Plo \ + ./$(DEPDIR)/cairo-traps.Plo ./$(DEPDIR)/cairo-tristrip.Plo \ + ./$(DEPDIR)/cairo-truetype-subset.Plo \ + ./$(DEPDIR)/cairo-type1-fallback.Plo \ + ./$(DEPDIR)/cairo-type1-glyph-names.Plo \ + ./$(DEPDIR)/cairo-type1-subset.Plo \ + ./$(DEPDIR)/cairo-type3-glyph-surface.Plo \ + ./$(DEPDIR)/cairo-unicode.Plo ./$(DEPDIR)/cairo-user-font.Plo \ + ./$(DEPDIR)/cairo-version.Plo ./$(DEPDIR)/cairo-vg-surface.Plo \ + ./$(DEPDIR)/cairo-wgl-context.Plo \ + ./$(DEPDIR)/cairo-wideint.Plo \ + ./$(DEPDIR)/cairo-win32-debug.Plo \ + ./$(DEPDIR)/cairo-win32-device.Plo \ + ./$(DEPDIR)/cairo-win32-display-surface.Plo \ + ./$(DEPDIR)/cairo-win32-font.Plo \ + ./$(DEPDIR)/cairo-win32-gdi-compositor.Plo \ + ./$(DEPDIR)/cairo-win32-printing-surface.Plo \ + ./$(DEPDIR)/cairo-win32-surface.Plo \ + ./$(DEPDIR)/cairo-win32-system.Plo \ + ./$(DEPDIR)/cairo-xcb-connection-core.Plo \ + ./$(DEPDIR)/cairo-xcb-connection-render.Plo \ + ./$(DEPDIR)/cairo-xcb-connection-shm.Plo \ + ./$(DEPDIR)/cairo-xcb-connection.Plo \ + ./$(DEPDIR)/cairo-xcb-resources.Plo \ + ./$(DEPDIR)/cairo-xcb-screen.Plo ./$(DEPDIR)/cairo-xcb-shm.Plo \ + ./$(DEPDIR)/cairo-xcb-surface-core.Plo \ + ./$(DEPDIR)/cairo-xcb-surface-render.Plo \ + ./$(DEPDIR)/cairo-xcb-surface.Plo \ + ./$(DEPDIR)/cairo-xlib-core-compositor.Plo \ + ./$(DEPDIR)/cairo-xlib-display.Plo \ + ./$(DEPDIR)/cairo-xlib-fallback-compositor.Plo \ + ./$(DEPDIR)/cairo-xlib-render-compositor.Plo \ + ./$(DEPDIR)/cairo-xlib-screen.Plo \ + ./$(DEPDIR)/cairo-xlib-source.Plo \ + ./$(DEPDIR)/cairo-xlib-surface-shm.Plo \ + ./$(DEPDIR)/cairo-xlib-surface.Plo \ + ./$(DEPDIR)/cairo-xlib-visual.Plo \ + ./$(DEPDIR)/cairo-xlib-xcb-surface.Plo \ + ./$(DEPDIR)/cairo-xml-surface.Plo ./$(DEPDIR)/cairo.Plo \ + ./$(DEPDIR)/check-link.Po \ + ./$(DEPDIR)/test-base-compositor-surface.Plo \ + ./$(DEPDIR)/test-compositor-surface.Plo \ + ./$(DEPDIR)/test-null-compositor-surface.Plo \ + ./$(DEPDIR)/test-paginated-surface.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libcairo_la_SOURCES) $(nodist_libcairo_la_SOURCES) \ + $(libcairo_cxx_la_SOURCES) check-link.c +DIST_SOURCES = $(am__libcairo_la_SOURCES_DIST) \ + $(am__libcairo_cxx_la_SOURCES_DIST) check-link.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +am__cairoinclude_HEADERS_DIST = cairo.h cairo-version.h \ + cairo-deprecated.h cairo-xlib.h cairo-xlib-xrender.h \ + cairo-xcb.h cairo-qt.h cairo-quartz.h cairo-quartz-image.h \ + cairo-win32.h cairo-os2.h cairo-beos.h cairo-drm.h cairo-gl.h \ + cairo-cogl.h cairo-directfb.h cairo-vg.h cairo-script.h \ + cairo-ft.h cairo-ps.h cairo-pdf.h cairo-svg.h cairo-tee.h \ + cairo-xml.h +HEADERS = $(cairoinclude_HEADERS) $(nodist_cairoinclude_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +am__EXEEXT_1 = check-def.sh check-doc-syntax.sh check-headers.sh \ + check-plt.sh check-preprocessor-syntax.sh +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/build/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.am.analysis \ + $(srcdir)/Makefile.am.features $(srcdir)/Makefile.in \ + $(srcdir)/cairo-features.pc.in $(srcdir)/cairo.pc.in \ + $(top_srcdir)/build/Makefile.am.common \ + $(top_srcdir)/build/depcomp $(top_srcdir)/build/test-driver \ + $(top_srcdir)/src/Makefile.sources README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BFD_LIBS = @BFD_LIBS@ +CAIROBOILERPLATE_LIBS = @CAIROBOILERPLATE_LIBS@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LDFLAGS = @CAIRO_LDFLAGS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CAIRO_LIBTOOL_VERSION_INFO = @CAIRO_LIBTOOL_VERSION_INFO@ +CAIRO_NONPKGCONFIG_CFLAGS = @CAIRO_NONPKGCONFIG_CFLAGS@ +CAIRO_NONPKGCONFIG_LIBS = @CAIRO_NONPKGCONFIG_LIBS@ +CAIRO_RELEASE_STATUS = @CAIRO_RELEASE_STATUS@ +CAIRO_REQUIRES = @CAIRO_REQUIRES@ +CAIRO_TEST_MODE = @CAIRO_TEST_MODE@ +CAIRO_TEST_UNDEFINED_LDFLAGS = @CAIRO_TEST_UNDEFINED_LDFLAGS@ +CAIRO_VERSION_MAJOR = @CAIRO_VERSION_MAJOR@ +CAIRO_VERSION_MICRO = @CAIRO_VERSION_MICRO@ +CAIRO_VERSION_MINOR = @CAIRO_VERSION_MINOR@ +CAIRO_VERSION_SONUM = @CAIRO_VERSION_SONUM@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FIND = @FIND@ +FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@ +FONTCONFIG_LIBS = @FONTCONFIG_LIBS@ +FREETYPE_CFLAGS = @FREETYPE_CFLAGS@ +FREETYPE_CONFIG = @FREETYPE_CONFIG@ +FREETYPE_LIBS = @FREETYPE_LIBS@ +GOBJECT_CFLAGS = @GOBJECT_CFLAGS@ +GOBJECT_LIBS = @GOBJECT_LIBS@ +GREP = @GREP@ +GS = @GS@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBRSVG_CFLAGS = @LIBRSVG_CFLAGS@ +LIBRSVG_LIBS = @LIBRSVG_LIBS@ +LIBS = @LIBS@ +LIBSPECTRE_CFLAGS = @LIBSPECTRE_CFLAGS@ +LIBSPECTRE_LIBS = @LIBSPECTRE_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LTP = @LTP@ +LTP_GENHTML = @LTP_GENHTML@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKGCONFIG_REQUIRES = @PKGCONFIG_REQUIRES@ +PKG_CONFIG = @PKG_CONFIG@ +POPPLER_CFLAGS = @POPPLER_CFLAGS@ +POPPLER_LIBS = @POPPLER_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHLIB_EXT = @SHLIB_EXT@ +SHM_LIBS = @SHM_LIBS@ +SHTOOL = @SHTOOL@ +STRINGS = @STRINGS@ +STRIP = @STRIP@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VERSION = @VERSION@ +XARGS = @XARGS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cogl_CFLAGS = @cogl_CFLAGS@ +cogl_LIBS = @cogl_LIBS@ +datadir = @datadir@ +datarootdir = @datarootdir@ +directfb_CFLAGS = @directfb_CFLAGS@ +directfb_LIBS = @directfb_LIBS@ +docdir = @docdir@ +drm_CFLAGS = @drm_CFLAGS@ +drm_LIBS = @drm_LIBS@ +dvidir = @dvidir@ +egl_CFLAGS = @egl_CFLAGS@ +egl_LIBS = @egl_LIBS@ +exec_prefix = @exec_prefix@ +gallium_DIR = @gallium_DIR@ +gl_CFLAGS = @gl_CFLAGS@ +gl_LIBS = @gl_LIBS@ +glesv2_CFLAGS = @glesv2_CFLAGS@ +glesv2_LIBS = @glesv2_LIBS@ +glesv3_CFLAGS = @glesv3_CFLAGS@ +glesv3_LIBS = @glesv3_LIBS@ +glib_CFLAGS = @glib_CFLAGS@ +glib_LIBS = @glib_LIBS@ +gtk_CFLAGS = @gtk_CFLAGS@ +gtk_LIBS = @gtk_LIBS@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lzo_LIBS = @lzo_LIBS@ +mandir = @mandir@ +mesa_DIR = @mesa_DIR@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pixman_CFLAGS = @pixman_CFLAGS@ +pixman_LIBS = @pixman_LIBS@ +png_CFLAGS = @png_CFLAGS@ +png_LIBS = @png_LIBS@ +png_REQUIRES = @png_REQUIRES@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pthread_CFLAGS = @pthread_CFLAGS@ +pthread_LIBS = @pthread_LIBS@ +qt_CFLAGS = @qt_CFLAGS@ +qt_LIBS = @qt_LIBS@ +real_pthread_CFLAGS = @real_pthread_CFLAGS@ +real_pthread_LIBS = @real_pthread_LIBS@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +shm_LIBS = @shm_LIBS@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +xcb_CFLAGS = @xcb_CFLAGS@ +xcb_LIBS = @xcb_LIBS@ +xcb_shm_CFLAGS = @xcb_shm_CFLAGS@ +xcb_shm_LIBS = @xcb_shm_LIBS@ +xlib_CFLAGS = @xlib_CFLAGS@ +xlib_LIBS = @xlib_LIBS@ +xlib_xcb_CFLAGS = @xlib_xcb_CFLAGS@ +xlib_xcb_LIBS = @xlib_xcb_LIBS@ +xlib_xrender_CFLAGS = @xlib_xrender_CFLAGS@ +xlib_xrender_LIBS = @xlib_xrender_LIBS@ +BUILT_SOURCES = cairo-features.h cairo-supported-features.h +CLEANFILES = *.i *.s *.gch $(EXTRA_LTLIBRARIES) $(EXTRA_PROGRAMS) \ + $(check_PROGRAMS) cairo.def headers-standalone +DISTCLEANFILES = $(BUILT_SOURCES) cairo-features.h \ + cairo-supported-features.h +EXTRA_DIST = Makefile.win32 Makefile.win32.features $(TESTS_SH) \ + check-has-hidden-symbols.c check-doc-syntax.awk +EXTRA_LTLIBRARIES = +MAINTAINERCLEANFILES = Makefile.in +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_font_subset_private) $(_cairo_pdf_operators_private) +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_sources) $(_cairo_pdf_operators_sources) \ + $(_cairo_deflate_stream_sources) +_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_egl_sources = cairo-egl-context.c +cairo_glx_sources = cairo-glx-context.c +cairo_wgl_sources = cairo-wgl-context.c +_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_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_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_qt_headers = cairo-qt.h +cairo_qt_cxx_sources = cairo-qt-surface.cpp +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_os2_headers = cairo-os2.h +cairo_os2_private = cairo-os2-private.h +cairo_os2_sources = cairo-os2-surface.c + +# automake is stupid enough to always use c++ linker if we enable the +# following lines, even if beos surface is not enabled. Disable it for now. +cairo_beos_headers = cairo-beos.h +cairo_beos_cxx_sources = cairo-beos-surface.cpp +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_directfb_headers = cairo-directfb.h +cairo_directfb_sources = cairo-directfb-surface.c +cairo_drm_headers = cairo-drm.h +cairo_drm_private = drm/cairo-drm-private.h \ + drm/cairo-drm-intel-private.h \ + drm/cairo-drm-intel-brw-defines.h \ + drm/cairo-drm-intel-brw-structs.h \ + drm/cairo-drm-intel-brw-eu.h \ + drm/cairo-drm-intel-command-private.h \ + drm/cairo-drm-intel-ioctl-private.h \ + drm/cairo-drm-i915-private.h \ + drm/cairo-drm-i965-private.h \ + drm/cairo-drm-radeon-private.h + +cairo_drm_sources = drm/cairo-drm.c \ + drm/cairo-drm-bo.c \ + drm/cairo-drm-surface.c \ + drm/cairo-drm-intel.c \ + drm/cairo-drm-intel-debug.c \ + drm/cairo-drm-intel-surface.c \ + drm/cairo-drm-i915-surface.c \ + drm/cairo-drm-i915-glyphs.c \ + drm/cairo-drm-i915-shader.c \ + drm/cairo-drm-i915-spans.c \ + drm/cairo-drm-i965-surface.c \ + drm/cairo-drm-i965-glyphs.c \ + drm/cairo-drm-i965-shader.c \ + drm/cairo-drm-i965-spans.c \ + drm/cairo-drm-intel-brw-eu.c \ + drm/cairo-drm-intel-brw-eu-emit.c \ + drm/cairo-drm-intel-brw-eu-util.c \ + drm/cairo-drm-radeon.c \ + drm/cairo-drm-radeon-surface.c + +cairo_gallium_sources = drm/cairo-drm-gallium-surface.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 +cairo_vg_headers = cairo-vg.h +cairo_vg_sources = cairo-vg-surface.c +cairo_cogl_headers = cairo-cogl.h +cairo_cogl_private = cairo-cogl-private.h \ + cairo-cogl-gradient-private.h + +cairo_cogl_sources = cairo-cogl-surface.c \ + cairo-cogl-gradient.c + +supported_cairo_headers = $(cairo_headers) $(cairo_xlib_headers) \ + $(cairo_xlib_xrender_headers) $(cairo_xcb_headers) \ + $(cairo_xcb_shm_headers) $(cairo_quartz_headers) \ + $(cairo_quartz_font_headers) $(cairo_win32_headers) \ + $(cairo_win32_font_headers) $(cairo_png_headers) \ + $(cairo_egl_headers) $(cairo_glx_headers) $(cairo_wgl_headers) \ + $(cairo_script_headers) $(cairo_ft_headers) \ + $(cairo_fc_headers) $(cairo_ps_headers) $(cairo_pdf_headers) \ + $(cairo_svg_headers) $(cairo_image_headers) \ + $(cairo_mime_headers) $(cairo_recording_headers) \ + $(cairo_observer_headers) $(cairo_user_headers) \ + $(cairo_gobject_headers) +unsupported_cairo_headers = $(cairo_xlib_xcb_headers) \ + $(cairo_qt_headers) $(cairo_quartz_image_headers) \ + $(cairo_os2_headers) $(cairo_beos_headers) \ + $(cairo_drm_headers) $(cairo_gallium_headers) \ + $(cairo_gl_headers) $(cairo_glesv2_headers) \ + $(cairo_glesv3_headers) $(cairo_cogl_headers) \ + $(cairo_directfb_headers) $(cairo_vg_headers) \ + $(cairo_tee_headers) $(cairo_xml_headers) +all_cairo_headers = $(cairo_headers) $(cairo_xlib_headers) \ + $(cairo_xlib_xrender_headers) $(cairo_xcb_headers) \ + $(cairo_xlib_xcb_headers) $(cairo_xcb_shm_headers) \ + $(cairo_qt_headers) $(cairo_quartz_headers) \ + $(cairo_quartz_font_headers) $(cairo_quartz_image_headers) \ + $(cairo_win32_headers) $(cairo_win32_font_headers) \ + $(cairo_os2_headers) $(cairo_beos_headers) \ + $(cairo_drm_headers) $(cairo_gallium_headers) \ + $(cairo_png_headers) $(cairo_gl_headers) \ + $(cairo_glesv2_headers) $(cairo_glesv3_headers) \ + $(cairo_cogl_headers) $(cairo_directfb_headers) \ + $(cairo_vg_headers) $(cairo_egl_headers) $(cairo_glx_headers) \ + $(cairo_wgl_headers) $(cairo_script_headers) \ + $(cairo_ft_headers) $(cairo_fc_headers) $(cairo_ps_headers) \ + $(cairo_pdf_headers) $(cairo_svg_headers) \ + $(cairo_image_headers) $(cairo_mime_headers) \ + $(cairo_recording_headers) $(cairo_observer_headers) \ + $(cairo_tee_headers) $(cairo_xml_headers) \ + $(cairo_user_headers) $(cairo_gobject_headers) +all_cairo_private = $(cairo_private) $(cairo_xlib_private) \ + $(cairo_xlib_xrender_private) $(cairo_xcb_private) \ + $(cairo_xlib_xcb_private) $(cairo_xcb_shm_private) \ + $(cairo_qt_private) $(cairo_quartz_private) \ + $(cairo_quartz_font_private) $(cairo_quartz_image_private) \ + $(cairo_win32_private) $(cairo_win32_font_private) \ + $(cairo_os2_private) $(cairo_beos_private) \ + $(cairo_drm_private) $(cairo_gallium_private) \ + $(cairo_png_private) $(cairo_gl_private) \ + $(cairo_glesv2_private) $(cairo_glesv3_private) \ + $(cairo_cogl_private) $(cairo_directfb_private) \ + $(cairo_vg_private) $(cairo_egl_private) $(cairo_glx_private) \ + $(cairo_wgl_private) $(cairo_script_private) \ + $(cairo_ft_private) $(cairo_fc_private) $(cairo_ps_private) \ + $(cairo_pdf_private) $(cairo_svg_private) \ + $(cairo_test_surfaces_private) $(cairo_test_surfaces_headers) \ + $(cairo_image_private) $(cairo_mime_private) \ + $(cairo_recording_private) $(cairo_observer_private) \ + $(cairo_tee_private) $(cairo_xml_private) \ + $(cairo_user_private) $(cairo_pthread_private) \ + $(cairo_pthread_headers) $(cairo_gobject_private) \ + $(cairo_trace_private) $(cairo_trace_headers) \ + $(cairo_interpreter_private) $(cairo_interpreter_headers) \ + $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) +all_cairo_cxx_sources = $(cairo_cxx_sources) $(cairo_xlib_cxx_sources) \ + $(cairo_xlib_xrender_cxx_sources) $(cairo_xcb_cxx_sources) \ + $(cairo_xlib_xcb_cxx_sources) $(cairo_xcb_shm_cxx_sources) \ + $(cairo_qt_cxx_sources) $(cairo_quartz_cxx_sources) \ + $(cairo_quartz_font_cxx_sources) \ + $(cairo_quartz_image_cxx_sources) $(cairo_win32_cxx_sources) \ + $(cairo_win32_font_cxx_sources) $(cairo_os2_cxx_sources) \ + $(cairo_beos_cxx_sources) $(cairo_drm_cxx_sources) \ + $(cairo_gallium_cxx_sources) $(cairo_png_cxx_sources) \ + $(cairo_gl_cxx_sources) $(cairo_glesv2_cxx_sources) \ + $(cairo_glesv3_cxx_sources) $(cairo_cogl_cxx_sources) \ + $(cairo_directfb_cxx_sources) $(cairo_vg_cxx_sources) \ + $(cairo_egl_cxx_sources) $(cairo_glx_cxx_sources) \ + $(cairo_wgl_cxx_sources) $(cairo_script_cxx_sources) \ + $(cairo_ft_cxx_sources) $(cairo_fc_cxx_sources) \ + $(cairo_ps_cxx_sources) $(cairo_pdf_cxx_sources) \ + $(cairo_svg_cxx_sources) $(cairo_test_surfaces_cxx_sources) \ + $(cairo_image_cxx_sources) $(cairo_mime_cxx_sources) \ + $(cairo_recording_cxx_sources) $(cairo_observer_cxx_sources) \ + $(cairo_tee_cxx_sources) $(cairo_xml_cxx_sources) \ + $(cairo_user_cxx_sources) $(cairo_pthread_cxx_sources) \ + $(cairo_gobject_cxx_sources) $(cairo_trace_cxx_sources) \ + $(cairo_interpreter_cxx_sources) \ + $(cairo_symbol_lookup_cxx_sources) +all_cairo_sources = $(cairo_sources) $(cairo_xlib_sources) \ + $(cairo_xlib_xrender_sources) $(cairo_xcb_sources) \ + $(cairo_xlib_xcb_sources) $(cairo_xcb_shm_sources) \ + $(cairo_qt_sources) $(cairo_quartz_sources) \ + $(cairo_quartz_font_sources) $(cairo_quartz_image_sources) \ + $(cairo_win32_sources) $(cairo_win32_font_sources) \ + $(cairo_os2_sources) $(cairo_beos_sources) \ + $(cairo_drm_sources) $(cairo_gallium_sources) \ + $(cairo_png_sources) $(cairo_gl_sources) \ + $(cairo_glesv2_sources) $(cairo_glesv3_sources) \ + $(cairo_cogl_sources) $(cairo_directfb_sources) \ + $(cairo_vg_sources) $(cairo_egl_sources) $(cairo_glx_sources) \ + $(cairo_wgl_sources) $(cairo_script_sources) \ + $(cairo_ft_sources) $(cairo_fc_sources) $(cairo_ps_sources) \ + $(cairo_pdf_sources) $(cairo_svg_sources) \ + $(cairo_test_surfaces_sources) $(cairo_image_sources) \ + $(cairo_mime_sources) $(cairo_recording_sources) \ + $(cairo_observer_sources) $(cairo_tee_sources) \ + $(cairo_xml_sources) $(cairo_user_sources) \ + $(cairo_pthread_sources) $(cairo_gobject_sources) \ + $(cairo_trace_sources) $(cairo_interpreter_sources) \ + $(cairo_symbol_lookup_sources) +enabled_cairo_headers = $(cairo_headers) $(am__append_1) \ + $(am__append_6) $(am__append_11) $(am__append_16) \ + $(am__append_21) $(am__append_26) $(am__append_31) \ + $(am__append_36) $(am__append_41) $(am__append_46) \ + $(am__append_51) $(am__append_56) $(am__append_61) \ + $(am__append_66) $(am__append_71) $(am__append_76) \ + $(am__append_81) $(am__append_86) $(am__append_91) \ + $(am__append_96) $(am__append_101) $(am__append_106) \ + $(am__append_111) $(am__append_116) $(am__append_121) \ + $(am__append_126) $(am__append_131) $(am__append_136) \ + $(am__append_141) $(am__append_146) $(am__append_151) \ + $(cairo_image_headers) $(cairo_mime_headers) \ + $(cairo_recording_headers) $(cairo_observer_headers) \ + $(am__append_159) $(am__append_164) $(cairo_user_headers) \ + $(am__append_172) +enabled_cairo_private = $(cairo_private) $(am__append_2) \ + $(am__append_7) $(am__append_12) $(am__append_17) \ + $(am__append_22) $(am__append_27) $(am__append_32) \ + $(am__append_37) $(am__append_42) $(am__append_47) \ + $(am__append_52) $(am__append_57) $(am__append_62) \ + $(am__append_67) $(am__append_72) $(am__append_77) \ + $(am__append_82) $(am__append_87) $(am__append_92) \ + $(am__append_97) $(am__append_102) $(am__append_107) \ + $(am__append_112) $(am__append_117) $(am__append_122) \ + $(am__append_127) $(am__append_132) $(am__append_137) \ + $(am__append_142) $(am__append_147) $(am__append_152) \ + $(am__append_156) $(cairo_image_private) $(cairo_mime_private) \ + $(cairo_recording_private) $(cairo_observer_private) \ + $(am__append_160) $(am__append_165) $(cairo_user_private) \ + $(am__append_169) $(am__append_173) $(am__append_177) \ + $(am__append_180) $(am__append_183) +enabled_cairo_cxx_sources = $(cairo_cxx_sources) $(am__append_3) \ + $(am__append_8) $(am__append_13) $(am__append_18) \ + $(am__append_23) $(am__append_28) $(am__append_33) \ + $(am__append_38) $(am__append_43) $(am__append_48) \ + $(am__append_53) $(am__append_58) $(am__append_63) \ + $(am__append_68) $(am__append_73) $(am__append_78) \ + $(am__append_83) $(am__append_88) $(am__append_93) \ + $(am__append_98) $(am__append_103) $(am__append_108) \ + $(am__append_113) $(am__append_118) $(am__append_123) \ + $(am__append_128) $(am__append_133) $(am__append_138) \ + $(am__append_143) $(am__append_148) $(am__append_153) \ + $(am__append_157) $(cairo_image_cxx_sources) \ + $(cairo_mime_cxx_sources) $(cairo_recording_cxx_sources) \ + $(cairo_observer_cxx_sources) $(am__append_161) \ + $(am__append_166) $(cairo_user_cxx_sources) $(am__append_170) \ + $(am__append_174) $(am__append_178) $(am__append_181) \ + $(am__append_184) +enabled_cairo_sources = $(cairo_sources) $(am__append_4) \ + $(am__append_9) $(am__append_14) $(am__append_19) \ + $(am__append_24) $(am__append_29) $(am__append_34) \ + $(am__append_39) $(am__append_44) $(am__append_49) \ + $(am__append_54) $(am__append_59) $(am__append_64) \ + $(am__append_69) $(am__append_74) $(am__append_79) \ + $(am__append_84) $(am__append_89) $(am__append_94) \ + $(am__append_99) $(am__append_104) $(am__append_109) \ + $(am__append_114) $(am__append_119) $(am__append_124) \ + $(am__append_129) $(am__append_134) $(am__append_139) \ + $(am__append_144) $(am__append_149) $(am__append_154) \ + $(am__append_158) $(cairo_image_sources) $(cairo_mime_sources) \ + $(cairo_recording_sources) $(cairo_observer_sources) \ + $(am__append_162) $(am__append_167) $(cairo_user_sources) \ + $(am__append_171) $(am__append_175) $(am__append_179) \ + $(am__append_182) $(am__append_185) +all_cairo_pkgconf = cairo.pc cairo-xlib.pc cairo-xlib-xrender.pc \ + cairo-xcb.pc cairo-xlib-xcb.pc cairo-xcb-shm.pc cairo-qt.pc \ + cairo-quartz.pc cairo-quartz-font.pc cairo-quartz-image.pc \ + cairo-win32.pc cairo-win32-font.pc cairo-os2.pc cairo-beos.pc \ + cairo-drm.pc cairo-gallium.pc cairo-png.pc cairo-gl.pc \ + cairo-glesv2.pc cairo-glesv3.pc cairo-cogl.pc \ + cairo-directfb.pc cairo-vg.pc cairo-egl.pc cairo-glx.pc \ + cairo-wgl.pc cairo-script.pc cairo-ft.pc cairo-fc.pc \ + cairo-ps.pc cairo-pdf.pc cairo-svg.pc cairo-tee.pc \ + cairo-xml.pc cairo-gobject.pc +enabled_cairo_pkgconf = cairo.pc $(am__append_5) $(am__append_10) \ + $(am__append_15) $(am__append_20) $(am__append_25) \ + $(am__append_30) $(am__append_35) $(am__append_40) \ + $(am__append_45) $(am__append_50) $(am__append_55) \ + $(am__append_60) $(am__append_65) $(am__append_70) \ + $(am__append_75) $(am__append_80) $(am__append_85) \ + $(am__append_90) $(am__append_95) $(am__append_100) \ + $(am__append_105) $(am__append_110) $(am__append_115) \ + $(am__append_120) $(am__append_125) $(am__append_130) \ + $(am__append_135) $(am__append_140) $(am__append_145) \ + $(am__append_150) $(am__append_155) $(am__append_163) \ + $(am__append_168) $(am__append_176) +#MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features +AM_CPPFLAGS = -I$(srcdir) $(CAIRO_CFLAGS) +AM_LDFLAGS = $(CAIRO_LDFLAGS) +@OS_WIN32_TRUE@export_symbols = -export-symbols cairo.def +@OS_WIN32_TRUE@cairo_def_dependency = cairo.def +cairoincludedir = $(includedir)/cairo +cairoinclude_HEADERS = $(enabled_cairo_headers) +lib_LTLIBRARIES = libcairo.la +@BUILD_CXX_FALSE@cairo_cxx_lib = +@BUILD_CXX_TRUE@cairo_cxx_lib = libcairo_cxx.la +noinst_LTLIBRARIES = $(cairo_cxx_lib) +libcairo_cxx_la_SOURCES = \ + $(enabled_cairo_headers) \ + $(enabled_cairo_private) \ + $(enabled_cairo_cxx_sources) \ + $(NULL) + +libcairo_cxx_la_LDFLAGS = $(AM_LDFLAGS) $(export_symbols) +libcairo_cxx_la_LIBADD = $(CAIRO_LIBS) +libcairo_cxx_la_DEPENDENCIES = $(cairo_def_dependency) +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) \ + $(cairo_cxx_lib) + +libcairo_la_DEPENDENCIES = $(cairo_def_dependency) $(cairo_cxx_lib) + +# Special headers +nodist_cairoinclude_HEADERS = cairo-features.h +nodist_libcairo_la_SOURCES = cairo-features.h +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(enabled_cairo_pkgconf) +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) + +check_link_LDADD = libcairo.la +PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) +COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS) +SPARSE = sparse +SPLINT = splint -badflag +UNO = uno +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .i .lo .log .o .obj .s .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/Makefile.am.common $(srcdir)/Makefile.am.features $(top_srcdir)/src/Makefile.sources $(srcdir)/Makefile.am.analysis $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/build/Makefile.am.common $(srcdir)/Makefile.am.features $(top_srcdir)/src/Makefile.sources $(srcdir)/Makefile.am.analysis $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +cairo.pc: $(top_builddir)/config.status $(srcdir)/cairo.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-xlib.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-xlib-xrender.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-xcb.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-xlib-xcb.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-xcb-shm.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-qt.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-quartz.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-quartz-font.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-quartz-image.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-win32.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-win32-font.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-os2.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-beos.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-drm.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-gallium.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-png.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-gl.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-glesv2.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-glesv3.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-cogl.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-directfb.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-vg.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-egl.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-glx.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-wgl.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-script.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-ft.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-fc.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-ps.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-pdf.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-svg.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-tee.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-xml.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +cairo-gobject.pc: $(top_builddir)/config.status $(srcdir)/cairo-features.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libcairo.la: $(libcairo_la_OBJECTS) $(libcairo_la_DEPENDENCIES) $(EXTRA_libcairo_la_DEPENDENCIES) + $(AM_V_CCLD)$(libcairo_la_LINK) -rpath $(libdir) $(libcairo_la_OBJECTS) $(libcairo_la_LIBADD) $(LIBS) + +libcairo_cxx.la: $(libcairo_cxx_la_OBJECTS) $(libcairo_cxx_la_DEPENDENCIES) $(EXTRA_libcairo_cxx_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libcairo_cxx_la_LINK) $(am_libcairo_cxx_la_rpath) $(libcairo_cxx_la_OBJECTS) $(libcairo_cxx_la_LIBADD) $(LIBS) + +check-link$(EXEEXT): $(check_link_OBJECTS) $(check_link_DEPENDENCIES) $(EXTRA_check_link_DEPENDENCIES) + @rm -f check-link$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(check_link_OBJECTS) $(check_link_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-analysis-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-arc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-atomic.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-base64-stream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-base85-stream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-bentley-ottmann-rectangular.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-bentley-ottmann-rectilinear.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-bentley-ottmann.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-beos-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-botor-scan-converter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-boxes-intersect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-boxes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-cache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-cff-subset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-clip-boxes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-clip-polygon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-clip-region.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-clip-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-clip-tor-scan-converter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-clip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-cogl-gradient.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-cogl-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-color.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-composite-rectangles.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-contour.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-damage.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-default-context.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-deflate-stream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-device.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-directfb-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-bo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-gallium-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i915-glyphs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i915-shader.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i915-spans.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i915-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i965-glyphs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i965-shader.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i965-spans.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-i965-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-intel-brw-eu-emit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-intel-brw-eu-util.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-intel-brw-eu.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-intel-debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-intel-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-intel.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-radeon-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-radeon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-drm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-egl-context.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-error.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-fallback-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-fixed.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-font-face-twin-data.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-font-face-twin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-font-face.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-font-options.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-freed-pool.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-freelist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-ft-font.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-composite.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-device.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-dispatch.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-glyphs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-gradient.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-info.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-msaa-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-operand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-shaders.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-source.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-spans-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gl-traps-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-glx-context.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-gstate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-hull.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-image-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-image-info.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-image-source.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-image-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-line.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-lzw.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-mask-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-matrix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-mempool.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-mesh-pattern-rasterizer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-misc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-mono-scan-converter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-mutex.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-no-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-observer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-os2-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-output-stream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-paginated-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-bounds.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-fill.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-fixed.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-in-fill.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-stroke-boxes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-stroke-polygon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-stroke-traps.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-stroke-tristrip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path-stroke.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-path.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pattern.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pdf-interchange.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pdf-operators.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pdf-shading.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pdf-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-pen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-png.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-polygon-intersect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-polygon-reduce.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-polygon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-ps-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-qt-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-quartz-font.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-quartz-image-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-quartz-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-raster-source-pattern.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-recording-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-rectangle.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-rectangular-scan-converter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-region.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-rtree.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-scaled-font-subsets.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-scaled-font.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-script-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-shape-mask-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-slope.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-spans-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-spans.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-spline.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-stroke-dash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-stroke-style.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-clipper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-fallback.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-observer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-offset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-snapshot.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-subsurface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface-wrapper.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-svg-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tag-attributes.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tag-stack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tee-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-time.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tor-scan-converter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tor22-scan-converter.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-toy-font-face.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-traps-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-traps.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-tristrip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-truetype-subset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-type1-fallback.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-type1-glyph-names.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-type1-subset.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-type3-glyph-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-unicode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-user-font.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-version.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-vg-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-wgl-context.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-wideint.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-device.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-display-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-font.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-gdi-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-printing-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-win32-system.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-connection-core.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-connection-render.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-connection-shm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-resources.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-screen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-shm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-surface-core.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-surface-render.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xcb-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-core-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-display.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-fallback-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-render-compositor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-screen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-source.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-surface-shm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-visual.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xlib-xcb-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo-xml-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cairo.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check-link.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-base-compositor-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-compositor-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-null-compositor-surface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-paginated-surface.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +cairo-win32-debug.lo: win32/cairo-win32-debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-debug.lo -MD -MP -MF $(DEPDIR)/cairo-win32-debug.Tpo -c -o cairo-win32-debug.lo `test -f 'win32/cairo-win32-debug.c' || echo '$(srcdir)/'`win32/cairo-win32-debug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-debug.Tpo $(DEPDIR)/cairo-win32-debug.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-debug.c' object='cairo-win32-debug.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-debug.lo `test -f 'win32/cairo-win32-debug.c' || echo '$(srcdir)/'`win32/cairo-win32-debug.c + +cairo-win32-device.lo: win32/cairo-win32-device.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-device.lo -MD -MP -MF $(DEPDIR)/cairo-win32-device.Tpo -c -o cairo-win32-device.lo `test -f 'win32/cairo-win32-device.c' || echo '$(srcdir)/'`win32/cairo-win32-device.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-device.Tpo $(DEPDIR)/cairo-win32-device.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-device.c' object='cairo-win32-device.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-device.lo `test -f 'win32/cairo-win32-device.c' || echo '$(srcdir)/'`win32/cairo-win32-device.c + +cairo-win32-gdi-compositor.lo: win32/cairo-win32-gdi-compositor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-gdi-compositor.lo -MD -MP -MF $(DEPDIR)/cairo-win32-gdi-compositor.Tpo -c -o cairo-win32-gdi-compositor.lo `test -f 'win32/cairo-win32-gdi-compositor.c' || echo '$(srcdir)/'`win32/cairo-win32-gdi-compositor.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-gdi-compositor.Tpo $(DEPDIR)/cairo-win32-gdi-compositor.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-gdi-compositor.c' object='cairo-win32-gdi-compositor.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-gdi-compositor.lo `test -f 'win32/cairo-win32-gdi-compositor.c' || echo '$(srcdir)/'`win32/cairo-win32-gdi-compositor.c + +cairo-win32-system.lo: win32/cairo-win32-system.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-system.lo -MD -MP -MF $(DEPDIR)/cairo-win32-system.Tpo -c -o cairo-win32-system.lo `test -f 'win32/cairo-win32-system.c' || echo '$(srcdir)/'`win32/cairo-win32-system.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-system.Tpo $(DEPDIR)/cairo-win32-system.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-system.c' object='cairo-win32-system.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-system.lo `test -f 'win32/cairo-win32-system.c' || echo '$(srcdir)/'`win32/cairo-win32-system.c + +cairo-win32-surface.lo: win32/cairo-win32-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-surface.lo -MD -MP -MF $(DEPDIR)/cairo-win32-surface.Tpo -c -o cairo-win32-surface.lo `test -f 'win32/cairo-win32-surface.c' || echo '$(srcdir)/'`win32/cairo-win32-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-surface.Tpo $(DEPDIR)/cairo-win32-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-surface.c' object='cairo-win32-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-surface.lo `test -f 'win32/cairo-win32-surface.c' || echo '$(srcdir)/'`win32/cairo-win32-surface.c + +cairo-win32-display-surface.lo: win32/cairo-win32-display-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-display-surface.lo -MD -MP -MF $(DEPDIR)/cairo-win32-display-surface.Tpo -c -o cairo-win32-display-surface.lo `test -f 'win32/cairo-win32-display-surface.c' || echo '$(srcdir)/'`win32/cairo-win32-display-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-display-surface.Tpo $(DEPDIR)/cairo-win32-display-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-display-surface.c' object='cairo-win32-display-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-display-surface.lo `test -f 'win32/cairo-win32-display-surface.c' || echo '$(srcdir)/'`win32/cairo-win32-display-surface.c + +cairo-win32-printing-surface.lo: win32/cairo-win32-printing-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-printing-surface.lo -MD -MP -MF $(DEPDIR)/cairo-win32-printing-surface.Tpo -c -o cairo-win32-printing-surface.lo `test -f 'win32/cairo-win32-printing-surface.c' || echo '$(srcdir)/'`win32/cairo-win32-printing-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-printing-surface.Tpo $(DEPDIR)/cairo-win32-printing-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-printing-surface.c' object='cairo-win32-printing-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-printing-surface.lo `test -f 'win32/cairo-win32-printing-surface.c' || echo '$(srcdir)/'`win32/cairo-win32-printing-surface.c + +cairo-win32-font.lo: win32/cairo-win32-font.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-win32-font.lo -MD -MP -MF $(DEPDIR)/cairo-win32-font.Tpo -c -o cairo-win32-font.lo `test -f 'win32/cairo-win32-font.c' || echo '$(srcdir)/'`win32/cairo-win32-font.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-win32-font.Tpo $(DEPDIR)/cairo-win32-font.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='win32/cairo-win32-font.c' object='cairo-win32-font.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-win32-font.lo `test -f 'win32/cairo-win32-font.c' || echo '$(srcdir)/'`win32/cairo-win32-font.c + +cairo-drm.lo: drm/cairo-drm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm.lo -MD -MP -MF $(DEPDIR)/cairo-drm.Tpo -c -o cairo-drm.lo `test -f 'drm/cairo-drm.c' || echo '$(srcdir)/'`drm/cairo-drm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm.Tpo $(DEPDIR)/cairo-drm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm.c' object='cairo-drm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm.lo `test -f 'drm/cairo-drm.c' || echo '$(srcdir)/'`drm/cairo-drm.c + +cairo-drm-bo.lo: drm/cairo-drm-bo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-bo.lo -MD -MP -MF $(DEPDIR)/cairo-drm-bo.Tpo -c -o cairo-drm-bo.lo `test -f 'drm/cairo-drm-bo.c' || echo '$(srcdir)/'`drm/cairo-drm-bo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-bo.Tpo $(DEPDIR)/cairo-drm-bo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-bo.c' object='cairo-drm-bo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-bo.lo `test -f 'drm/cairo-drm-bo.c' || echo '$(srcdir)/'`drm/cairo-drm-bo.c + +cairo-drm-surface.lo: drm/cairo-drm-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-surface.lo -MD -MP -MF $(DEPDIR)/cairo-drm-surface.Tpo -c -o cairo-drm-surface.lo `test -f 'drm/cairo-drm-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-surface.Tpo $(DEPDIR)/cairo-drm-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-surface.c' object='cairo-drm-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-surface.lo `test -f 'drm/cairo-drm-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-surface.c + +cairo-drm-intel.lo: drm/cairo-drm-intel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-intel.lo -MD -MP -MF $(DEPDIR)/cairo-drm-intel.Tpo -c -o cairo-drm-intel.lo `test -f 'drm/cairo-drm-intel.c' || echo '$(srcdir)/'`drm/cairo-drm-intel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-intel.Tpo $(DEPDIR)/cairo-drm-intel.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-intel.c' object='cairo-drm-intel.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-intel.lo `test -f 'drm/cairo-drm-intel.c' || echo '$(srcdir)/'`drm/cairo-drm-intel.c + +cairo-drm-intel-debug.lo: drm/cairo-drm-intel-debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-intel-debug.lo -MD -MP -MF $(DEPDIR)/cairo-drm-intel-debug.Tpo -c -o cairo-drm-intel-debug.lo `test -f 'drm/cairo-drm-intel-debug.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-debug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-intel-debug.Tpo $(DEPDIR)/cairo-drm-intel-debug.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-intel-debug.c' object='cairo-drm-intel-debug.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-intel-debug.lo `test -f 'drm/cairo-drm-intel-debug.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-debug.c + +cairo-drm-intel-surface.lo: drm/cairo-drm-intel-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-intel-surface.lo -MD -MP -MF $(DEPDIR)/cairo-drm-intel-surface.Tpo -c -o cairo-drm-intel-surface.lo `test -f 'drm/cairo-drm-intel-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-intel-surface.Tpo $(DEPDIR)/cairo-drm-intel-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-intel-surface.c' object='cairo-drm-intel-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-intel-surface.lo `test -f 'drm/cairo-drm-intel-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-surface.c + +cairo-drm-i915-surface.lo: drm/cairo-drm-i915-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i915-surface.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i915-surface.Tpo -c -o cairo-drm-i915-surface.lo `test -f 'drm/cairo-drm-i915-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i915-surface.Tpo $(DEPDIR)/cairo-drm-i915-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i915-surface.c' object='cairo-drm-i915-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i915-surface.lo `test -f 'drm/cairo-drm-i915-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-surface.c + +cairo-drm-i915-glyphs.lo: drm/cairo-drm-i915-glyphs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i915-glyphs.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i915-glyphs.Tpo -c -o cairo-drm-i915-glyphs.lo `test -f 'drm/cairo-drm-i915-glyphs.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-glyphs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i915-glyphs.Tpo $(DEPDIR)/cairo-drm-i915-glyphs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i915-glyphs.c' object='cairo-drm-i915-glyphs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i915-glyphs.lo `test -f 'drm/cairo-drm-i915-glyphs.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-glyphs.c + +cairo-drm-i915-shader.lo: drm/cairo-drm-i915-shader.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i915-shader.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i915-shader.Tpo -c -o cairo-drm-i915-shader.lo `test -f 'drm/cairo-drm-i915-shader.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-shader.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i915-shader.Tpo $(DEPDIR)/cairo-drm-i915-shader.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i915-shader.c' object='cairo-drm-i915-shader.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i915-shader.lo `test -f 'drm/cairo-drm-i915-shader.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-shader.c + +cairo-drm-i915-spans.lo: drm/cairo-drm-i915-spans.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i915-spans.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i915-spans.Tpo -c -o cairo-drm-i915-spans.lo `test -f 'drm/cairo-drm-i915-spans.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-spans.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i915-spans.Tpo $(DEPDIR)/cairo-drm-i915-spans.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i915-spans.c' object='cairo-drm-i915-spans.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i915-spans.lo `test -f 'drm/cairo-drm-i915-spans.c' || echo '$(srcdir)/'`drm/cairo-drm-i915-spans.c + +cairo-drm-i965-surface.lo: drm/cairo-drm-i965-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i965-surface.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i965-surface.Tpo -c -o cairo-drm-i965-surface.lo `test -f 'drm/cairo-drm-i965-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i965-surface.Tpo $(DEPDIR)/cairo-drm-i965-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i965-surface.c' object='cairo-drm-i965-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i965-surface.lo `test -f 'drm/cairo-drm-i965-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-surface.c + +cairo-drm-i965-glyphs.lo: drm/cairo-drm-i965-glyphs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i965-glyphs.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i965-glyphs.Tpo -c -o cairo-drm-i965-glyphs.lo `test -f 'drm/cairo-drm-i965-glyphs.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-glyphs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i965-glyphs.Tpo $(DEPDIR)/cairo-drm-i965-glyphs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i965-glyphs.c' object='cairo-drm-i965-glyphs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i965-glyphs.lo `test -f 'drm/cairo-drm-i965-glyphs.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-glyphs.c + +cairo-drm-i965-shader.lo: drm/cairo-drm-i965-shader.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i965-shader.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i965-shader.Tpo -c -o cairo-drm-i965-shader.lo `test -f 'drm/cairo-drm-i965-shader.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-shader.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i965-shader.Tpo $(DEPDIR)/cairo-drm-i965-shader.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i965-shader.c' object='cairo-drm-i965-shader.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i965-shader.lo `test -f 'drm/cairo-drm-i965-shader.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-shader.c + +cairo-drm-i965-spans.lo: drm/cairo-drm-i965-spans.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-i965-spans.lo -MD -MP -MF $(DEPDIR)/cairo-drm-i965-spans.Tpo -c -o cairo-drm-i965-spans.lo `test -f 'drm/cairo-drm-i965-spans.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-spans.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-i965-spans.Tpo $(DEPDIR)/cairo-drm-i965-spans.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-i965-spans.c' object='cairo-drm-i965-spans.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-i965-spans.lo `test -f 'drm/cairo-drm-i965-spans.c' || echo '$(srcdir)/'`drm/cairo-drm-i965-spans.c + +cairo-drm-intel-brw-eu.lo: drm/cairo-drm-intel-brw-eu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-intel-brw-eu.lo -MD -MP -MF $(DEPDIR)/cairo-drm-intel-brw-eu.Tpo -c -o cairo-drm-intel-brw-eu.lo `test -f 'drm/cairo-drm-intel-brw-eu.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-brw-eu.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-intel-brw-eu.Tpo $(DEPDIR)/cairo-drm-intel-brw-eu.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-intel-brw-eu.c' object='cairo-drm-intel-brw-eu.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-intel-brw-eu.lo `test -f 'drm/cairo-drm-intel-brw-eu.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-brw-eu.c + +cairo-drm-intel-brw-eu-emit.lo: drm/cairo-drm-intel-brw-eu-emit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-intel-brw-eu-emit.lo -MD -MP -MF $(DEPDIR)/cairo-drm-intel-brw-eu-emit.Tpo -c -o cairo-drm-intel-brw-eu-emit.lo `test -f 'drm/cairo-drm-intel-brw-eu-emit.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-brw-eu-emit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-intel-brw-eu-emit.Tpo $(DEPDIR)/cairo-drm-intel-brw-eu-emit.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-intel-brw-eu-emit.c' object='cairo-drm-intel-brw-eu-emit.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-intel-brw-eu-emit.lo `test -f 'drm/cairo-drm-intel-brw-eu-emit.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-brw-eu-emit.c + +cairo-drm-intel-brw-eu-util.lo: drm/cairo-drm-intel-brw-eu-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-intel-brw-eu-util.lo -MD -MP -MF $(DEPDIR)/cairo-drm-intel-brw-eu-util.Tpo -c -o cairo-drm-intel-brw-eu-util.lo `test -f 'drm/cairo-drm-intel-brw-eu-util.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-brw-eu-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-intel-brw-eu-util.Tpo $(DEPDIR)/cairo-drm-intel-brw-eu-util.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-intel-brw-eu-util.c' object='cairo-drm-intel-brw-eu-util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-intel-brw-eu-util.lo `test -f 'drm/cairo-drm-intel-brw-eu-util.c' || echo '$(srcdir)/'`drm/cairo-drm-intel-brw-eu-util.c + +cairo-drm-radeon.lo: drm/cairo-drm-radeon.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-radeon.lo -MD -MP -MF $(DEPDIR)/cairo-drm-radeon.Tpo -c -o cairo-drm-radeon.lo `test -f 'drm/cairo-drm-radeon.c' || echo '$(srcdir)/'`drm/cairo-drm-radeon.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-radeon.Tpo $(DEPDIR)/cairo-drm-radeon.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-radeon.c' object='cairo-drm-radeon.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-radeon.lo `test -f 'drm/cairo-drm-radeon.c' || echo '$(srcdir)/'`drm/cairo-drm-radeon.c + +cairo-drm-radeon-surface.lo: drm/cairo-drm-radeon-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-radeon-surface.lo -MD -MP -MF $(DEPDIR)/cairo-drm-radeon-surface.Tpo -c -o cairo-drm-radeon-surface.lo `test -f 'drm/cairo-drm-radeon-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-radeon-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-radeon-surface.Tpo $(DEPDIR)/cairo-drm-radeon-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-radeon-surface.c' object='cairo-drm-radeon-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-radeon-surface.lo `test -f 'drm/cairo-drm-radeon-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-radeon-surface.c + +cairo-drm-gallium-surface.lo: drm/cairo-drm-gallium-surface.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cairo-drm-gallium-surface.lo -MD -MP -MF $(DEPDIR)/cairo-drm-gallium-surface.Tpo -c -o cairo-drm-gallium-surface.lo `test -f 'drm/cairo-drm-gallium-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-gallium-surface.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cairo-drm-gallium-surface.Tpo $(DEPDIR)/cairo-drm-gallium-surface.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='drm/cairo-drm-gallium-surface.c' object='cairo-drm-gallium-surface.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cairo-drm-gallium-surface.lo `test -f 'drm/cairo-drm-gallium-surface.c' || echo '$(srcdir)/'`drm/cairo-drm-gallium-surface.c + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +install-cairoincludeHEADERS: $(cairoinclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(cairoinclude_HEADERS)'; test -n "$(cairoincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(cairoincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(cairoincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(cairoincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(cairoincludedir)" || exit $$?; \ + done + +uninstall-cairoincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(cairoinclude_HEADERS)'; test -n "$(cairoincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(cairoincludedir)'; $(am__uninstall_files_from_dir) +install-nodist_cairoincludeHEADERS: $(nodist_cairoinclude_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nodist_cairoinclude_HEADERS)'; test -n "$(cairoincludedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(cairoincludedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(cairoincludedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(cairoincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(cairoincludedir)" || exit $$?; \ + done + +uninstall-nodist_cairoincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nodist_cairoinclude_HEADERS)'; test -n "$(cairoincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(cairoincludedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: $(check_PROGRAMS) + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +check-def.sh.log: check-def.sh + @p='check-def.sh'; \ + b='check-def.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +check-doc-syntax.sh.log: check-doc-syntax.sh + @p='check-doc-syntax.sh'; \ + b='check-doc-syntax.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +check-headers.sh.log: check-headers.sh + @p='check-headers.sh'; \ + b='check-headers.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +check-plt.sh.log: check-plt.sh + @p='check-plt.sh'; \ + b='check-plt.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +check-preprocessor-syntax.sh.log: check-preprocessor-syntax.sh + @p='check-preprocessor-syntax.sh'; \ + b='check-preprocessor-syntax.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +check-link.log: check-link$(EXEEXT) + @p='check-link$(EXEEXT)'; \ + b='check-link'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(cairoincludedir)" "$(DESTDIR)$(cairoincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cairo-analysis-surface.Plo + -rm -f ./$(DEPDIR)/cairo-arc.Plo + -rm -f ./$(DEPDIR)/cairo-array.Plo + -rm -f ./$(DEPDIR)/cairo-atomic.Plo + -rm -f ./$(DEPDIR)/cairo-base64-stream.Plo + -rm -f ./$(DEPDIR)/cairo-base85-stream.Plo + -rm -f ./$(DEPDIR)/cairo-bentley-ottmann-rectangular.Plo + -rm -f ./$(DEPDIR)/cairo-bentley-ottmann-rectilinear.Plo + -rm -f ./$(DEPDIR)/cairo-bentley-ottmann.Plo + -rm -f ./$(DEPDIR)/cairo-beos-surface.Plo + -rm -f ./$(DEPDIR)/cairo-botor-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-boxes-intersect.Plo + -rm -f ./$(DEPDIR)/cairo-boxes.Plo + -rm -f ./$(DEPDIR)/cairo-cache.Plo + -rm -f ./$(DEPDIR)/cairo-cff-subset.Plo + -rm -f ./$(DEPDIR)/cairo-clip-boxes.Plo + -rm -f ./$(DEPDIR)/cairo-clip-polygon.Plo + -rm -f ./$(DEPDIR)/cairo-clip-region.Plo + -rm -f ./$(DEPDIR)/cairo-clip-surface.Plo + -rm -f ./$(DEPDIR)/cairo-clip-tor-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-clip.Plo + -rm -f ./$(DEPDIR)/cairo-cogl-gradient.Plo + -rm -f ./$(DEPDIR)/cairo-cogl-surface.Plo + -rm -f ./$(DEPDIR)/cairo-color.Plo + -rm -f ./$(DEPDIR)/cairo-composite-rectangles.Plo + -rm -f ./$(DEPDIR)/cairo-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-contour.Plo + -rm -f ./$(DEPDIR)/cairo-damage.Plo + -rm -f ./$(DEPDIR)/cairo-debug.Plo + -rm -f ./$(DEPDIR)/cairo-default-context.Plo + -rm -f ./$(DEPDIR)/cairo-deflate-stream.Plo + -rm -f ./$(DEPDIR)/cairo-device.Plo + -rm -f ./$(DEPDIR)/cairo-directfb-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-bo.Plo + -rm -f ./$(DEPDIR)/cairo-drm-gallium-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-glyphs.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-shader.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-spans.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-glyphs.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-shader.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-spans.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-brw-eu-emit.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-brw-eu-util.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-brw-eu.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-debug.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel.Plo + -rm -f ./$(DEPDIR)/cairo-drm-radeon-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-radeon.Plo + -rm -f ./$(DEPDIR)/cairo-drm-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm.Plo + -rm -f ./$(DEPDIR)/cairo-egl-context.Plo + -rm -f ./$(DEPDIR)/cairo-error.Plo + -rm -f ./$(DEPDIR)/cairo-fallback-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-fixed.Plo + -rm -f ./$(DEPDIR)/cairo-font-face-twin-data.Plo + -rm -f ./$(DEPDIR)/cairo-font-face-twin.Plo + -rm -f ./$(DEPDIR)/cairo-font-face.Plo + -rm -f ./$(DEPDIR)/cairo-font-options.Plo + -rm -f ./$(DEPDIR)/cairo-freed-pool.Plo + -rm -f ./$(DEPDIR)/cairo-freelist.Plo + -rm -f ./$(DEPDIR)/cairo-ft-font.Plo + -rm -f ./$(DEPDIR)/cairo-gl-composite.Plo + -rm -f ./$(DEPDIR)/cairo-gl-device.Plo + -rm -f ./$(DEPDIR)/cairo-gl-dispatch.Plo + -rm -f ./$(DEPDIR)/cairo-gl-glyphs.Plo + -rm -f ./$(DEPDIR)/cairo-gl-gradient.Plo + -rm -f ./$(DEPDIR)/cairo-gl-info.Plo + -rm -f ./$(DEPDIR)/cairo-gl-msaa-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-gl-operand.Plo + -rm -f ./$(DEPDIR)/cairo-gl-shaders.Plo + -rm -f ./$(DEPDIR)/cairo-gl-source.Plo + -rm -f ./$(DEPDIR)/cairo-gl-spans-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-gl-surface.Plo + -rm -f ./$(DEPDIR)/cairo-gl-traps-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-glx-context.Plo + -rm -f ./$(DEPDIR)/cairo-gstate.Plo + -rm -f ./$(DEPDIR)/cairo-hash.Plo + -rm -f ./$(DEPDIR)/cairo-hull.Plo + -rm -f ./$(DEPDIR)/cairo-image-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-image-info.Plo + -rm -f ./$(DEPDIR)/cairo-image-source.Plo + -rm -f ./$(DEPDIR)/cairo-image-surface.Plo + -rm -f ./$(DEPDIR)/cairo-line.Plo + -rm -f ./$(DEPDIR)/cairo-lzw.Plo + -rm -f ./$(DEPDIR)/cairo-mask-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-matrix.Plo + -rm -f ./$(DEPDIR)/cairo-mempool.Plo + -rm -f ./$(DEPDIR)/cairo-mesh-pattern-rasterizer.Plo + -rm -f ./$(DEPDIR)/cairo-misc.Plo + -rm -f ./$(DEPDIR)/cairo-mono-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-mutex.Plo + -rm -f ./$(DEPDIR)/cairo-no-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-observer.Plo + -rm -f ./$(DEPDIR)/cairo-os2-surface.Plo + -rm -f ./$(DEPDIR)/cairo-output-stream.Plo + -rm -f ./$(DEPDIR)/cairo-paginated-surface.Plo + -rm -f ./$(DEPDIR)/cairo-path-bounds.Plo + -rm -f ./$(DEPDIR)/cairo-path-fill.Plo + -rm -f ./$(DEPDIR)/cairo-path-fixed.Plo + -rm -f ./$(DEPDIR)/cairo-path-in-fill.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-boxes.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-polygon.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-traps.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-tristrip.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke.Plo + -rm -f ./$(DEPDIR)/cairo-path.Plo + -rm -f ./$(DEPDIR)/cairo-pattern.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-interchange.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-operators.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-shading.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-surface.Plo + -rm -f ./$(DEPDIR)/cairo-pen.Plo + -rm -f ./$(DEPDIR)/cairo-png.Plo + -rm -f ./$(DEPDIR)/cairo-polygon-intersect.Plo + -rm -f ./$(DEPDIR)/cairo-polygon-reduce.Plo + -rm -f ./$(DEPDIR)/cairo-polygon.Plo + -rm -f ./$(DEPDIR)/cairo-ps-surface.Plo + -rm -f ./$(DEPDIR)/cairo-qt-surface.Plo + -rm -f ./$(DEPDIR)/cairo-quartz-font.Plo + -rm -f ./$(DEPDIR)/cairo-quartz-image-surface.Plo + -rm -f ./$(DEPDIR)/cairo-quartz-surface.Plo + -rm -f ./$(DEPDIR)/cairo-raster-source-pattern.Plo + -rm -f ./$(DEPDIR)/cairo-recording-surface.Plo + -rm -f ./$(DEPDIR)/cairo-rectangle.Plo + -rm -f ./$(DEPDIR)/cairo-rectangular-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-region.Plo + -rm -f ./$(DEPDIR)/cairo-rtree.Plo + -rm -f ./$(DEPDIR)/cairo-scaled-font-subsets.Plo + -rm -f ./$(DEPDIR)/cairo-scaled-font.Plo + -rm -f ./$(DEPDIR)/cairo-script-surface.Plo + -rm -f ./$(DEPDIR)/cairo-shape-mask-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-slope.Plo + -rm -f ./$(DEPDIR)/cairo-spans-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-spans.Plo + -rm -f ./$(DEPDIR)/cairo-spline.Plo + -rm -f ./$(DEPDIR)/cairo-stroke-dash.Plo + -rm -f ./$(DEPDIR)/cairo-stroke-style.Plo + -rm -f ./$(DEPDIR)/cairo-surface-clipper.Plo + -rm -f ./$(DEPDIR)/cairo-surface-fallback.Plo + -rm -f ./$(DEPDIR)/cairo-surface-observer.Plo + -rm -f ./$(DEPDIR)/cairo-surface-offset.Plo + -rm -f ./$(DEPDIR)/cairo-surface-snapshot.Plo + -rm -f ./$(DEPDIR)/cairo-surface-subsurface.Plo + -rm -f ./$(DEPDIR)/cairo-surface-wrapper.Plo + -rm -f ./$(DEPDIR)/cairo-surface.Plo + -rm -f ./$(DEPDIR)/cairo-svg-surface.Plo + -rm -f ./$(DEPDIR)/cairo-tag-attributes.Plo + -rm -f ./$(DEPDIR)/cairo-tag-stack.Plo + -rm -f ./$(DEPDIR)/cairo-tee-surface.Plo + -rm -f ./$(DEPDIR)/cairo-time.Plo + -rm -f ./$(DEPDIR)/cairo-tor-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-tor22-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-toy-font-face.Plo + -rm -f ./$(DEPDIR)/cairo-traps-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-traps.Plo + -rm -f ./$(DEPDIR)/cairo-tristrip.Plo + -rm -f ./$(DEPDIR)/cairo-truetype-subset.Plo + -rm -f ./$(DEPDIR)/cairo-type1-fallback.Plo + -rm -f ./$(DEPDIR)/cairo-type1-glyph-names.Plo + -rm -f ./$(DEPDIR)/cairo-type1-subset.Plo + -rm -f ./$(DEPDIR)/cairo-type3-glyph-surface.Plo + -rm -f ./$(DEPDIR)/cairo-unicode.Plo + -rm -f ./$(DEPDIR)/cairo-user-font.Plo + -rm -f ./$(DEPDIR)/cairo-version.Plo + -rm -f ./$(DEPDIR)/cairo-vg-surface.Plo + -rm -f ./$(DEPDIR)/cairo-wgl-context.Plo + -rm -f ./$(DEPDIR)/cairo-wideint.Plo + -rm -f ./$(DEPDIR)/cairo-win32-debug.Plo + -rm -f ./$(DEPDIR)/cairo-win32-device.Plo + -rm -f ./$(DEPDIR)/cairo-win32-display-surface.Plo + -rm -f ./$(DEPDIR)/cairo-win32-font.Plo + -rm -f ./$(DEPDIR)/cairo-win32-gdi-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-win32-printing-surface.Plo + -rm -f ./$(DEPDIR)/cairo-win32-surface.Plo + -rm -f ./$(DEPDIR)/cairo-win32-system.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection-core.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection-render.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection-shm.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-resources.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-screen.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-shm.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-surface-core.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-surface-render.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-surface.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-core-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-display.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-fallback-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-render-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-screen.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-source.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-surface-shm.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-surface.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-visual.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-xcb-surface.Plo + -rm -f ./$(DEPDIR)/cairo-xml-surface.Plo + -rm -f ./$(DEPDIR)/cairo.Plo + -rm -f ./$(DEPDIR)/check-link.Po + -rm -f ./$(DEPDIR)/test-base-compositor-surface.Plo + -rm -f ./$(DEPDIR)/test-compositor-surface.Plo + -rm -f ./$(DEPDIR)/test-null-compositor-surface.Plo + -rm -f ./$(DEPDIR)/test-paginated-surface.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-cairoincludeHEADERS \ + install-nodist_cairoincludeHEADERS install-pkgconfigDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/cairo-analysis-surface.Plo + -rm -f ./$(DEPDIR)/cairo-arc.Plo + -rm -f ./$(DEPDIR)/cairo-array.Plo + -rm -f ./$(DEPDIR)/cairo-atomic.Plo + -rm -f ./$(DEPDIR)/cairo-base64-stream.Plo + -rm -f ./$(DEPDIR)/cairo-base85-stream.Plo + -rm -f ./$(DEPDIR)/cairo-bentley-ottmann-rectangular.Plo + -rm -f ./$(DEPDIR)/cairo-bentley-ottmann-rectilinear.Plo + -rm -f ./$(DEPDIR)/cairo-bentley-ottmann.Plo + -rm -f ./$(DEPDIR)/cairo-beos-surface.Plo + -rm -f ./$(DEPDIR)/cairo-botor-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-boxes-intersect.Plo + -rm -f ./$(DEPDIR)/cairo-boxes.Plo + -rm -f ./$(DEPDIR)/cairo-cache.Plo + -rm -f ./$(DEPDIR)/cairo-cff-subset.Plo + -rm -f ./$(DEPDIR)/cairo-clip-boxes.Plo + -rm -f ./$(DEPDIR)/cairo-clip-polygon.Plo + -rm -f ./$(DEPDIR)/cairo-clip-region.Plo + -rm -f ./$(DEPDIR)/cairo-clip-surface.Plo + -rm -f ./$(DEPDIR)/cairo-clip-tor-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-clip.Plo + -rm -f ./$(DEPDIR)/cairo-cogl-gradient.Plo + -rm -f ./$(DEPDIR)/cairo-cogl-surface.Plo + -rm -f ./$(DEPDIR)/cairo-color.Plo + -rm -f ./$(DEPDIR)/cairo-composite-rectangles.Plo + -rm -f ./$(DEPDIR)/cairo-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-contour.Plo + -rm -f ./$(DEPDIR)/cairo-damage.Plo + -rm -f ./$(DEPDIR)/cairo-debug.Plo + -rm -f ./$(DEPDIR)/cairo-default-context.Plo + -rm -f ./$(DEPDIR)/cairo-deflate-stream.Plo + -rm -f ./$(DEPDIR)/cairo-device.Plo + -rm -f ./$(DEPDIR)/cairo-directfb-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-bo.Plo + -rm -f ./$(DEPDIR)/cairo-drm-gallium-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-glyphs.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-shader.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-spans.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i915-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-glyphs.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-shader.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-spans.Plo + -rm -f ./$(DEPDIR)/cairo-drm-i965-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-brw-eu-emit.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-brw-eu-util.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-brw-eu.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-debug.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-intel.Plo + -rm -f ./$(DEPDIR)/cairo-drm-radeon-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm-radeon.Plo + -rm -f ./$(DEPDIR)/cairo-drm-surface.Plo + -rm -f ./$(DEPDIR)/cairo-drm.Plo + -rm -f ./$(DEPDIR)/cairo-egl-context.Plo + -rm -f ./$(DEPDIR)/cairo-error.Plo + -rm -f ./$(DEPDIR)/cairo-fallback-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-fixed.Plo + -rm -f ./$(DEPDIR)/cairo-font-face-twin-data.Plo + -rm -f ./$(DEPDIR)/cairo-font-face-twin.Plo + -rm -f ./$(DEPDIR)/cairo-font-face.Plo + -rm -f ./$(DEPDIR)/cairo-font-options.Plo + -rm -f ./$(DEPDIR)/cairo-freed-pool.Plo + -rm -f ./$(DEPDIR)/cairo-freelist.Plo + -rm -f ./$(DEPDIR)/cairo-ft-font.Plo + -rm -f ./$(DEPDIR)/cairo-gl-composite.Plo + -rm -f ./$(DEPDIR)/cairo-gl-device.Plo + -rm -f ./$(DEPDIR)/cairo-gl-dispatch.Plo + -rm -f ./$(DEPDIR)/cairo-gl-glyphs.Plo + -rm -f ./$(DEPDIR)/cairo-gl-gradient.Plo + -rm -f ./$(DEPDIR)/cairo-gl-info.Plo + -rm -f ./$(DEPDIR)/cairo-gl-msaa-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-gl-operand.Plo + -rm -f ./$(DEPDIR)/cairo-gl-shaders.Plo + -rm -f ./$(DEPDIR)/cairo-gl-source.Plo + -rm -f ./$(DEPDIR)/cairo-gl-spans-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-gl-surface.Plo + -rm -f ./$(DEPDIR)/cairo-gl-traps-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-glx-context.Plo + -rm -f ./$(DEPDIR)/cairo-gstate.Plo + -rm -f ./$(DEPDIR)/cairo-hash.Plo + -rm -f ./$(DEPDIR)/cairo-hull.Plo + -rm -f ./$(DEPDIR)/cairo-image-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-image-info.Plo + -rm -f ./$(DEPDIR)/cairo-image-source.Plo + -rm -f ./$(DEPDIR)/cairo-image-surface.Plo + -rm -f ./$(DEPDIR)/cairo-line.Plo + -rm -f ./$(DEPDIR)/cairo-lzw.Plo + -rm -f ./$(DEPDIR)/cairo-mask-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-matrix.Plo + -rm -f ./$(DEPDIR)/cairo-mempool.Plo + -rm -f ./$(DEPDIR)/cairo-mesh-pattern-rasterizer.Plo + -rm -f ./$(DEPDIR)/cairo-misc.Plo + -rm -f ./$(DEPDIR)/cairo-mono-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-mutex.Plo + -rm -f ./$(DEPDIR)/cairo-no-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-observer.Plo + -rm -f ./$(DEPDIR)/cairo-os2-surface.Plo + -rm -f ./$(DEPDIR)/cairo-output-stream.Plo + -rm -f ./$(DEPDIR)/cairo-paginated-surface.Plo + -rm -f ./$(DEPDIR)/cairo-path-bounds.Plo + -rm -f ./$(DEPDIR)/cairo-path-fill.Plo + -rm -f ./$(DEPDIR)/cairo-path-fixed.Plo + -rm -f ./$(DEPDIR)/cairo-path-in-fill.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-boxes.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-polygon.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-traps.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke-tristrip.Plo + -rm -f ./$(DEPDIR)/cairo-path-stroke.Plo + -rm -f ./$(DEPDIR)/cairo-path.Plo + -rm -f ./$(DEPDIR)/cairo-pattern.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-interchange.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-operators.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-shading.Plo + -rm -f ./$(DEPDIR)/cairo-pdf-surface.Plo + -rm -f ./$(DEPDIR)/cairo-pen.Plo + -rm -f ./$(DEPDIR)/cairo-png.Plo + -rm -f ./$(DEPDIR)/cairo-polygon-intersect.Plo + -rm -f ./$(DEPDIR)/cairo-polygon-reduce.Plo + -rm -f ./$(DEPDIR)/cairo-polygon.Plo + -rm -f ./$(DEPDIR)/cairo-ps-surface.Plo + -rm -f ./$(DEPDIR)/cairo-qt-surface.Plo + -rm -f ./$(DEPDIR)/cairo-quartz-font.Plo + -rm -f ./$(DEPDIR)/cairo-quartz-image-surface.Plo + -rm -f ./$(DEPDIR)/cairo-quartz-surface.Plo + -rm -f ./$(DEPDIR)/cairo-raster-source-pattern.Plo + -rm -f ./$(DEPDIR)/cairo-recording-surface.Plo + -rm -f ./$(DEPDIR)/cairo-rectangle.Plo + -rm -f ./$(DEPDIR)/cairo-rectangular-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-region.Plo + -rm -f ./$(DEPDIR)/cairo-rtree.Plo + -rm -f ./$(DEPDIR)/cairo-scaled-font-subsets.Plo + -rm -f ./$(DEPDIR)/cairo-scaled-font.Plo + -rm -f ./$(DEPDIR)/cairo-script-surface.Plo + -rm -f ./$(DEPDIR)/cairo-shape-mask-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-slope.Plo + -rm -f ./$(DEPDIR)/cairo-spans-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-spans.Plo + -rm -f ./$(DEPDIR)/cairo-spline.Plo + -rm -f ./$(DEPDIR)/cairo-stroke-dash.Plo + -rm -f ./$(DEPDIR)/cairo-stroke-style.Plo + -rm -f ./$(DEPDIR)/cairo-surface-clipper.Plo + -rm -f ./$(DEPDIR)/cairo-surface-fallback.Plo + -rm -f ./$(DEPDIR)/cairo-surface-observer.Plo + -rm -f ./$(DEPDIR)/cairo-surface-offset.Plo + -rm -f ./$(DEPDIR)/cairo-surface-snapshot.Plo + -rm -f ./$(DEPDIR)/cairo-surface-subsurface.Plo + -rm -f ./$(DEPDIR)/cairo-surface-wrapper.Plo + -rm -f ./$(DEPDIR)/cairo-surface.Plo + -rm -f ./$(DEPDIR)/cairo-svg-surface.Plo + -rm -f ./$(DEPDIR)/cairo-tag-attributes.Plo + -rm -f ./$(DEPDIR)/cairo-tag-stack.Plo + -rm -f ./$(DEPDIR)/cairo-tee-surface.Plo + -rm -f ./$(DEPDIR)/cairo-time.Plo + -rm -f ./$(DEPDIR)/cairo-tor-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-tor22-scan-converter.Plo + -rm -f ./$(DEPDIR)/cairo-toy-font-face.Plo + -rm -f ./$(DEPDIR)/cairo-traps-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-traps.Plo + -rm -f ./$(DEPDIR)/cairo-tristrip.Plo + -rm -f ./$(DEPDIR)/cairo-truetype-subset.Plo + -rm -f ./$(DEPDIR)/cairo-type1-fallback.Plo + -rm -f ./$(DEPDIR)/cairo-type1-glyph-names.Plo + -rm -f ./$(DEPDIR)/cairo-type1-subset.Plo + -rm -f ./$(DEPDIR)/cairo-type3-glyph-surface.Plo + -rm -f ./$(DEPDIR)/cairo-unicode.Plo + -rm -f ./$(DEPDIR)/cairo-user-font.Plo + -rm -f ./$(DEPDIR)/cairo-version.Plo + -rm -f ./$(DEPDIR)/cairo-vg-surface.Plo + -rm -f ./$(DEPDIR)/cairo-wgl-context.Plo + -rm -f ./$(DEPDIR)/cairo-wideint.Plo + -rm -f ./$(DEPDIR)/cairo-win32-debug.Plo + -rm -f ./$(DEPDIR)/cairo-win32-device.Plo + -rm -f ./$(DEPDIR)/cairo-win32-display-surface.Plo + -rm -f ./$(DEPDIR)/cairo-win32-font.Plo + -rm -f ./$(DEPDIR)/cairo-win32-gdi-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-win32-printing-surface.Plo + -rm -f ./$(DEPDIR)/cairo-win32-surface.Plo + -rm -f ./$(DEPDIR)/cairo-win32-system.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection-core.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection-render.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection-shm.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-connection.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-resources.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-screen.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-shm.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-surface-core.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-surface-render.Plo + -rm -f ./$(DEPDIR)/cairo-xcb-surface.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-core-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-display.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-fallback-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-render-compositor.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-screen.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-source.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-surface-shm.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-surface.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-visual.Plo + -rm -f ./$(DEPDIR)/cairo-xlib-xcb-surface.Plo + -rm -f ./$(DEPDIR)/cairo-xml-surface.Plo + -rm -f ./$(DEPDIR)/cairo.Plo + -rm -f ./$(DEPDIR)/check-link.Po + -rm -f ./$(DEPDIR)/test-base-compositor-surface.Plo + -rm -f ./$(DEPDIR)/test-compositor-surface.Plo + -rm -f ./$(DEPDIR)/test-null-compositor-surface.Plo + -rm -f ./$(DEPDIR)/test-paginated-surface.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-cairoincludeHEADERS uninstall-libLTLIBRARIES \ + uninstall-nodist_cairoincludeHEADERS uninstall-pkgconfigDATA + +.MAKE: all check check-am install install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-cairoincludeHEADERS install-data install-data-am \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-man \ + install-nodist_cairoincludeHEADERS install-pdf install-pdf-am \ + install-pkgconfigDATA install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am \ + uninstall-cairoincludeHEADERS uninstall-libLTLIBRARIES \ + uninstall-nodist_cairoincludeHEADERS uninstall-pkgconfigDATA + +.PRECIOUS: Makefile + + +$(top_builddir)/config.h: $(top_srcdir)/config.h.in + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) config.h +cairo-features.h cairo-supported-features.h: + cd $(top_builddir) && ./config.status src/$@ +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) + +check: headers-standalone + +# 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 $@ +sparse: + @echo Checking enabled sources with sparse checker + @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_sources); do \ + echo $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f; \ + $(SPARSE) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \ + done; $$status +splint: + @echo Checking enabled sources with splint checker + @status=true; for f in $(enabled_cairo_sources) $(enabled_cairo_cxx_sources); do \ + echo $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f; \ + $(SPLINT) $(PREPROCESS_ARGS) $(srcdir)/$$f || status=false; \ + done; $$status +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 $@ + +analysis: all headers-standalone sparse splint uno + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gfx/cairo/cairo/src/Makefile.sources b/gfx/cairo/cairo/src/Makefile.sources new file mode 100644 index 000000000000..0a1ce352743d --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.sources @@ -0,0 +1,461 @@ +# 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_qt_headers = cairo-qt.h +cairo_qt_cxx_sources = cairo-qt-surface.cpp + +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_os2_headers = cairo-os2.h +cairo_os2_private = cairo-os2-private.h +cairo_os2_sources = cairo-os2-surface.c + +# automake is stupid enough to always use c++ linker if we enable the +# following lines, even if beos surface is not enabled. Disable it for now. +cairo_beos_headers = cairo-beos.h +cairo_beos_cxx_sources = cairo-beos-surface.cpp + +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_directfb_headers = cairo-directfb.h +cairo_directfb_sources = cairo-directfb-surface.c + +cairo_drm_headers = cairo-drm.h +cairo_drm_private = drm/cairo-drm-private.h \ + drm/cairo-drm-intel-private.h \ + drm/cairo-drm-intel-brw-defines.h \ + drm/cairo-drm-intel-brw-structs.h \ + drm/cairo-drm-intel-brw-eu.h \ + drm/cairo-drm-intel-command-private.h \ + drm/cairo-drm-intel-ioctl-private.h \ + drm/cairo-drm-i915-private.h \ + drm/cairo-drm-i965-private.h \ + drm/cairo-drm-radeon-private.h +cairo_drm_sources = drm/cairo-drm.c \ + drm/cairo-drm-bo.c \ + drm/cairo-drm-surface.c \ + drm/cairo-drm-intel.c \ + drm/cairo-drm-intel-debug.c \ + drm/cairo-drm-intel-surface.c \ + drm/cairo-drm-i915-surface.c \ + drm/cairo-drm-i915-glyphs.c \ + drm/cairo-drm-i915-shader.c \ + drm/cairo-drm-i915-spans.c \ + drm/cairo-drm-i965-surface.c \ + drm/cairo-drm-i965-glyphs.c \ + drm/cairo-drm-i965-shader.c \ + drm/cairo-drm-i965-spans.c \ + drm/cairo-drm-intel-brw-eu.c \ + drm/cairo-drm-intel-brw-eu-emit.c \ + drm/cairo-drm-intel-brw-eu-util.c \ + drm/cairo-drm-radeon.c \ + drm/cairo-drm-radeon-surface.c +cairo_gallium_sources = drm/cairo-drm-gallium-surface.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 + +cairo_vg_headers = cairo-vg.h +cairo_vg_sources = cairo-vg-surface.c + +cairo_cogl_headers = cairo-cogl.h +cairo_cogl_private = cairo-cogl-private.h \ + cairo-cogl-gradient-private.h +cairo_cogl_sources = cairo-cogl-surface.c \ + cairo-cogl-gradient.c diff --git a/gfx/cairo/cairo/src/Makefile.win32 b/gfx/cairo/cairo/src/Makefile.win32 new file mode 100644 index 000000000000..9afefe2b79d3 --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.win32 @@ -0,0 +1,28 @@ +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/gfx/cairo/cairo/src/Makefile.win32.features b/gfx/cairo/cairo/src/Makefile.win32.features new file mode 100644 index 000000000000..377d6dd12a8e --- /dev/null +++ b/gfx/cairo/cairo/src/Makefile.win32.features @@ -0,0 +1,661 @@ +# 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_cxx_sources = $(cairo_cxx_sources) +all_cairo_sources = $(cairo_sources) + +enabled_cairo_headers = $(cairo_headers) +enabled_cairo_private = $(cairo_private) +enabled_cairo_cxx_sources = $(cairo_cxx_sources) +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_cxx_sources += $(cairo_xlib_cxx_sources) +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_cxx_sources += $(cairo_xlib_cxx_sources) +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_cxx_sources += $(cairo_xlib_xrender_cxx_sources) +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_cxx_sources += $(cairo_xlib_xrender_cxx_sources) +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_cxx_sources += $(cairo_xcb_cxx_sources) +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_cxx_sources += $(cairo_xcb_cxx_sources) +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_cxx_sources += $(cairo_xlib_xcb_cxx_sources) +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_cxx_sources += $(cairo_xlib_xcb_cxx_sources) +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_cxx_sources += $(cairo_xcb_shm_cxx_sources) +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_cxx_sources += $(cairo_xcb_shm_cxx_sources) +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 + +unsupported_cairo_headers += $(cairo_qt_headers) +all_cairo_headers += $(cairo_qt_headers) +all_cairo_private += $(cairo_qt_private) +all_cairo_cxx_sources += $(cairo_qt_cxx_sources) +all_cairo_sources += $(cairo_qt_sources) +ifeq ($(CAIRO_HAS_QT_SURFACE),1) +enabled_cairo_headers += $(cairo_qt_headers) +enabled_cairo_private += $(cairo_qt_private) +enabled_cairo_cxx_sources += $(cairo_qt_cxx_sources) +enabled_cairo_sources += $(cairo_qt_sources) +endif +all_cairo_pkgconf += cairo-qt.pc +ifeq ($(CAIRO_HAS_QT_SURFACE),1) +enabled_cairo_pkgconf += cairo-qt.pc +endif + +supported_cairo_headers += $(cairo_quartz_headers) +all_cairo_headers += $(cairo_quartz_headers) +all_cairo_private += $(cairo_quartz_private) +all_cairo_cxx_sources += $(cairo_quartz_cxx_sources) +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_cxx_sources += $(cairo_quartz_cxx_sources) +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_cxx_sources += $(cairo_quartz_font_cxx_sources) +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_cxx_sources += $(cairo_quartz_font_cxx_sources) +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_cxx_sources += $(cairo_quartz_image_cxx_sources) +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_cxx_sources += $(cairo_quartz_image_cxx_sources) +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_cxx_sources += $(cairo_win32_cxx_sources) +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_cxx_sources += $(cairo_win32_cxx_sources) +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_cxx_sources += $(cairo_win32_font_cxx_sources) +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_cxx_sources += $(cairo_win32_font_cxx_sources) +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 + +unsupported_cairo_headers += $(cairo_os2_headers) +all_cairo_headers += $(cairo_os2_headers) +all_cairo_private += $(cairo_os2_private) +all_cairo_cxx_sources += $(cairo_os2_cxx_sources) +all_cairo_sources += $(cairo_os2_sources) +ifeq ($(CAIRO_HAS_OS2_SURFACE),1) +enabled_cairo_headers += $(cairo_os2_headers) +enabled_cairo_private += $(cairo_os2_private) +enabled_cairo_cxx_sources += $(cairo_os2_cxx_sources) +enabled_cairo_sources += $(cairo_os2_sources) +endif +all_cairo_pkgconf += cairo-os2.pc +ifeq ($(CAIRO_HAS_OS2_SURFACE),1) +enabled_cairo_pkgconf += cairo-os2.pc +endif + +unsupported_cairo_headers += $(cairo_beos_headers) +all_cairo_headers += $(cairo_beos_headers) +all_cairo_private += $(cairo_beos_private) +all_cairo_cxx_sources += $(cairo_beos_cxx_sources) +all_cairo_sources += $(cairo_beos_sources) +ifeq ($(CAIRO_HAS_BEOS_SURFACE),1) +enabled_cairo_headers += $(cairo_beos_headers) +enabled_cairo_private += $(cairo_beos_private) +enabled_cairo_cxx_sources += $(cairo_beos_cxx_sources) +enabled_cairo_sources += $(cairo_beos_sources) +endif +all_cairo_pkgconf += cairo-beos.pc +ifeq ($(CAIRO_HAS_BEOS_SURFACE),1) +enabled_cairo_pkgconf += cairo-beos.pc +endif + +unsupported_cairo_headers += $(cairo_drm_headers) +all_cairo_headers += $(cairo_drm_headers) +all_cairo_private += $(cairo_drm_private) +all_cairo_cxx_sources += $(cairo_drm_cxx_sources) +all_cairo_sources += $(cairo_drm_sources) +ifeq ($(CAIRO_HAS_DRM_SURFACE),1) +enabled_cairo_headers += $(cairo_drm_headers) +enabled_cairo_private += $(cairo_drm_private) +enabled_cairo_cxx_sources += $(cairo_drm_cxx_sources) +enabled_cairo_sources += $(cairo_drm_sources) +endif +all_cairo_pkgconf += cairo-drm.pc +ifeq ($(CAIRO_HAS_DRM_SURFACE),1) +enabled_cairo_pkgconf += cairo-drm.pc +endif + +unsupported_cairo_headers += $(cairo_gallium_headers) +all_cairo_headers += $(cairo_gallium_headers) +all_cairo_private += $(cairo_gallium_private) +all_cairo_cxx_sources += $(cairo_gallium_cxx_sources) +all_cairo_sources += $(cairo_gallium_sources) +ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1) +enabled_cairo_headers += $(cairo_gallium_headers) +enabled_cairo_private += $(cairo_gallium_private) +enabled_cairo_cxx_sources += $(cairo_gallium_cxx_sources) +enabled_cairo_sources += $(cairo_gallium_sources) +endif +all_cairo_pkgconf += cairo-gallium.pc +ifeq ($(CAIRO_HAS_GALLIUM_SURFACE),1) +enabled_cairo_pkgconf += cairo-gallium.pc +endif + +supported_cairo_headers += $(cairo_png_headers) +all_cairo_headers += $(cairo_png_headers) +all_cairo_private += $(cairo_png_private) +all_cairo_cxx_sources += $(cairo_png_cxx_sources) +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_cxx_sources += $(cairo_png_cxx_sources) +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_cxx_sources += $(cairo_gl_cxx_sources) +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_cxx_sources += $(cairo_gl_cxx_sources) +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_cxx_sources += $(cairo_glesv2_cxx_sources) +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_cxx_sources += $(cairo_glesv2_cxx_sources) +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_cxx_sources += $(cairo_glesv3_cxx_sources) +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_cxx_sources += $(cairo_glesv3_cxx_sources) +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 + +unsupported_cairo_headers += $(cairo_cogl_headers) +all_cairo_headers += $(cairo_cogl_headers) +all_cairo_private += $(cairo_cogl_private) +all_cairo_cxx_sources += $(cairo_cogl_cxx_sources) +all_cairo_sources += $(cairo_cogl_sources) +ifeq ($(CAIRO_HAS_COGL_SURFACE),1) +enabled_cairo_headers += $(cairo_cogl_headers) +enabled_cairo_private += $(cairo_cogl_private) +enabled_cairo_cxx_sources += $(cairo_cogl_cxx_sources) +enabled_cairo_sources += $(cairo_cogl_sources) +endif +all_cairo_pkgconf += cairo-cogl.pc +ifeq ($(CAIRO_HAS_COGL_SURFACE),1) +enabled_cairo_pkgconf += cairo-cogl.pc +endif + +unsupported_cairo_headers += $(cairo_directfb_headers) +all_cairo_headers += $(cairo_directfb_headers) +all_cairo_private += $(cairo_directfb_private) +all_cairo_cxx_sources += $(cairo_directfb_cxx_sources) +all_cairo_sources += $(cairo_directfb_sources) +ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1) +enabled_cairo_headers += $(cairo_directfb_headers) +enabled_cairo_private += $(cairo_directfb_private) +enabled_cairo_cxx_sources += $(cairo_directfb_cxx_sources) +enabled_cairo_sources += $(cairo_directfb_sources) +endif +all_cairo_pkgconf += cairo-directfb.pc +ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1) +enabled_cairo_pkgconf += cairo-directfb.pc +endif + +unsupported_cairo_headers += $(cairo_vg_headers) +all_cairo_headers += $(cairo_vg_headers) +all_cairo_private += $(cairo_vg_private) +all_cairo_cxx_sources += $(cairo_vg_cxx_sources) +all_cairo_sources += $(cairo_vg_sources) +ifeq ($(CAIRO_HAS_VG_SURFACE),1) +enabled_cairo_headers += $(cairo_vg_headers) +enabled_cairo_private += $(cairo_vg_private) +enabled_cairo_cxx_sources += $(cairo_vg_cxx_sources) +enabled_cairo_sources += $(cairo_vg_sources) +endif +all_cairo_pkgconf += cairo-vg.pc +ifeq ($(CAIRO_HAS_VG_SURFACE),1) +enabled_cairo_pkgconf += cairo-vg.pc +endif + +supported_cairo_headers += $(cairo_egl_headers) +all_cairo_headers += $(cairo_egl_headers) +all_cairo_private += $(cairo_egl_private) +all_cairo_cxx_sources += $(cairo_egl_cxx_sources) +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_cxx_sources += $(cairo_egl_cxx_sources) +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_cxx_sources += $(cairo_glx_cxx_sources) +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_cxx_sources += $(cairo_glx_cxx_sources) +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_cxx_sources += $(cairo_wgl_cxx_sources) +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_cxx_sources += $(cairo_wgl_cxx_sources) +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_cxx_sources += $(cairo_script_cxx_sources) +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_cxx_sources += $(cairo_script_cxx_sources) +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_cxx_sources += $(cairo_ft_cxx_sources) +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_cxx_sources += $(cairo_ft_cxx_sources) +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_cxx_sources += $(cairo_fc_cxx_sources) +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_cxx_sources += $(cairo_fc_cxx_sources) +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_cxx_sources += $(cairo_ps_cxx_sources) +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_cxx_sources += $(cairo_ps_cxx_sources) +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_cxx_sources += $(cairo_pdf_cxx_sources) +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_cxx_sources += $(cairo_pdf_cxx_sources) +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_cxx_sources += $(cairo_svg_cxx_sources) +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_cxx_sources += $(cairo_svg_cxx_sources) +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_cxx_sources += $(cairo_test_surfaces_cxx_sources) +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_cxx_sources += $(cairo_test_surfaces_cxx_sources) +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_cxx_sources += $(cairo_image_cxx_sources) +all_cairo_sources += $(cairo_image_sources) +enabled_cairo_headers += $(cairo_image_headers) +enabled_cairo_private += $(cairo_image_private) +enabled_cairo_cxx_sources += $(cairo_image_cxx_sources) +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_cxx_sources += $(cairo_mime_cxx_sources) +all_cairo_sources += $(cairo_mime_sources) +enabled_cairo_headers += $(cairo_mime_headers) +enabled_cairo_private += $(cairo_mime_private) +enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources) +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_cxx_sources += $(cairo_recording_cxx_sources) +all_cairo_sources += $(cairo_recording_sources) +enabled_cairo_headers += $(cairo_recording_headers) +enabled_cairo_private += $(cairo_recording_private) +enabled_cairo_cxx_sources += $(cairo_recording_cxx_sources) +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_cxx_sources += $(cairo_observer_cxx_sources) +all_cairo_sources += $(cairo_observer_sources) +enabled_cairo_headers += $(cairo_observer_headers) +enabled_cairo_private += $(cairo_observer_private) +enabled_cairo_cxx_sources += $(cairo_observer_cxx_sources) +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_cxx_sources += $(cairo_tee_cxx_sources) +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_cxx_sources += $(cairo_tee_cxx_sources) +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_cxx_sources += $(cairo_xml_cxx_sources) +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_cxx_sources += $(cairo_xml_cxx_sources) +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_cxx_sources += $(cairo_user_cxx_sources) +all_cairo_sources += $(cairo_user_sources) +enabled_cairo_headers += $(cairo_user_headers) +enabled_cairo_private += $(cairo_user_private) +enabled_cairo_cxx_sources += $(cairo_user_cxx_sources) +enabled_cairo_sources += $(cairo_user_sources) + +all_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers) +all_cairo_cxx_sources += $(cairo_pthread_cxx_sources) +all_cairo_sources += $(cairo_pthread_sources) +ifeq ($(CAIRO_HAS_PTHREAD),1) +enabled_cairo_private += $(cairo_pthread_private) $(cairo_pthread_headers) +enabled_cairo_cxx_sources += $(cairo_pthread_cxx_sources) +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_cxx_sources += $(cairo_gobject_cxx_sources) +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_cxx_sources += $(cairo_gobject_cxx_sources) +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_cxx_sources += $(cairo_trace_cxx_sources) +all_cairo_sources += $(cairo_trace_sources) +ifeq ($(CAIRO_HAS_TRACE),1) +enabled_cairo_private += $(cairo_trace_private) $(cairo_trace_headers) +enabled_cairo_cxx_sources += $(cairo_trace_cxx_sources) +enabled_cairo_sources += $(cairo_trace_sources) +endif + +all_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers) +all_cairo_cxx_sources += $(cairo_interpreter_cxx_sources) +all_cairo_sources += $(cairo_interpreter_sources) +ifeq ($(CAIRO_HAS_INTERPRETER),1) +enabled_cairo_private += $(cairo_interpreter_private) $(cairo_interpreter_headers) +enabled_cairo_cxx_sources += $(cairo_interpreter_cxx_sources) +enabled_cairo_sources += $(cairo_interpreter_sources) +endif + +all_cairo_private += $(cairo_symbol_lookup_private) $(cairo_symbol_lookup_headers) +all_cairo_cxx_sources += $(cairo_symbol_lookup_cxx_sources) +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_cxx_sources += $(cairo_symbol_lookup_cxx_sources) +enabled_cairo_sources += $(cairo_symbol_lookup_sources) +endif diff --git a/gfx/cairo/cairo/src/README b/gfx/cairo/cairo/src/README new file mode 100644 index 000000000000..03e4455ea58c --- /dev/null +++ b/gfx/cairo/cairo/src/README @@ -0,0 +1,69 @@ +Cairo Library Source Code +========================= + +This directory contains the source code of the cairo library. + + +Source Code Listing +------------------- + +The canonical list of source files is the file Makefile.sources. See that +file for how it works. + + +New Backends +------------ + +The rule of the thumb for adding new backends is to see how other +backends are integrated. Pick one of the simpler, unsupported, backends +and search the entire tree for it, and go from there. + +To add new backends you need to basically: + + * Modify $(top_srcdir)/configure.in to add checks for your backend. + + * Modify Makefile.sources to add source files for your backend, + + * Modify $(top_srcdir)/boilerplate/ to add boilerplate code for + testing your new backend. + + +New API +------- + +After adding new API, run "make check" in this directory and fix any +reported issues. Also add new API to the right location in +$(top_srcdir)/doc/public/cairo-sections.txt and run "make check" +in $(top_builddir)/doc/public to make sure that any newly added +documentation is correctly hooked up. + +Do not forget to add tests for the new API. See next section. + + +Tests +----- + +There are some tests in this directory that check the source code and +the build for various issues. The tests are very quick to run, and +particularly should be run after any documentation or API changes. It +does not hurt to run them after any source modification either. Run +them simply by calling: + + make check + +There are also extensive regression tests in $(top_srcdir)/test. It is +a good idea to run that test suite for any changes made to the source +code. Moreover, for any new feature, API, or bug fix, new tests should +be added to the regression test suite to test the new code. + + +Bibliography +------------ + +A detailed list of academic publications used in cairo code is available +in the file $(top_srcdir)/BIBLIOGRAPHY. Feel free to update as you +implement more papers. + +For more technical publications (eg. Adobe technical reports) just +point them out in a comment in the header of the file implementing them. + diff --git a/gfx/cairo/cairo/src/cairo-analysis-surface-private.h b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h index c7dcb8291e43..1e054c209abf 100644 --- a/gfx/cairo/cairo/src/cairo-analysis-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h @@ -37,8 +37,6 @@ #include "cairoint.h" -CAIRO_BEGIN_DECLS - cairo_private cairo_surface_t * _cairo_analysis_surface_create (cairo_surface_t *target); @@ -70,6 +68,7 @@ cairo_private cairo_int_status_t _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, cairo_int_status_t status_b); -CAIRO_END_DECLS +cairo_private cairo_surface_t * +_cairo_null_surface_create (cairo_content_t content); #endif /* CAIRO_ANALYSIS_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/cairo-analysis-surface.c b/gfx/cairo/cairo/src/cairo-analysis-surface.c index 96b43285d046..3a6fd0598678 100644 --- a/gfx/cairo/cairo/src/cairo-analysis-surface.c +++ b/gfx/cairo/cairo/src/cairo-analysis-surface.c @@ -37,10 +37,13 @@ #include "cairoint.h" #include "cairo-analysis-surface-private.h" +#include "cairo-box-inline.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-paginated-private.h" -#include "cairo-recording-surface-private.h" -#include "cairo-surface-subsurface-private.h" +#include "cairo-recording-surface-inline.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-subsurface-inline.h" #include "cairo-region-private.h" typedef struct { @@ -66,8 +69,8 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, cairo_int_status_t status_b) { /* fatal errors should be checked and propagated at source */ - assert (! _cairo_status_is_error (status_a)); - assert (! _cairo_status_is_error (status_b)); + assert (! _cairo_int_status_is_error (status_a)); + assert (! _cairo_int_status_is_error (status_b)); /* return the most important status */ if (status_a == CAIRO_INT_STATUS_UNSUPPORTED || @@ -87,48 +90,51 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a, return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; /* at this point we have checked all the valid internal codes, so... */ - assert (status_a == CAIRO_STATUS_SUCCESS && - status_b == CAIRO_STATUS_SUCCESS); + assert (status_a == CAIRO_INT_STATUS_SUCCESS && + status_b == CAIRO_INT_STATUS_SUCCESS); + return CAIRO_INT_STATUS_SUCCESS; +} + +struct proxy { + cairo_surface_t base; + cairo_surface_t *target; +}; + +static cairo_status_t +proxy_finish (void *abstract_surface) +{ return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, - const cairo_pattern_t *pattern) +static const cairo_surface_backend_t proxy_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + proxy_finish, +}; + +static cairo_surface_t * +attach_proxy (cairo_surface_t *source, + cairo_surface_t *target) { - const cairo_surface_pattern_t *surface_pattern; - cairo_bool_t old_has_ctm; - cairo_matrix_t old_ctm, p2d; - cairo_status_t status; - cairo_surface_t *source; + struct proxy *proxy; - assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); - surface_pattern = (const cairo_surface_pattern_t *) pattern; - assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING); + proxy = _cairo_malloc (sizeof (*proxy)); + if (unlikely (proxy == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); - old_ctm = surface->ctm; - old_has_ctm = surface->has_ctm; + _cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content, target->is_vector); - p2d = pattern->matrix; - status = cairo_matrix_invert (&p2d); - assert (status == CAIRO_STATUS_SUCCESS); + proxy->target = target; + _cairo_surface_attach_snapshot (source, &proxy->base, NULL); - cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm); - surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); + return &proxy->base; +} - source = surface_pattern->surface; - if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; - source = sub->target; - } - - status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base); - - surface->ctm = old_ctm; - surface->has_ctm = old_has_ctm; - - return status; +static void +detach_proxy (cairo_surface_t *proxy) +{ + cairo_surface_finish (proxy); + cairo_surface_destroy (proxy); } static cairo_int_status_t @@ -143,10 +149,11 @@ _add_operation (cairo_analysis_surface_t *surface, /* Even though the operation is not visible we must be careful * to not allow unsupported operations to be replayed to the * backend during CAIRO_PAGINATED_MODE_RENDER */ - if (backend_status == CAIRO_STATUS_SUCCESS || - backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + if (backend_status == CAIRO_INT_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || + backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } else { @@ -162,6 +169,14 @@ _add_operation (cairo_analysis_surface_t *surface, if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) { rect->x += tx; rect->y += ty; + + tx = _cairo_fixed_from_int (tx); + bbox.p1.x += tx; + bbox.p2.x += tx; + + ty = _cairo_fixed_from_int (ty); + bbox.p1.y += ty; + bbox.p2.y += ty; } else { _cairo_matrix_transform_bounding_box_fixed (&surface->ctm, &bbox, NULL); @@ -171,10 +186,11 @@ _add_operation (cairo_analysis_surface_t *surface, * careful to not allow unsupported operations to be * replayed to the backend during * CAIRO_PAGINATED_MODE_RENDER */ - if (backend_status == CAIRO_STATUS_SUCCESS || - backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + if (backend_status == CAIRO_INT_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || + backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } else { @@ -189,16 +205,8 @@ _add_operation (cairo_analysis_surface_t *surface, if (surface->first_op) { surface->first_op = FALSE; surface->page_bbox = bbox; - } else { - if (bbox.p1.x < surface->page_bbox.p1.x) - surface->page_bbox.p1.x = bbox.p1.x; - if (bbox.p1.y < surface->page_bbox.p1.y) - surface->page_bbox.p1.y = bbox.p1.y; - if (bbox.p2.x > surface->page_bbox.p2.x) - surface->page_bbox.p2.x = bbox.p2.x; - if (bbox.p2.y > surface->page_bbox.p2.y) - surface->page_bbox.p2.y = bbox.p2.y; - } + } else + _cairo_box_add_box(&surface->page_bbox, &bbox); /* If the operation is completely enclosed within the fallback * region there is no benefit in emitting a native operation as @@ -216,10 +224,10 @@ _add_operation (cairo_analysis_surface_t *surface, * transparency into the white background. */ if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) - backend_status = CAIRO_STATUS_SUCCESS; + backend_status = CAIRO_INT_STATUS_SUCCESS; } - if (backend_status == CAIRO_STATUS_SUCCESS) { + if (backend_status == CAIRO_INT_STATUS_SUCCESS) { /* Add the operation to the supported region. Operations in * this region will be emitted as native operations. */ @@ -240,12 +248,110 @@ _add_operation (cairo_analysis_surface_t *surface, * invoke the cairo-surface-fallback path then return * CAIRO_STATUS_SUCCESS. */ - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) return CAIRO_INT_STATUS_IMAGE_FALLBACK; else return status; } +static cairo_int_status_t +_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents) +{ + const cairo_surface_pattern_t *surface_pattern; + cairo_analysis_surface_t *tmp; + cairo_surface_t *source, *proxy; + cairo_matrix_t p2d; + cairo_int_status_t status; + cairo_int_status_t analysis_status = CAIRO_INT_STATUS_SUCCESS; + cairo_bool_t surface_is_unbounded; + cairo_bool_t unused; + + assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); + surface_pattern = (const cairo_surface_pattern_t *) pattern; + assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING); + source = surface_pattern->surface; + + proxy = _cairo_surface_has_snapshot (source, &proxy_backend); + if (proxy != NULL) { + /* nothing untoward found so far */ + return CAIRO_STATUS_SUCCESS; + } + + tmp = (cairo_analysis_surface_t *) + _cairo_analysis_surface_create (surface->target); + if (unlikely (tmp->base.status)) { + status =tmp->base.status; + goto cleanup1; + } + proxy = attach_proxy (source, &tmp->base); + + p2d = pattern->matrix; + status = cairo_matrix_invert (&p2d); + assert (status == CAIRO_INT_STATUS_SUCCESS); + _cairo_analysis_surface_set_ctm (&tmp->base, &p2d); + + + 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; + + /* black background or mime data fills entire extents */ + if (!(source->content & CAIRO_CONTENT_ALPHA) || _cairo_surface_has_mime_image (source)) { + cairo_rectangle_int_t rect; + + if (_cairo_surface_get_extents (source, &rect)) { + cairo_box_t bbox; + + _cairo_box_from_rectangle (&bbox, &rect); + _cairo_matrix_transform_bounding_box_fixed (&p2d, &bbox, NULL); + _cairo_box_round_to_rectangle (&bbox, &rect); + status = _add_operation (tmp, &rect, CAIRO_INT_STATUS_SUCCESS); + if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) + status = CAIRO_INT_STATUS_SUCCESS; + if (unlikely (status)) + goto cleanup2; + } + } + + if (tmp->has_supported) { + surface->has_supported = TRUE; + unused = cairo_region_union (&surface->supported_region, &tmp->supported_region); + } + + if (tmp->has_unsupported) { + surface->has_unsupported = TRUE; + unused = cairo_region_union (&surface->fallback_region, &tmp->fallback_region); + } + + analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS; + if (pattern->extend != CAIRO_EXTEND_NONE) { + _cairo_unbounded_rectangle_init (extents); + } else { + status = cairo_matrix_invert (&tmp->ctm); + _cairo_matrix_transform_bounding_box_fixed (&tmp->ctm, + &tmp->page_bbox, NULL); + _cairo_box_round_to_rectangle (&tmp->page_bbox, extents); + } + + cleanup2: + detach_proxy (proxy); + cleanup1: + cairo_surface_destroy (&tmp->base); + + if (unlikely (status)) + return status; + else + return analysis_status; +} + static cairo_status_t _cairo_analysis_surface_finish (void *abstract_surface) { @@ -269,24 +375,17 @@ _cairo_analysis_surface_get_extents (void *abstract_surface, } static void -_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip) { - const cairo_rectangle_int_t *clip_extents; - cairo_bool_t is_empty; - - clip_extents = NULL; if (clip != NULL) - clip_extents = _cairo_clip_get_extents (clip); - - if (clip_extents != NULL) - is_empty = _cairo_rectangle_intersect (extents, clip_extents); + _cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip)); } static void _cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip, + const cairo_clip_t *clip, cairo_rectangle_int_t *extents) { cairo_bool_t is_empty; @@ -296,8 +395,8 @@ _cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface, if (_cairo_operator_bounded_by_source (op)) { cairo_rectangle_int_t source_extents; - _cairo_pattern_get_extents (source, &source_extents); - is_empty = _cairo_rectangle_intersect (extents, &source_extents); + _cairo_pattern_get_extents (source, &source_extents, surface->target->is_vector); + _cairo_rectangle_intersect (extents, &source_extents); } _rectangle_intersect_clip (extents, clip); @@ -307,10 +406,10 @@ static cairo_int_status_t _cairo_analysis_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t backend_status; + cairo_int_status_t backend_status; cairo_rectangle_int_t extents; if (surface->target->backend->paint == NULL) { @@ -319,16 +418,18 @@ _cairo_analysis_surface_paint (void *abstract_surface, backend_status = surface->target->backend->paint (surface->target, op, source, clip); - if (_cairo_status_is_error (backend_status)) + if (_cairo_int_status_is_error (backend_status)) return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &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); + _cairo_rectangle_intersect (&extents, &rec_extents); + } return _add_operation (surface, &extents, backend_status); } @@ -338,12 +439,11 @@ _cairo_analysis_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t backend_status; cairo_rectangle_int_t extents; - cairo_bool_t is_empty; if (surface->target->backend->mask == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -351,31 +451,41 @@ _cairo_analysis_surface_mask (void *abstract_surface, backend_status = surface->target->backend->mask (surface->target, op, source, mask, clip); - if (_cairo_status_is_error (backend_status)) + if (_cairo_int_status_is_error (backend_status)) return backend_status; } + _cairo_analysis_surface_operation_extents (surface, + op, source, clip, + &extents); if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) { cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS; cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS; + cairo_rectangle_int_t rec_extents; if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source; - if (_cairo_surface_is_recording (surface_pattern->surface)) { + cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->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); - if (_cairo_status_is_error (backend_source_status)) + _analyze_recording_surface_pattern (surface, source, &rec_extents); + if (_cairo_int_status_is_error (backend_source_status)) return backend_source_status; + + _cairo_rectangle_intersect (&extents, &rec_extents); } } if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask; - if (_cairo_surface_is_recording (surface_pattern->surface)) { + cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->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); - if (_cairo_status_is_error (backend_mask_status)) + _analyze_recording_surface_pattern (surface, mask, &rec_extents); + if (_cairo_int_status_is_error (backend_mask_status)) return backend_mask_status; + + _cairo_rectangle_intersect (&extents, &rec_extents); } } @@ -384,37 +494,31 @@ _cairo_analysis_surface_mask (void *abstract_surface, backend_mask_status); } - _cairo_analysis_surface_operation_extents (surface, - op, source, clip, - &extents); - if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; - _cairo_pattern_get_extents (mask, &mask_extents); - is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); - + _cairo_pattern_get_extents (mask, &mask_extents, surface->target->is_vector); + _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); } static cairo_int_status_t -_cairo_analysis_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip) +_cairo_analysis_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_analysis_surface_t *surface = abstract_surface; - cairo_status_t backend_status; + cairo_int_status_t backend_status; cairo_rectangle_int_t extents; - cairo_bool_t is_empty; if (surface->target->backend->stroke == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -425,42 +529,31 @@ _cairo_analysis_surface_stroke (void *abstract_surface, ctm, ctm_inverse, tolerance, antialias, clip); - if (_cairo_status_is_error (backend_status)) + if (_cairo_int_status_is_error (backend_status)) return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &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); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; + cairo_int_status_t status; - /* If the backend can handle the stroke, then mark the approximate - * extents of the operation. However, if we need to fallback in order - * to draw the stroke, then ensure that the fallback is as tight as - * possible -- both to minimise output file size and to ensure good - * quality printed output for neighbouring regions. - */ - if (backend_status == CAIRO_STATUS_SUCCESS) { - _cairo_path_fixed_approximate_stroke_extents (path, - style, ctm, - &mask_extents); - } else { - cairo_status_t status; + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &mask_extents); + if (unlikely (status)) + return status; - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &mask_extents); - if (unlikely (status)) - return status; - } - - is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); + _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); @@ -470,16 +563,15 @@ static cairo_int_status_t _cairo_analysis_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t backend_status; + cairo_int_status_t backend_status; cairo_rectangle_int_t extents; - cairo_bool_t is_empty; if (surface->target->backend->fill == NULL) { backend_status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -489,31 +581,26 @@ _cairo_analysis_surface_fill (void *abstract_surface, source, path, fill_rule, tolerance, antialias, clip); - if (_cairo_status_is_error (backend_status)) + if (_cairo_int_status_is_error (backend_status)) return backend_status; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &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); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t mask_extents; - /* We want speed for the likely case where the operation can be - * performed natively, but accuracy if we have to resort to - * using images. - */ - if (backend_status == CAIRO_STATUS_SUCCESS) { - _cairo_path_fixed_approximate_fill_extents (path, &mask_extents); - } else { - _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &mask_extents); - } - is_empty = _cairo_rectangle_intersect (&extents, &mask_extents); + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, + &mask_extents); + + _cairo_rectangle_intersect (&extents, &mask_extents); } return _add_operation (surface, &extents, backend_status); @@ -526,13 +613,11 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; + cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; - cairo_bool_t is_empty; /* Adapted from _cairo_surface_show_glyphs */ if (surface->target->backend->show_glyphs != NULL) { @@ -541,9 +626,8 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, source, glyphs, num_glyphs, scaled_font, - clip, - remaining_glyphs); - if (_cairo_status_is_error (backend_status)) + clip); + if (_cairo_int_status_is_error (backend_status)) return backend_status; } else if (surface->target->backend->show_text_glyphs != NULL) @@ -557,7 +641,7 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, FALSE, scaled_font, clip); - if (_cairo_status_is_error (backend_status)) + if (_cairo_int_status_is_error (backend_status)) return backend_status; } else @@ -565,12 +649,14 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, backend_status = CAIRO_INT_STATUS_UNSUPPORTED; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &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); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, @@ -581,7 +667,7 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, if (unlikely (status)) return status; - is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); + _cairo_rectangle_intersect (&extents, &glyph_extents); } return _add_operation (surface, &extents, backend_status); @@ -607,12 +693,11 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; - cairo_status_t status, backend_status; + cairo_int_status_t status, backend_status; cairo_rectangle_int_t extents, glyph_extents; - cairo_bool_t is_empty; /* Adapted from _cairo_surface_show_glyphs */ backend_status = CAIRO_INT_STATUS_UNSUPPORTED; @@ -626,35 +711,30 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, cluster_flags, scaled_font, clip); - if (_cairo_status_is_error (backend_status)) + if (_cairo_int_status_is_error (backend_status)) return backend_status; } if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && surface->target->backend->show_glyphs != NULL) { - int remaining_glyphs = num_glyphs; backend_status = surface->target->backend->show_glyphs (surface->target, op, source, glyphs, num_glyphs, scaled_font, - clip, - &remaining_glyphs); - if (_cairo_status_is_error (backend_status)) + clip); + if (_cairo_int_status_is_error (backend_status)) return backend_status; - - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (remaining_glyphs == 0) - backend_status = CAIRO_STATUS_SUCCESS; } - if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) - backend_status = _analyze_recording_surface_pattern (surface, source); - _cairo_analysis_surface_operation_extents (surface, op, source, clip, &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); + _cairo_rectangle_intersect (&extents, &rec_extents); + } if (_cairo_operator_bounded_by_mask (op)) { status = _cairo_scaled_font_glyph_device_extents (scaled_font, @@ -665,47 +745,80 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, if (unlikely (status)) return status; - is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents); + _cairo_rectangle_intersect (&extents, &glyph_extents); } return _add_operation (surface, &extents, backend_status); } +static cairo_int_status_t +_cairo_analysis_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + + backend_status = CAIRO_INT_STATUS_SUCCESS; + if (surface->target->backend->tag != NULL) { + backend_status = + surface->target->backend->tag (surface->target, + begin, + tag_name, + attributes, + source, + stroke_style, + ctm, + ctm_inverse, + clip); + if (backend_status == CAIRO_INT_STATUS_SUCCESS) + surface->has_supported = TRUE; + } + + return backend_status; +} + static const cairo_surface_backend_t cairo_analysis_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, - NULL, /* create_similar */ + _cairo_analysis_surface_finish, + NULL, + + NULL, /* create_similar */ + NULL, /* create_similar_image */ + NULL, /* map_to_image */ + NULL, /* unmap */ + + NULL, /* source */ NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_analysis_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ + _cairo_analysis_surface_paint, _cairo_analysis_surface_mask, _cairo_analysis_surface_stroke, _cairo_analysis_surface_fill, - _cairo_analysis_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + _cairo_analysis_surface_show_glyphs, _cairo_analysis_surface_has_show_text_glyphs, - _cairo_analysis_surface_show_text_glyphs + _cairo_analysis_surface_show_text_glyphs, + NULL, /* get_supported_mime_types */ + _cairo_analysis_surface_tag }; cairo_surface_t * @@ -718,7 +831,7 @@ _cairo_analysis_surface_create (cairo_surface_t *target) if (unlikely (status)) return _cairo_surface_create_in_error (status); - surface = malloc (sizeof (cairo_analysis_surface_t)); + surface = _cairo_malloc (sizeof (cairo_analysis_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -727,7 +840,8 @@ _cairo_analysis_surface_create (cairo_surface_t *target) _cairo_surface_init (&surface->base, &cairo_analysis_surface_backend, NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); + CAIRO_CONTENT_COLOR_ALPHA, + target->is_vector); cairo_matrix_init_identity (&surface->ctm); surface->has_ctm = FALSE; @@ -817,101 +931,105 @@ _cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface, /* null surface type: a surface that does nothing (has no side effects, yay!) */ static cairo_int_status_t -_return_success (void) +_paint_return_success (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -/* These typedefs are just to silence the compiler... */ -typedef cairo_int_status_t -(*_paint_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); +static cairo_int_status_t +_mask_return_success (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + return CAIRO_INT_STATUS_SUCCESS; +} -typedef cairo_int_status_t -(*_mask_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); +static cairo_int_status_t +_stroke_return_success (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_INT_STATUS_SUCCESS; +} -typedef cairo_int_status_t -(*_stroke_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip); +static cairo_int_status_t +_fill_return_success (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_INT_STATUS_SUCCESS; +} -typedef cairo_int_status_t -(*_fill_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_show_glyphs_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); +static cairo_int_status_t +_show_glyphs_return_success (void *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) +{ + return CAIRO_INT_STATUS_SUCCESS; +} static const cairo_surface_backend_t cairo_null_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_NULL, + NULL, /* finish */ + + NULL, /* only accessed through the surface functions */ NULL, /* create_similar */ - NULL, /* finish */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image*/ + + NULL, /* source */ NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ NULL, /* show_page */ + NULL, /* get_extents */ - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - (_paint_func) _return_success, /* paint */ - (_mask_func) _return_success, /* mask */ - (_stroke_func) _return_success, /* stroke */ - (_fill_func) _return_success, /* fill */ - (_show_glyphs_func) _return_success, /* show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ + + _paint_return_success, /* paint */ + _mask_return_success, /* mask */ + _stroke_return_success, /* stroke */ + _fill_return_success, /* fill */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + _show_glyphs_return_success, /* show_glyphs */ NULL, /* has_show_text_glyphs */ NULL /* show_text_glyphs */ }; cairo_surface_t * -cairo_null_surface_create (cairo_content_t content) +_cairo_null_surface_create (cairo_content_t content) { cairo_surface_t *surface; - surface = malloc (sizeof (cairo_surface_t)); + surface = _cairo_malloc (sizeof (cairo_surface_t)); if (unlikely (surface == NULL)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } @@ -919,7 +1037,8 @@ cairo_null_surface_create (cairo_content_t content) _cairo_surface_init (surface, &cairo_null_surface_backend, NULL, /* device */ - content); + content, + TRUE); /* is_vector */ return surface; } diff --git a/gfx/cairo/cairo/src/cairo-arc-private.h b/gfx/cairo/cairo/src/cairo-arc-private.h index 018a14b4a711..a22c01ac90bf 100644 --- a/gfx/cairo/cairo/src/cairo-arc-private.h +++ b/gfx/cairo/cairo/src/cairo-arc-private.h @@ -38,6 +38,8 @@ #include "cairoint.h" +CAIRO_BEGIN_DECLS + cairo_private void _cairo_arc_path (cairo_t *cr, double xc, @@ -54,4 +56,6 @@ _cairo_arc_path_negative (cairo_t *cr, double angle1, double angle2); +CAIRO_END_DECLS + #endif /* CAIRO_ARC_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-arc.c b/gfx/cairo/cairo/src/cairo-arc.c index 56d42f19eb77..390397bae104 100644 --- a/gfx/cairo/cairo/src/cairo-arc.c +++ b/gfx/cairo/cairo/src/cairo-arc.c @@ -38,6 +38,8 @@ #include "cairo-arc-private.h" +#define MAX_FULL_CIRCLES 65536 + /* Spline deviation from the circle in radius would be given by: error = sqrt (x**2 + y**2) - 1 @@ -131,13 +133,13 @@ _arc_segments_needed (double angle, for some value of h. - "Approximation of circular arcs by cubic poynomials", Michael + "Approximation of circular arcs by cubic polynomials", Michael Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides various values of h along with error analysis for each. From that paper, a very practical value of h is: - h = 4/3 * tan(angle/4) + h = 4/3 * R * tan(angle/4) This value does not give the spline with minimal error, but it does provide a very good approximation, (6th-order convergence), and the @@ -184,8 +186,13 @@ _cairo_arc_in_direction (cairo_t *cr, if (cairo_status (cr)) return; - while (angle_max - angle_min > 4 * M_PI) - angle_max -= 2 * M_PI; + assert (angle_max >= angle_min); + + if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { + angle_max = fmod (angle_max - angle_min, 2 * M_PI); + angle_min = fmod (angle_min, 2 * M_PI); + angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES; + } /* Recurse if drawing arc larger than pi */ if (angle_max - angle_min > M_PI) { @@ -210,32 +217,45 @@ _cairo_arc_in_direction (cairo_t *cr, } else if (angle_max != angle_min) { cairo_matrix_t ctm; int i, segments; - double angle, angle_step; + double step; cairo_get_matrix (cr, &ctm); segments = _arc_segments_needed (angle_max - angle_min, radius, &ctm, cairo_get_tolerance (cr)); - angle_step = (angle_max - angle_min) / (double) segments; + step = (angle_max - angle_min) / segments; + segments -= 1; - if (dir == CAIRO_DIRECTION_FORWARD) { - angle = angle_min; - } else { - angle = angle_max; - angle_step = - angle_step; + if (dir == CAIRO_DIRECTION_REVERSE) { + double t; + + t = angle_min; + angle_min = angle_max; + angle_max = t; + + step = -step; } - for (i = 0; i < segments; i++, angle += angle_step) { - _cairo_arc_segment (cr, xc, yc, - radius, - angle, - angle + angle_step); + cairo_line_to (cr, + xc + radius * cos (angle_min), + yc + radius * sin (angle_min)); + + for (i = 0; i < segments; i++, angle_min += step) { + _cairo_arc_segment (cr, xc, yc, radius, + angle_min, angle_min + step); } + + _cairo_arc_segment (cr, xc, yc, radius, + angle_min, angle_max); + } else { + cairo_line_to (cr, + xc + radius * cos (angle_min), + yc + radius * sin (angle_min)); } } /** - * _cairo_arc_path + * _cairo_arc_path: * @cr: a cairo context * @xc: X position of the center of the arc * @yc: Y position of the center of the arc diff --git a/gfx/cairo/cairo/src/cairo-array-private.h b/gfx/cairo/cairo/src/cairo-array-private.h new file mode 100644 index 000000000000..35b29e5fc07d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-array-private.h @@ -0,0 +1,90 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Carl D. Worth + */ + +#ifndef CAIRO_ARRAY_PRIVATE_H +#define CAIRO_ARRAY_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +/* cairo-array.c structures and functions */ + +cairo_private void +_cairo_array_init (cairo_array_t *array, unsigned int element_size); + +cairo_private void +_cairo_array_fini (cairo_array_t *array); + +cairo_private cairo_status_t +_cairo_array_grow_by (cairo_array_t *array, unsigned int additional); + +cairo_private void +_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements); + +cairo_private cairo_status_t +_cairo_array_append (cairo_array_t *array, const void *element); + +cairo_private cairo_status_t +_cairo_array_append_multiple (cairo_array_t *array, + const void *elements, + unsigned int num_elements); + +cairo_private cairo_status_t +_cairo_array_allocate (cairo_array_t *array, + unsigned int num_elements, + void **elements); + +cairo_private void * +_cairo_array_index (cairo_array_t *array, unsigned int index); + +cairo_private const void * +_cairo_array_index_const (const cairo_array_t *array, unsigned int index); + +cairo_private void +_cairo_array_copy_element (const cairo_array_t *array, unsigned int index, void *dst); + +cairo_private unsigned int +_cairo_array_num_elements (const cairo_array_t *array); + +cairo_private unsigned int +_cairo_array_size (const cairo_array_t *array); + +CAIRO_END_DECLS + +#endif /* CAIRO_ARRAY_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-array.c b/gfx/cairo/cairo/src/cairo-array.c index b516d4c25c38..af8b7e982487 100644 --- a/gfx/cairo/cairo/src/cairo-array.c +++ b/gfx/cairo/cairo/src/cairo-array.c @@ -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 © 2004 Red Hat, Inc @@ -36,6 +37,7 @@ */ #include "cairoint.h" +#include "cairo-array-private.h" #include "cairo-error-private.h" /** @@ -53,39 +55,12 @@ * called to free resources allocated during use of the array. **/ void -_cairo_array_init (cairo_array_t *array, int element_size) +_cairo_array_init (cairo_array_t *array, unsigned int element_size) { array->size = 0; array->num_elements = 0; array->element_size = element_size; array->elements = NULL; - - array->is_snapshot = FALSE; - -} - -/** - * _cairo_array_init_snapshot: - * @array: A #cairo_array_t to be initialized as a snapshot - * @other: The #cairo_array_t from which to create the snapshot - * - * Initialize @array as an immutable copy of @other. It is an error to - * call an array-modifying function (other than _cairo_array_fini) on - * @array after calling this function. - **/ -void -_cairo_array_init_snapshot (cairo_array_t *array, - const cairo_array_t *other) -{ - array->size = other->size; - array->num_elements = other->num_elements; - array->element_size = other->element_size; - array->elements = other->elements; - - array->is_snapshot = TRUE; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); } /** @@ -99,18 +74,7 @@ _cairo_array_init_snapshot (cairo_array_t *array, void _cairo_array_fini (cairo_array_t *array) { - if (array->is_snapshot) - return; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - - if (array->elements) { - free (* array->elements); - free (array->elements); - array->elements = NULL; - array->num_elements = 0; - } + free (array->elements); } /** @@ -129,8 +93,6 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) unsigned int required_size = array->num_elements + additional; unsigned int new_size; - assert (! array->is_snapshot); - /* check for integer overflow */ if (required_size > INT_MAX || required_size < array->num_elements) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -149,16 +111,8 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) while (new_size < required_size) new_size = new_size * 2; - if (array->elements == NULL) { - array->elements = malloc (sizeof (char *)); - if (unlikely (array->elements == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *array->elements = NULL; - } - array->size = new_size; - new_elements = _cairo_realloc_ab (*array->elements, + new_elements = _cairo_realloc_ab (array->elements, array->size, array->element_size); if (unlikely (new_elements == NULL)) { @@ -166,10 +120,7 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - *array->elements = new_elements; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); + array->elements = new_elements; return CAIRO_STATUS_SUCCESS; } @@ -185,13 +136,8 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) void _cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) { - assert (! array->is_snapshot); - if (num_elements < array->num_elements) array->num_elements = num_elements; - - if (array->num_elements != 0 && *array->elements == NULL) - abort(); } /** @@ -222,7 +168,7 @@ _cairo_array_index (cairo_array_t *array, unsigned int index) /* We allow an index of 0 for the no-elements case. * This makes for cleaner calling code which will often look like: * - * elements = _cairo_array_index (array, num_elements); + * elements = _cairo_array_index (array, 0); * for (i=0; i < num_elements; i++) { * ... use elements[i] here ... * } @@ -235,10 +181,51 @@ _cairo_array_index (cairo_array_t *array, unsigned int index) assert (index < array->num_elements); - if (array->num_elements != 0 && *array->elements == NULL) - abort(); + return array->elements + index * array->element_size; +} - return (void *) &(*array->elements)[index * array->element_size]; +/** + * _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 + * pointer may be used for further direct indexing with []. For + * example: + * + * + * cairo_array_t array; + * const double *values; + * + * _cairo_array_init (&array, sizeof(double)); + * ... calls to _cairo_array_append() here ... + * + * values = _cairo_array_index_const (&array, 0); + * for (i = 0; i < _cairo_array_num_elements (&array); i++) + * ... read values[i] here ... + * + **/ +const void * +_cairo_array_index_const (const cairo_array_t *array, unsigned int index) +{ + /* We allow an index of 0 for the no-elements case. + * This makes for cleaner calling code which will often look like: + * + * elements = _cairo_array_index_const (array, 0); + * for (i=0; i < num_elements; i++) { + * ... read elements[i] here ... + * } + * + * which in the num_elements==0 case gets the NULL pointer here, + * but never dereferences it. + */ + if (index == 0 && array->num_elements == 0) + return NULL; + + assert (index < array->num_elements); + + return array->elements + index * array->element_size; } /** @@ -249,9 +236,11 @@ _cairo_array_index (cairo_array_t *array, unsigned int index) * location pointed to by @dst. **/ void -_cairo_array_copy_element (cairo_array_t *array, int index, void *dst) +_cairo_array_copy_element (const cairo_array_t *array, + unsigned int index, + void *dst) { - memcpy (dst, _cairo_array_index (array, index), array->element_size); + memcpy (dst, _cairo_array_index_const (array, index), array->element_size); } /** @@ -273,8 +262,6 @@ cairo_status_t _cairo_array_append (cairo_array_t *array, const void *element) { - assert (! array->is_snapshot); - return _cairo_array_append_multiple (array, element, 1); } @@ -293,22 +280,17 @@ _cairo_array_append (cairo_array_t *array, cairo_status_t _cairo_array_append_multiple (cairo_array_t *array, const void *elements, - int num_elements) + unsigned int num_elements) { cairo_status_t status; void *dest; - assert (! array->is_snapshot); - status = _cairo_array_allocate (array, num_elements, &dest); if (unlikely (status)) return status; memcpy (dest, elements, num_elements * array->element_size); - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - return CAIRO_STATUS_SUCCESS; } @@ -318,7 +300,7 @@ _cairo_array_append_multiple (cairo_array_t *array, * * Allocate space at the end of the array for @num_elements additional * elements, providing the address of the new memory chunk in - * @elements. This memory will be unitialized, but will be accounted + * @elements. This memory will be uninitialized, but will be accounted * for in the return value of _cairo_array_num_elements(). * * Return value: %CAIRO_STATUS_SUCCESS if successful or @@ -332,21 +314,16 @@ _cairo_array_allocate (cairo_array_t *array, { cairo_status_t status; - assert (! array->is_snapshot); - status = _cairo_array_grow_by (array, num_elements); if (unlikely (status)) return status; assert (array->num_elements + num_elements <= array->size); - *elements = &(*array->elements)[array->num_elements * array->element_size]; + *elements = array->elements + array->num_elements * array->element_size; array->num_elements += num_elements; - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - return CAIRO_STATUS_SUCCESS; } @@ -357,8 +334,8 @@ _cairo_array_allocate (cairo_array_t *array, * * This space was left intentionally blank, but gtk-doc filled it. **/ -int -_cairo_array_num_elements (cairo_array_t *array) +unsigned int +_cairo_array_num_elements (const cairo_array_t *array) { return array->num_elements; } @@ -371,8 +348,8 @@ _cairo_array_num_elements (cairo_array_t *array) * * This space was left intentionally blank, but gtk-doc filled it. **/ -int -_cairo_array_size (cairo_array_t *array) +unsigned int +_cairo_array_size (const cairo_array_t *array) { return array->size; } @@ -404,24 +381,18 @@ _cairo_user_data_array_fini (cairo_user_data_array_t *array) { unsigned int num_slots; - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - num_slots = array->num_elements; if (num_slots) { cairo_user_data_slot_t *slots; slots = _cairo_array_index (array, 0); - do { - if (slots->user_data != NULL && slots->destroy != NULL) - slots->destroy (slots->user_data); - slots++; - } while (--num_slots); + while (num_slots--) { + cairo_user_data_slot_t *s = &slots[num_slots]; + if (s->user_data != NULL && s->destroy != NULL) + s->destroy (s->user_data); + } } - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - _cairo_array_fini (array); } @@ -448,9 +419,6 @@ _cairo_user_data_array_get_data (cairo_user_data_array_t *array, if (array == NULL) return NULL; - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - num_slots = array->num_elements; slots = _cairo_array_index (array, 0); for (i = 0; i < num_slots; i++) { @@ -512,14 +480,14 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, } } - if (array->num_elements != 0 && *array->elements == NULL) - abort(); - if (slot) { *slot = new_slot; return CAIRO_STATUS_SUCCESS; } + if (user_data == NULL) + return CAIRO_STATUS_SUCCESS; + status = _cairo_array_append (array, &new_slot); if (unlikely (status)) return status; @@ -529,7 +497,7 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, cairo_status_t _cairo_user_data_array_copy (cairo_user_data_array_t *dst, - cairo_user_data_array_t *src) + const cairo_user_data_array_t *src) { /* discard any existing user-data */ if (dst->num_elements != 0) { @@ -537,11 +505,13 @@ _cairo_user_data_array_copy (cairo_user_data_array_t *dst, _cairo_user_data_array_init (dst); } + /* don't call _cairo_array_append_multiple if there's nothing to do, + * as it assumes at least 1 element is to be appended */ if (src->num_elements == 0) - return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; return _cairo_array_append_multiple (dst, - _cairo_array_index (src, 0), + _cairo_array_index_const (src, 0), src->num_elements); } diff --git a/gfx/cairo/cairo/src/cairo-atomic-private.h b/gfx/cairo/cairo/src/cairo-atomic-private.h index af462e370054..46761856fac7 100644 --- a/gfx/cairo/cairo/src/cairo-atomic-private.h +++ b/gfx/cairo/cairo/src/cairo-atomic-private.h @@ -45,9 +45,7 @@ #include "config.h" #endif -#if HAVE_WIN32_ATOMIC_PRIMITIVES -#include -#endif +#include /* The autoconf on OpenBSD 4.5 produces the malformed constant name * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */ @@ -159,37 +157,12 @@ _cairo_atomic_ptr_cmpxchg_return_old_impl(void **x, void *oldv, void *newv) #endif -#if HAVE_WIN32_ATOMIC_PRIMITIVES - -#define HAS_ATOMIC_OPS 1 - -typedef volatile long cairo_atomic_int_t; - -# define _cairo_atomic_int_get(x) ((int)*x) -# define _cairo_atomic_int_get_relaxed(x) ((int)*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) -# define _cairo_atomic_ptr_get(x) ((void*)*x) - -# define _cairo_atomic_int_inc(x) ((void) InterlockedIncrement(x)) -# define _cairo_atomic_int_dec(x) ((void) InterlockedDecrement(x)) -# define _cairo_atomic_int_dec_and_test(x) (InterlockedDecrement(x) == 0) -# define _cairo_atomic_int_cmpxchg(x, oldv, newv) (InterlockedCompareExchange(x, newv, oldv) == oldv) -# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) InterlockedCompareExchange(x, newv, oldv) - -typedef volatile void* cairo_atomic_intptr_t; - -#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv) == oldv) -#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv)) - -#endif - -#if HAVE_INTEL_ATOMIC_PRIMITIVES +#if HAVE_GCC_LEGACY_ATOMICS #define HAS_ATOMIC_OPS 1 typedef int cairo_atomic_int_t; -#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER static cairo_always_inline cairo_atomic_int_t _cairo_atomic_int_get (cairo_atomic_int_t *x) { @@ -215,14 +188,9 @@ _cairo_atomic_ptr_get (void **x) __sync_synchronize (); return *x; } -#else -# define _cairo_atomic_int_get(x) (*x) -# define _cairo_atomic_int_get_relaxed(x) (*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) -# define _cairo_atomic_ptr_get(x) (*x) -#endif # define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1)) +# define _cairo_atomic_int_dec(x) ((void) __sync_fetch_and_add(x, -1)) # define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv) # define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv) @@ -257,6 +225,7 @@ typedef AO_t cairo_atomic_int_t; # define _cairo_atomic_int_set_relaxed(x, val) (AO_store_full ((x), (val))) # define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) +# define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_full(x)) # define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv) @@ -284,10 +253,11 @@ typedef unsigned long long cairo_atomic_intptr_t; typedef int32_t cairo_atomic_int_t; # define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x)) -# define _cairo_atomic_int_get_relaxed(x) (*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) +# define _cairo_atomic_int_get_relaxed(x) *(x) +# define _cairo_atomic_int_set_relaxed(x, val) *(x) = (val) # define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x)) +# define _cairo_atomic_int_dec(x) ((void) OSAtomicDecrement32Barrier (x)) # define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0) # define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x) @@ -326,6 +296,8 @@ typedef cairo_atomic_intptr_t cairo_atomic_int_t; cairo_private void _cairo_atomic_int_inc (cairo_atomic_int_t *x); +#define _cairo_atomic_int_dec(x) _cairo_atomic_int_dec_and_test(x) + cairo_private cairo_bool_t _cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x); @@ -348,8 +320,8 @@ _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) #else # define _cairo_atomic_int_get(x) (*x) -# define _cairo_atomic_int_get_relaxed(x) (*(x)) -# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val)) +# define _cairo_atomic_int_get_relaxed(x) (*x) +# define _cairo_atomic_int_set_relaxed(x, val) (*x) = (val) # define _cairo_atomic_ptr_get(x) (*x) #endif @@ -408,12 +380,45 @@ _cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv) _cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv) #define _cairo_status_set_error(status, err) do { \ + int ret__; \ + assert (err < CAIRO_STATUS_LAST_STATUS); \ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \ * an unsigned integer instead, and about ignoring the return value. */ \ - int ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ + ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \ (void) ret__; \ } while (0) +typedef cairo_atomic_int_t cairo_atomic_once_t; + +#define CAIRO_ATOMIC_ONCE_UNINITIALIZED (0) +#define CAIRO_ATOMIC_ONCE_INITIALIZING (1) +#define CAIRO_ATOMIC_ONCE_INITIALIZED (2) +#define CAIRO_ATOMIC_ONCE_INIT CAIRO_ATOMIC_ONCE_UNINITIALIZED + +static cairo_always_inline cairo_bool_t +_cairo_atomic_init_once_enter(cairo_atomic_once_t *once) +{ + if (likely(_cairo_atomic_int_get(once) == CAIRO_ATOMIC_ONCE_INITIALIZED)) + return 0; + + if (_cairo_atomic_int_cmpxchg(once, + CAIRO_ATOMIC_ONCE_UNINITIALIZED, + CAIRO_ATOMIC_ONCE_INITIALIZING)) + return 1; + + while (_cairo_atomic_int_get(once) != CAIRO_ATOMIC_ONCE_INITIALIZED) {} + return 0; +} + +static cairo_always_inline void +_cairo_atomic_init_once_leave(cairo_atomic_once_t *once) +{ + if (unlikely(!_cairo_atomic_int_cmpxchg(once, + CAIRO_ATOMIC_ONCE_INITIALIZING, + CAIRO_ATOMIC_ONCE_INITIALIZED))) + assert (0 && "incorrect use of _cairo_atomic_init_once API (once != CAIRO_ATOMIC_ONCE_INITIALIZING)"); +} + CAIRO_END_DECLS #endif diff --git a/gfx/cairo/cairo/src/cairo-backend-private.h b/gfx/cairo/cairo/src/cairo-backend-private.h new file mode 100644 index 000000000000..67607c12f3a5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-backend-private.h @@ -0,0 +1,204 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2010 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_BACKEND_PRIVATE_H +#define CAIRO_BACKEND_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-private.h" + +typedef enum _cairo_backend_type { + CAIRO_TYPE_DEFAULT, + CAIRO_TYPE_SKIA, +} cairo_backend_type_t; + +struct _cairo_backend { + cairo_backend_type_t type; + void (*destroy) (void *cr); + + cairo_surface_t *(*get_original_target) (void *cr); + cairo_surface_t *(*get_current_target) (void *cr); + + cairo_status_t (*save) (void *cr); + cairo_status_t (*restore) (void *cr); + + cairo_status_t (*push_group) (void *cr, cairo_content_t content); + cairo_pattern_t *(*pop_group) (void *cr); + + cairo_status_t (*set_source_rgba) (void *cr, double red, double green, double blue, double alpha); + cairo_status_t (*set_source_surface) (void *cr, cairo_surface_t *surface, double x, double y); + cairo_status_t (*set_source) (void *cr, cairo_pattern_t *source); + cairo_pattern_t *(*get_source) (void *cr); + + cairo_status_t (*set_antialias) (void *cr, cairo_antialias_t antialias); + cairo_status_t (*set_dash) (void *cr, const double *dashes, int num_dashes, double offset); + cairo_status_t (*set_fill_rule) (void *cr, cairo_fill_rule_t fill_rule); + cairo_status_t (*set_line_cap) (void *cr, cairo_line_cap_t line_cap); + cairo_status_t (*set_line_join) (void *cr, cairo_line_join_t line_join); + cairo_status_t (*set_line_width) (void *cr, double line_width); + cairo_status_t (*set_miter_limit) (void *cr, double limit); + cairo_status_t (*set_opacity) (void *cr, double opacity); + cairo_status_t (*set_operator) (void *cr, cairo_operator_t op); + cairo_status_t (*set_tolerance) (void *cr, double tolerance); + + cairo_antialias_t (*get_antialias) (void *cr); + void (*get_dash) (void *cr, double *dashes, int *num_dashes, double *offset); + cairo_fill_rule_t (*get_fill_rule) (void *cr); + cairo_line_cap_t (*get_line_cap) (void *cr); + cairo_line_join_t (*get_line_join) (void *cr); + double (*get_line_width) (void *cr); + double (*get_miter_limit) (void *cr); + double (*get_opacity) (void *cr); + cairo_operator_t (*get_operator) (void *cr); + double (*get_tolerance) (void *cr); + + cairo_status_t (*translate) (void *cr, double tx, double ty); + cairo_status_t (*scale) (void *cr, double sx, double sy); + cairo_status_t (*rotate) (void *cr, double theta); + cairo_status_t (*transform) (void *cr, const cairo_matrix_t *matrix); + cairo_status_t (*set_matrix) (void *cr, const cairo_matrix_t *matrix); + cairo_status_t (*set_identity_matrix) (void *cr); + void (*get_matrix) (void *cr, cairo_matrix_t *matrix); + + void (*user_to_device) (void *cr, double *x, double *y); + void (*user_to_device_distance) (void *cr, double *x, double *y); + void (*device_to_user) (void *cr, double *x, double *y); + void (*device_to_user_distance) (void *cr, double *x, double *y); + + void (*user_to_backend) (void *cr, double *x, double *y); + void (*user_to_backend_distance) (void *cr, double *x, double *y); + void (*backend_to_user) (void *cr, double *x, double *y); + void (*backend_to_user_distance) (void *cr, double *x, double *y); + + cairo_status_t (*new_path) (void *cr); + cairo_status_t (*new_sub_path) (void *cr); + cairo_status_t (*move_to) (void *cr, double x, double y); + cairo_status_t (*rel_move_to) (void *cr, double dx, double dy); + cairo_status_t (*line_to) (void *cr, double x, double y); + cairo_status_t (*rel_line_to) (void *cr, double dx, double dy); + cairo_status_t (*curve_to) (void *cr, double x1, double y1, double x2, double y2, double x3, double y3); + cairo_status_t (*rel_curve_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3); + cairo_status_t (*arc_to) (void *cr, double x1, double y1, double x2, double y2, double radius); + cairo_status_t (*rel_arc_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double radius); + cairo_status_t (*close_path) (void *cr); + + cairo_status_t (*arc) (void *cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward); + cairo_status_t (*rectangle) (void *cr, double x, double y, double width, double height); + + void (*path_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); + cairo_bool_t (*has_current_point) (void *cr); + cairo_bool_t (*get_current_point) (void *cr, double *x, double *y); + + cairo_path_t *(*copy_path) (void *cr); + cairo_path_t *(*copy_path_flat) (void *cr); + cairo_status_t (*append_path) (void *cr, const cairo_path_t *path); + + cairo_status_t (*stroke_to_path) (void *cr); + + cairo_status_t (*clip) (void *cr); + cairo_status_t (*clip_preserve) (void *cr); + cairo_status_t (*in_clip) (void *cr, double x, double y, cairo_bool_t *inside); + cairo_status_t (*clip_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); + cairo_status_t (*reset_clip) (void *cr); + cairo_rectangle_list_t *(*clip_copy_rectangle_list) (void *cr); + + cairo_status_t (*paint) (void *cr); + cairo_status_t (*paint_with_alpha) (void *cr, double opacity); + cairo_status_t (*mask) (void *cr, cairo_pattern_t *pattern); + + cairo_status_t (*stroke) (void *cr); + cairo_status_t (*stroke_preserve) (void *cr); + cairo_status_t (*in_stroke) (void *cr, double x, double y, cairo_bool_t *inside); + cairo_status_t (*stroke_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); + + cairo_status_t (*fill) (void *cr); + cairo_status_t (*fill_preserve) (void *cr); + cairo_status_t (*in_fill) (void *cr, double x, double y, cairo_bool_t *inside); + cairo_status_t (*fill_extents) (void *cr, double *x1, double *y1, double *x2, double *y2); + + cairo_status_t (*set_font_face) (void *cr, cairo_font_face_t *font_face); + cairo_font_face_t *(*get_font_face) (void *cr); + cairo_status_t (*set_font_size) (void *cr, double size); + cairo_status_t (*set_font_matrix) (void *cr, const cairo_matrix_t *matrix); + void (*get_font_matrix) (void *cr, cairo_matrix_t *matrix); + cairo_status_t (*set_font_options) (void *cr, const cairo_font_options_t *options); + void (*get_font_options) (void *cr, cairo_font_options_t *options); + cairo_status_t (*set_scaled_font) (void *cr, cairo_scaled_font_t *scaled_font); + cairo_scaled_font_t *(*get_scaled_font) (void *cr); + cairo_status_t (*font_extents) (void *cr, cairo_font_extents_t *extents); + + cairo_status_t (*glyphs) (void *cr, + const cairo_glyph_t *glyphs, int num_glyphs, + cairo_glyph_text_info_t *info); + cairo_status_t (*glyph_path) (void *cr, + const cairo_glyph_t *glyphs, int num_glyphs); + + cairo_status_t (*glyph_extents) (void *cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + + cairo_status_t (*copy_page) (void *cr); + cairo_status_t (*show_page) (void *cr); + + cairo_status_t (*tag_begin) (void *cr, const char *tag_name, const char *attributes); + cairo_status_t (*tag_end) (void *cr, const char *tag_name); +}; + +static inline void +_cairo_backend_to_user (cairo_t *cr, double *x, double *y) +{ + cr->backend->backend_to_user (cr, x, y); +} + +static inline void +_cairo_backend_to_user_distance (cairo_t *cr, double *x, double *y) +{ + cr->backend->backend_to_user_distance (cr, x, y); +} + +static inline void +_cairo_user_to_backend (cairo_t *cr, double *x, double *y) +{ + cr->backend->user_to_backend (cr, x, y); +} + +static inline void +_cairo_user_to_backend_distance (cairo_t *cr, double *x, double *y) +{ + cr->backend->user_to_backend_distance (cr, x, y); +} + +#endif /* CAIRO_BACKEND_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-base64-stream.c b/gfx/cairo/cairo/src/cairo-base64-stream.c index 636431372b58..7f331e539ce0 100644 --- a/gfx/cairo/cairo/src/cairo-base64-stream.c +++ b/gfx/cairo/cairo/src/cairo-base64-stream.c @@ -85,6 +85,7 @@ _cairo_base64_stream_write (cairo_output_stream_t *base, switch (stream->trailing) { case 2: dst[2] = '='; + /* fall through */ case 1: dst[3] = '='; default: @@ -125,7 +126,7 @@ _cairo_base64_stream_create (cairo_output_stream_t *output) if (output->status) return _cairo_output_stream_create_in_error (output->status); - stream = malloc (sizeof (cairo_base64_stream_t)); + stream = _cairo_malloc (sizeof (cairo_base64_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; diff --git a/gfx/cairo/cairo/src/cairo-base85-stream.c b/gfx/cairo/cairo/src/cairo-base85-stream.c index f81affb49ea4..c7f02ca50648 100644 --- a/gfx/cairo/cairo/src/cairo-base85-stream.c +++ b/gfx/cairo/cairo/src/cairo-base85-stream.c @@ -53,7 +53,7 @@ _expand_four_tuple_to_five (unsigned char four_tuple[4], uint32_t value; int digit, i; - value = four_tuple[0] << 24 | four_tuple[1] << 16 | four_tuple[2] << 8 | four_tuple[3]; + value = (uint32_t)four_tuple[0] << 24 | four_tuple[1] << 16 | four_tuple[2] << 8 | four_tuple[3]; if (all_zero) *all_zero = TRUE; for (i = 0; i < 5; i++) { @@ -114,7 +114,7 @@ _cairo_base85_stream_create (cairo_output_stream_t *output) if (output->status) return _cairo_output_stream_create_in_error (output->status); - stream = malloc (sizeof (cairo_base85_stream_t)); + stream = _cairo_malloc (sizeof (cairo_base85_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c index 8c1d54f0c701..65f95d797059 100644 --- a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c @@ -40,8 +40,9 @@ #include "cairo-boxes-private.h" #include "cairo-error-private.h" -#include "cairo-combsort-private.h" +#include "cairo-combsort-inline.h" #include "cairo-list-private.h" +#include "cairo-traps-private.h" #include @@ -69,23 +70,20 @@ struct _rectangle { /* left and right children are index * 2 and (index * 2) +1 respectively */ #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) -typedef struct _pqueue { - int size, max_size; - - rectangle_t **elements; - rectangle_t *elements_embedded[1024]; -} pqueue_t; - typedef struct _sweep_line { rectangle_t **rectangles; - pqueue_t pq; - edge_t head, tail; - edge_t *insert_left, *insert_right; + rectangle_t **stop; + edge_t head, tail, *insert, *cursor; int32_t current_y; int32_t last_y; + int stop_size; + int32_t insert_x; cairo_fill_rule_t fill_rule; + cairo_bool_t do_traps; + void *container; + jmp_buf unwind; } sweep_line_t; @@ -138,64 +136,14 @@ rectangle_compare_stop (const rectangle_t *a, return a->bottom - b->bottom; } -static inline void -pqueue_init (pqueue_t *pq) -{ - pq->max_size = ARRAY_LENGTH (pq->elements_embedded); - pq->size = 0; - - pq->elements = pq->elements_embedded; - pq->elements[PQ_FIRST_ENTRY] = NULL; -} - -static inline void -pqueue_fini (pqueue_t *pq) -{ - if (pq->elements != pq->elements_embedded) - free (pq->elements); -} - -static cairo_bool_t -pqueue_grow (pqueue_t *pq) -{ - rectangle_t **new_elements; - pq->max_size *= 2; - - if (pq->elements == pq->elements_embedded) { - new_elements = _cairo_malloc_ab (pq->max_size, - sizeof (rectangle_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - - memcpy (new_elements, pq->elements_embedded, - sizeof (pq->elements_embedded)); - } else { - new_elements = _cairo_realloc_ab (pq->elements, - pq->max_size, - sizeof (rectangle_t *)); - if (unlikely (new_elements == NULL)) - return FALSE; - } - - pq->elements = new_elements; - return TRUE; -} - static inline void pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) { rectangle_t **elements; int i, parent; - if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) { - if (unlikely (! pqueue_grow (&sweep->pq))) { - longjmp (sweep->unwind, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - elements = sweep->pq.elements; - for (i = ++sweep->pq.size; + elements = sweep->stop; + for (i = ++sweep->stop_size; i != PQ_FIRST_ENTRY && rectangle_compare_stop (rectangle, elements[parent = PQ_PARENT_INDEX (i)]) < 0; @@ -208,23 +156,23 @@ pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) } static inline void -pqueue_pop (pqueue_t *pq) +rectangle_pop_stop (sweep_line_t *sweep) { - rectangle_t **elements = pq->elements; + rectangle_t **elements = sweep->stop; rectangle_t *tail; int child, i; - tail = elements[pq->size--]; - if (pq->size == 0) { + tail = elements[sweep->stop_size--]; + if (sweep->stop_size == 0) { elements[PQ_FIRST_ENTRY] = NULL; return; } for (i = PQ_FIRST_ENTRY; - (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + (child = PQ_LEFT_CHILD_INDEX (i)) <= sweep->stop_size; i = child) { - if (child != pq->size && + if (child != sweep->stop_size && rectangle_compare_stop (elements[child+1], elements[child]) < 0) { @@ -248,7 +196,7 @@ rectangle_pop_start (sweep_line_t *sweep_line) static inline rectangle_t * rectangle_peek_stop (sweep_line_t *sweep_line) { - return sweep_line->pq.elements[PQ_FIRST_ENTRY]; + return sweep_line->stop[PQ_FIRST_ENTRY]; } CAIRO_COMBSORT_DECLARE (_rectangle_sort, @@ -259,57 +207,48 @@ static void sweep_line_init (sweep_line_t *sweep_line, rectangle_t **rectangles, int num_rectangles, - cairo_fill_rule_t fill_rule) + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) { - _rectangle_sort (rectangles, num_rectangles); + rectangles[-2] = NULL; + rectangles[-1] = NULL; rectangles[num_rectangles] = NULL; sweep_line->rectangles = rectangles; + sweep_line->stop = rectangles - 2; + sweep_line->stop_size = 0; + sweep_line->insert = NULL; + sweep_line->insert_x = INT_MAX; + sweep_line->cursor = &sweep_line->tail; + + sweep_line->head.dir = 0; sweep_line->head.x = INT32_MIN; sweep_line->head.right = NULL; - sweep_line->head.dir = 0; - sweep_line->head.next = &sweep_line->tail; - /* we need to initialize prev so that we can check - * if this edge is the left most and make sure - * we always insert to the right of it, even if - * our x coordinate matches */ sweep_line->head.prev = NULL; - - sweep_line->tail.x = INT32_MAX; - sweep_line->tail.right = NULL; - sweep_line->tail.dir = 0; + sweep_line->head.next = &sweep_line->tail; sweep_line->tail.prev = &sweep_line->head; sweep_line->tail.next = NULL; - - sweep_line->insert_left = &sweep_line->tail; - sweep_line->insert_right = &sweep_line->tail; + sweep_line->tail.right = NULL; + sweep_line->tail.x = INT32_MAX; + sweep_line->tail.dir = 0; sweep_line->current_y = INT32_MIN; sweep_line->last_y = INT32_MIN; sweep_line->fill_rule = fill_rule; - - pqueue_init (&sweep_line->pq); + sweep_line->container = container; + sweep_line->do_traps = do_traps; } static void -sweep_line_fini (sweep_line_t *sweep_line) -{ - pqueue_fini (&sweep_line->pq); -} - -static void -edge_end_box (sweep_line_t *sweep_line, - edge_t *left, - int32_t bot, - cairo_bool_t do_traps, - void *container) +edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot) { cairo_status_t status = CAIRO_STATUS_SUCCESS; /* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (left->top < bot)) { - if (do_traps) { + if (sweep_line->do_traps) { cairo_line_t _left = { { left->x, left->top }, { left->x, bot }, @@ -317,8 +256,8 @@ edge_end_box (sweep_line_t *sweep_line, { left->right->x, left->top }, { left->right->x, bot }, }; - _cairo_traps_add_trap (container, left->top, bot, &_left, &_right); - status = _cairo_traps_status ((cairo_traps_t *) container); + _cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right); + status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container); } else { cairo_box_t box; @@ -327,7 +266,9 @@ edge_end_box (sweep_line_t *sweep_line, box.p2.x = left->right->x; box.p2.y = bot; - status = _cairo_boxes_add (container, &box); + status = _cairo_boxes_add (sweep_line->container, + CAIRO_ANTIALIAS_DEFAULT, + &box); } } if (unlikely (status)) @@ -345,34 +286,169 @@ static inline void edge_start_or_continue_box (sweep_line_t *sweep_line, edge_t *left, edge_t *right, - int top, - cairo_bool_t do_traps, - void *container) + int top) { if (left->right == right) return; if (left->right != NULL) { - if (right != NULL && left->right->x == right->x) { + if (left->right->x == right->x) { /* continuation on right, so just swap edges */ left->right = right; return; } - edge_end_box (sweep_line, - left, top, do_traps, container); + edge_end_box (sweep_line, left, top); } - if (right != NULL && left->x != right->x) { + if (left->x != right->x) { left->top = top; left->right = right; } } +/* + * Merge two sorted edge lists. + * Input: + * - head_a: The head of the first list. + * - head_b: The head of the second list; head_b cannot be NULL. + * Output: + * Returns the head of the merged list. + * + * Implementation notes: + * To make it fast (in particular, to reduce to an insertion sort whenever + * one of the two input lists only has a single element) we iterate through + * a list until its head becomes greater than the head of the other list, + * then we switch their roles. As soon as one of the two lists is empty, we + * just attach the other one to the current list and exit. + * Writes to memory are only needed to "switch" lists (as it also requires + * attaching to the output list the list which we will be iterating next) and + * to attach the last non-empty list. + */ +static edge_t * +merge_sorted_edges (edge_t *head_a, edge_t *head_b) +{ + edge_t *head, *prev; + int32_t x; + + prev = head_a->prev; + if (head_a->x <= head_b->x) { + head = head_a; + } else { + head_b->prev = prev; + head = head_b; + goto start_with_b; + } + + do { + x = head_b->x; + while (head_a != NULL && head_a->x <= x) { + prev = head_a; + head_a = head_a->next; + } + + head_b->prev = prev; + prev->next = head_b; + if (head_a == NULL) + return head; + +start_with_b: + x = head_a->x; + while (head_b != NULL && head_b->x <= x) { + prev = head_b; + head_b = head_b->next; + } + + head_a->prev = prev; + prev->next = head_a; + if (head_b == NULL) + return head; + } while (1); +} + +/* + * Sort (part of) a list. + * Input: + * - list: The list to be sorted; list cannot be NULL. + * - limit: Recursion limit. + * Output: + * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the + * input list; if the input list has fewer elements, head_out be a sorted list + * containing all the elements of the input list. + * Returns the head of the list of unprocessed elements (NULL if the sorted list contains + * all the elements of the input list). + * + * Implementation notes: + * Special case single element list, unroll/inline the sorting of the first two elements. + * Some tail recursion is used since we iterate on the bottom-up solution of the problem + * (we start with a small sorted list and keep merging other lists of the same size to it). + */ +static edge_t * +sort_edges (edge_t *list, + unsigned int level, + edge_t **head_out) +{ + edge_t *head_other, *remaining; + unsigned int i; + + head_other = list->next; + + if (head_other == NULL) { + *head_out = list; + return NULL; + } + + remaining = head_other->next; + if (list->x <= head_other->x) { + *head_out = list; + head_other->next = NULL; + } else { + *head_out = head_other; + head_other->prev = list->prev; + head_other->next = list; + list->prev = head_other; + list->next = NULL; + } + + for (i = 0; i < level && remaining; i++) { + remaining = sort_edges (remaining, i, &head_other); + *head_out = merge_sorted_edges (*head_out, head_other); + } + + return remaining; +} + +static edge_t * +merge_unsorted_edges (edge_t *head, edge_t *unsorted) +{ + sort_edges (unsorted, UINT_MAX, &unsorted); + return merge_sorted_edges (head, unsorted); +} + +static void +active_edges_insert (sweep_line_t *sweep) +{ + edge_t *prev; + int x; + + x = sweep->insert_x; + prev = sweep->cursor; + if (prev->x > x) { + do { + prev = prev->prev; + } while (prev->x > x); + } else { + while (prev->next->x < x) + prev = prev->next; + } + + prev->next = merge_unsorted_edges (prev->next, sweep->insert); + sweep->cursor = sweep->insert; + sweep->insert = NULL; + sweep->insert_x = INT_MAX; +} static inline void -active_edges_to_traps (sweep_line_t *sweep, - cairo_bool_t do_traps, - void *container) +active_edges_to_traps (sweep_line_t *sweep) { int top = sweep->current_y; edge_t *pos; @@ -380,6 +456,9 @@ active_edges_to_traps (sweep_line_t *sweep, if (sweep->last_y == sweep->current_y) return; + if (sweep->insert) + active_edges_insert (sweep); + pos = sweep->head.next; if (pos == &sweep->tail) return; @@ -395,51 +474,42 @@ active_edges_to_traps (sweep_line_t *sweep, right = left->next; /* Check if there is a co-linear edge with an existing trap */ - if (left->right == NULL) { - while (unlikely (right->x == left->x)) { - winding += right->dir; - if (right->right != NULL) { - /* continuation on left */ - left->top = right->top; - left->right = right->right; - right->right = NULL; - winding -= right->dir; - break; - } - - right = right->next; - } - - if (winding == 0) { - pos = right; - continue; + while (right->x == left->x) { + if (right->right != NULL) { + assert (left->right == NULL); + /* continuation on left */ + left->top = right->top; + left->right = right->right; + right->right = NULL; } + winding += right->dir; + right = right->next; } - /* Greedily search for the closing edge, so that we generate the - * maximal span width with the minimal number of trapezoids. - */ + if (winding == 0) { + if (left->right != NULL) + edge_end_box (sweep, left, top); + pos = right; + continue; + } do { /* End all subsumed traps */ - if (unlikely (right->right != NULL)) { - edge_end_box (sweep, - right, top, do_traps, container); - } + if (unlikely (right->right != NULL)) + edge_end_box (sweep, right, top); + /* Greedily search for the closing edge, so that we generate + * the * maximal span width with the minimal number of + * boxes. + */ winding += right->dir; - if (winding == 0) { - /* skip co-linear edges */ - if (likely (right->x != right->next->x)) - break; - } + if (winding == 0 && right->x != right->next->x) + break; right = right->next; } while (TRUE); - edge_start_or_continue_box (sweep, - left, right, top, - do_traps, container); + edge_start_or_continue_box (sweep, left, right, top); pos = right->next; } while (pos != &sweep->tail); @@ -450,23 +520,17 @@ active_edges_to_traps (sweep_line_t *sweep, do { /* End all subsumed traps */ - if (unlikely (right->right != NULL)) { - edge_end_box (sweep, - right, top, do_traps, container); - } + if (unlikely (right->right != NULL)) + edge_end_box (sweep, right, top); - if (++count & 1) { /* skip co-linear edges */ - if (likely (right->x != right->next->x)) - break; - } + if (++count & 1 && right->x != right->next->x) + break; right = right->next; } while (TRUE); - edge_start_or_continue_box (sweep, - pos, right, top, - do_traps, container); + edge_start_or_continue_box (sweep, pos, right, top); pos = right->next; } while (pos != &sweep->tail); @@ -476,38 +540,26 @@ active_edges_to_traps (sweep_line_t *sweep, } static inline void -sweep_line_delete_edge (sweep_line_t *sweep_line, - edge_t *edge, - cairo_bool_t do_traps, - void *container) +sweep_line_delete_edge (sweep_line_t *sweep, edge_t *edge) { if (edge->right != NULL) { edge_t *next = edge->next; if (next->x == edge->x) { next->top = edge->top; next->right = edge->right; - } else { - edge_end_box (sweep_line, - edge, - sweep_line->current_y, - do_traps, container); - } + } else + edge_end_box (sweep, edge, sweep->current_y); } - if (sweep_line->insert_left == edge) - sweep_line->insert_left = edge->next; - if (sweep_line->insert_right == edge) - sweep_line->insert_right = edge->next; + if (sweep->cursor == edge) + sweep->cursor = edge->prev; edge->prev->next = edge->next; edge->next->prev = edge->prev; } static inline cairo_bool_t -sweep_line_delete (sweep_line_t *sweep, - rectangle_t *rectangle, - cairo_bool_t do_traps, - void *container) +sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle) { cairo_bool_t update; @@ -518,82 +570,27 @@ sweep_line_delete (sweep_line_t *sweep, update = rectangle->left.next != &rectangle->right; } - sweep_line_delete_edge (sweep, - &rectangle->left, - do_traps, container); + sweep_line_delete_edge (sweep, &rectangle->left); + sweep_line_delete_edge (sweep, &rectangle->right); - sweep_line_delete_edge (sweep, - &rectangle->right, - do_traps, container); - - pqueue_pop (&sweep->pq); + rectangle_pop_stop (sweep); return update; } static inline void -insert_edge (edge_t *edge, edge_t *pos) +sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) { - if (pos->x != edge->x) { - if (pos->x > edge->x) { - do { - UNROLL3({ - if (pos->prev->x <= edge->x) - break; - pos = pos->prev; - }) - } while (TRUE); - } else { - do { - UNROLL3({ - pos = pos->next; - if (pos->x >= edge->x) - break; - }) - } while (TRUE); - } - } - if (pos->prev) { - pos->prev->next = edge; - edge->prev = pos->prev; - edge->next = pos; - pos->prev = edge; - } else { - /* we have edge that shares an x coordinate with the left most sentinal. - * instead of inserting before pos and ruining our sentinal we insert after pos. */ - pos->next->prev = edge; - edge->next = pos->next; - edge->prev = pos; - pos->next = edge; - } -} - -static inline cairo_bool_t -sweep_line_insert (sweep_line_t *sweep, - rectangle_t *rectangle) -{ - edge_t *pos; - - /* right edge */ - pos = sweep->insert_right; - insert_edge (&rectangle->right, pos); - sweep->insert_right = &rectangle->right; - - /* left edge */ - pos = sweep->insert_left; - if (pos->x > sweep->insert_right->x) - pos = sweep->insert_right->prev; - insert_edge (&rectangle->left, pos); - sweep->insert_left = &rectangle->left; + if (sweep->insert) + sweep->insert->prev = &rectangle->right; + rectangle->right.next = sweep->insert; + rectangle->right.prev = &rectangle->left; + rectangle->left.next = &rectangle->right; + rectangle->left.prev = NULL; + sweep->insert = &rectangle->left; + if (rectangle->left.x < sweep->insert_x) + sweep->insert_x = rectangle->left.x; pqueue_push (sweep, rectangle); - - if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && - rectangle->left.prev->dir == rectangle->left.dir) - { - return rectangle->left.next != &rectangle->right; - } - - return TRUE; } static cairo_status_t @@ -606,11 +603,16 @@ _cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, sweep_line_t sweep_line; rectangle_t *rectangle; cairo_status_t status; - cairo_bool_t update = FALSE; + cairo_bool_t update; - sweep_line_init (&sweep_line, rectangles, num_rectangles, fill_rule); + sweep_line_init (&sweep_line, + rectangles, num_rectangles, + fill_rule, + do_traps, container); if ((status = setjmp (sweep_line.unwind))) - goto unwind; + return status; + + update = FALSE; rectangle = rectangle_pop_start (&sweep_line); do { @@ -621,46 +623,45 @@ _cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, while (stop != NULL && stop->bottom < rectangle->top) { if (stop->bottom != sweep_line.current_y) { if (update) { - active_edges_to_traps (&sweep_line, - do_traps, container); + active_edges_to_traps (&sweep_line); update = FALSE; } sweep_line.current_y = stop->bottom; } - update |= sweep_line_delete (&sweep_line, stop, do_traps, container); - + update |= sweep_line_delete (&sweep_line, stop); stop = rectangle_peek_stop (&sweep_line); } if (update) { - active_edges_to_traps (&sweep_line, do_traps, container); + active_edges_to_traps (&sweep_line); update = FALSE; } sweep_line.current_y = rectangle->top; } - update |= sweep_line_insert (&sweep_line, rectangle); - } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL); + do { + sweep_line_insert (&sweep_line, rectangle); + } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL && + sweep_line.current_y == rectangle->top); + update = TRUE; + } while (rectangle); while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { if (rectangle->bottom != sweep_line.current_y) { if (update) { - active_edges_to_traps (&sweep_line, do_traps, container); + active_edges_to_traps (&sweep_line); update = FALSE; } - sweep_line.current_y = rectangle->bottom; } - update |= sweep_line_delete (&sweep_line, rectangle, do_traps, container); + update |= sweep_line_delete (&sweep_line, rectangle); } -unwind: - sweep_line_fini (&sweep_line); - return status; + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -668,13 +669,12 @@ _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; - rectangle_t *rectangles; - rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; - rectangle_t **rectangles_ptrs; + rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; + rectangle_t *rectangles, **rectangles_ptrs; cairo_status_t status; int i; - assert (traps->is_rectangular); + assert (traps->is_rectangular); if (unlikely (traps->num_traps <= 1)) { if (traps->num_traps == 1) { @@ -683,9 +683,9 @@ _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_line_t tmp = trap->left; trap->left = trap->right; trap->right = tmp; - } + } } - return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; } dump_traps (traps, "bo-rects-traps-in.txt"); @@ -694,9 +694,9 @@ _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, rectangles_ptrs = stack_rectangles_ptrs; if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, - sizeof (rectangle_t) + - sizeof (rectangle_t *), - sizeof (rectangle_t *)); + sizeof (rectangle_t) + + sizeof (rectangle_t *), + 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -724,11 +724,13 @@ _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, rectangles[i].top = traps->traps[i].top; rectangles[i].bottom = traps->traps[i].bottom; - rectangles_ptrs[i] = &rectangles[i]; + rectangles_ptrs[i+2] = &rectangles[i]; } + /* XXX incremental sort */ + _rectangle_sort (rectangles_ptrs+2, i); _cairo_traps_clear (traps); - status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, i, + status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; @@ -748,12 +750,13 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, cairo_boxes_t *out) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; - rectangle_t *rectangles; - rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; - rectangle_t **rectangles_ptrs; + rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; + rectangle_t *rectangles, **rectangles_ptrs; + rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ]; + rectangle_t **rectangles_chain = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; - int i, j; + int i, j, y_min, y_max; if (unlikely (in->num_boxes == 0)) { _cairo_boxes_clear (out); @@ -779,21 +782,48 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, } _cairo_boxes_clear (out); - status = _cairo_boxes_add (out, &box); + status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); } return CAIRO_STATUS_SUCCESS; } + y_min = INT_MAX; y_max = INT_MIN; + for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + if (box[i].p1.y < y_min) + y_min = box[i].p1.y; + if (box[i].p1.y > y_max) + y_max = box[i].p1.y; + } + } + y_min = _cairo_fixed_integer_floor (y_min); + y_max = _cairo_fixed_integer_floor (y_max) + 1; + y_max -= y_min; + + if (y_max < in->num_boxes) { + rectangles_chain = stack_rectangles_chain; + if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) { + rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *)); + if (unlikely (rectangles_chain == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*)); + } + rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, - sizeof (rectangle_t) + - sizeof (rectangle_t *), - sizeof (rectangle_t *)); - if (unlikely (rectangles == NULL)) + sizeof (rectangle_t) + + sizeof (rectangle_t *), + 3*sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) { + if (rectangles_chain != stack_rectangles_chain) + free (rectangles_chain); return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); } @@ -802,6 +832,8 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { + int h; + if (box[i].p1.x < box[i].p2.x) { rectangles[j].left.x = box[i].p1.x; rectangles[j].left.dir = 1; @@ -822,13 +854,38 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; - rectangles_ptrs[j] = &rectangles[j]; + if (rectangles_chain) { + h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min; + rectangles[j].left.next = (edge_t *)rectangles_chain[h]; + rectangles_chain[h] = &rectangles[j]; + } else { + rectangles_ptrs[j+2] = &rectangles[j]; + } j++; } } + if (rectangles_chain) { + j = 2; + for (y_min = 0; y_min < y_max; y_min++) { + rectangle_t *r; + int start = j; + for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next) + rectangles_ptrs[j++] = r; + if (j > start + 1) + _rectangle_sort (rectangles_ptrs + start, j - start); + } + + if (rectangles_chain != stack_rectangles_chain) + free (rectangles_chain); + + j -= 2; + } else { + _rectangle_sort (rectangles_ptrs + 2, j); + } + _cairo_boxes_clear (out); - status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, j, + status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j, fill_rule, FALSE, out); if (rectangles != stack_rectangles) diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c index 1696d9367b61..7c0be69b712a 100644 --- a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c @@ -39,8 +39,9 @@ #include "cairoint.h" #include "cairo-boxes-private.h" -#include "cairo-combsort-private.h" +#include "cairo-combsort-inline.h" #include "cairo-error-private.h" +#include "cairo-traps-private.h" typedef struct _cairo_bo_edge cairo_bo_edge_t; typedef struct _cairo_bo_trap cairo_bo_trap_t; @@ -237,7 +238,7 @@ _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, box.p1.y = trap->top; box.p2.x = trap->right->edge.line.p1.x; box.p2.y = bot; - status = _cairo_boxes_add (container, &box); + status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box); } } @@ -436,74 +437,6 @@ _cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, - const cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule) -{ - cairo_status_t status; - cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; - cairo_bo_event_t *events; - cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; - cairo_bo_event_t **event_ptrs; - cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; - cairo_bo_edge_t *edges; - int num_events; - int i, j; - - if (unlikely (polygon->num_edges == 0)) - return CAIRO_STATUS_SUCCESS; - - num_events = 2 * polygon->num_edges; - - events = stack_events; - event_ptrs = stack_event_ptrs; - edges = stack_edges; - if (num_events > ARRAY_LENGTH (stack_events)) { - events = _cairo_malloc_ab_plus_c (num_events, - sizeof (cairo_bo_event_t) + - sizeof (cairo_bo_edge_t) + - sizeof (cairo_bo_event_t *), - sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - event_ptrs = (cairo_bo_event_t **) (events + num_events); - edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1); - } - - for (i = j = 0; i < polygon->num_edges; i++) { - edges[i].edge = polygon->edges[i]; - edges[i].deferred_trap.right = NULL; - edges[i].prev = NULL; - edges[i].next = NULL; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_START; - events[j].point.y = polygon->edges[i].top; - events[j].point.x = polygon->edges[i].line.p1.x; - events[j].edge = &edges[i]; - j++; - - event_ptrs[j] = &events[j]; - events[j].type = CAIRO_BO_EVENT_TYPE_STOP; - events[j].point.y = polygon->edges[i].bottom; - events[j].point.x = polygon->edges[i].line.p1.x; - events[j].edge = &edges[i]; - j++; - } - - status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, - fill_rule, - TRUE, traps); - if (events != stack_events) - free (events); - - traps->is_rectilinear = TRUE; - - return status; -} - cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule, diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c index b3819f2f7faf..eb4cee43067a 100644 --- a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c @@ -38,9 +38,11 @@ /* Provide definitions for standalone compilation */ #include "cairoint.h" +#include "cairo-combsort-inline.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" -#include "cairo-combsort-private.h" +#include "cairo-line-inline.h" +#include "cairo-traps-private.h" #define DEBUG_PRINT_STATE 0 #define DEBUG_EVENTS 0 @@ -71,6 +73,7 @@ struct _cairo_bo_edge { cairo_edge_t edge; cairo_bo_edge_t *prev; cairo_bo_edge_t *next; + cairo_bo_edge_t *colinear; cairo_bo_trap_t deferred_trap; }; @@ -305,156 +308,6 @@ _slope_compare (const cairo_bo_edge_t *a, } } -/* - * We need to compare the x-coordinates of a pair of lines for a particular y, - * without loss of precision. - * - * The x-coordinate along an edge for a given y is: - * X = A_x + (Y - A_y) * A_dx / A_dy - * - * So the inequality we wish to test is: - * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, - * where ∘ is our inequality operator. - * - * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are - * all positive, so we can rearrange it thus without causing a sign change: - * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy - * - (Y - A_y) * A_dx * B_dy - * - * Given the assumption that all the deltas fit within 32 bits, we can compute - * this comparison directly using 128 bit arithmetic. For certain, but common, - * input we can reduce this down to a single 32 bit compare by inspecting the - * deltas. - * - * (And put the burden of the work on developing fast 128 bit ops, which are - * required throughout the tessellator.) - * - * See the similar discussion for _slope_compare(). - */ -static int -edges_compare_x_for_y_general (const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b, - int32_t y) -{ - /* XXX: We're assuming here that dx and dy will still fit in 32 - * bits. That's not true in general as there could be overflow. We - * should prevent that before the tessellation algorithm - * begins. - */ - int32_t dx; - int32_t adx, ady; - int32_t bdx, bdy; - enum { - HAVE_NONE = 0x0, - HAVE_DX = 0x1, - HAVE_ADX = 0x2, - HAVE_DX_ADX = HAVE_DX | HAVE_ADX, - HAVE_BDX = 0x4, - HAVE_DX_BDX = HAVE_DX | HAVE_BDX, - HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, - HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX - } have_dx_adx_bdx = HAVE_ALL; - - /* don't bother solving for abscissa if the edges' bounding boxes - * can be used to order them. */ - { - int32_t amin, amax; - int32_t bmin, bmax; - if (a->edge.line.p1.x < a->edge.line.p2.x) { - amin = a->edge.line.p1.x; - amax = a->edge.line.p2.x; - } else { - amin = a->edge.line.p2.x; - amax = a->edge.line.p1.x; - } - if (b->edge.line.p1.x < b->edge.line.p2.x) { - bmin = b->edge.line.p1.x; - bmax = b->edge.line.p2.x; - } else { - bmin = b->edge.line.p2.x; - bmax = b->edge.line.p1.x; - } - if (amax < bmin) return -1; - if (amin > bmax) return +1; - } - - ady = a->edge.line.p2.y - a->edge.line.p1.y; - adx = a->edge.line.p2.x - a->edge.line.p1.x; - if (adx == 0) - have_dx_adx_bdx &= ~HAVE_ADX; - - bdy = b->edge.line.p2.y - b->edge.line.p1.y; - bdx = b->edge.line.p2.x - b->edge.line.p1.x; - if (bdx == 0) - have_dx_adx_bdx &= ~HAVE_BDX; - - dx = a->edge.line.p1.x - b->edge.line.p1.x; - if (dx == 0) - have_dx_adx_bdx &= ~HAVE_DX; - -#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) -#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) -#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) - switch (have_dx_adx_bdx) { - default: - case HAVE_NONE: - return 0; - case HAVE_DX: - /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ - return dx; /* ady * bdy is positive definite */ - case HAVE_ADX: - /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ - return adx; /* bdy * (y - a->top.y) is positive definite */ - case HAVE_BDX: - /* 0 ∘ (Y - B_y) * B_dx * A_dy */ - return -bdx; /* ady * (y - b->top.y) is positive definite */ - case HAVE_ADX_BDX: - /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ - if ((adx ^ bdx) < 0) { - return adx; - } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ - cairo_int64_t adx_bdy, bdx_ady; - - /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ - - adx_bdy = _cairo_int32x32_64_mul (adx, bdy); - bdx_ady = _cairo_int32x32_64_mul (bdx, ady); - - return _cairo_int64_cmp (adx_bdy, bdx_ady); - } else - return _cairo_int128_cmp (A, B); - case HAVE_DX_ADX: - /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ - if ((-adx ^ dx) < 0) { - return dx; - } else { - cairo_int64_t ady_dx, dy_adx; - - ady_dx = _cairo_int32x32_64_mul (ady, dx); - dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); - - return _cairo_int64_cmp (ady_dx, dy_adx); - } - case HAVE_DX_BDX: - /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ - if ((bdx ^ dx) < 0) { - return dx; - } else { - cairo_int64_t bdy_dx, dy_bdx; - - bdy_dx = _cairo_int32x32_64_mul (bdy, dx); - dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); - - return _cairo_int64_cmp (bdy_dx, dy_bdx); - } - case HAVE_ALL: - /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ - return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); - } -#undef B -#undef A -#undef L -} /* * We need to compare the x-coordinate of a line for a particular y wrt to a @@ -508,81 +361,19 @@ edge_compare_for_y_against_x (const cairo_bo_edge_t *a, return _cairo_int64_cmp (L, R); } -static int -edges_compare_x_for_y (const cairo_bo_edge_t *a, - const cairo_bo_edge_t *b, - int32_t y) -{ - /* If the sweep-line is currently on an end-point of a line, - * then we know its precise x value (and considering that we often need to - * compare events at end-points, this happens frequently enough to warrant - * special casing). - */ - enum { - HAVE_NEITHER = 0x0, - HAVE_AX = 0x1, - HAVE_BX = 0x2, - HAVE_BOTH = HAVE_AX | HAVE_BX - } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; - - if (y == a->edge.line.p1.y) - ax = a->edge.line.p1.x; - else if (y == a->edge.line.p2.y) - ax = a->edge.line.p2.x; - else - have_ax_bx &= ~HAVE_AX; - - if (y == b->edge.line.p1.y) - bx = b->edge.line.p1.x; - else if (y == b->edge.line.p2.y) - bx = b->edge.line.p2.x; - else - have_ax_bx &= ~HAVE_BX; - - switch (have_ax_bx) { - default: - case HAVE_NEITHER: - return edges_compare_x_for_y_general (a, b, y); - case HAVE_AX: - return -edge_compare_for_y_against_x (b, y, ax); - case HAVE_BX: - return edge_compare_for_y_against_x (a, y, bx); - case HAVE_BOTH: - return ax - bx; - } -} - static inline int -_line_equal (const cairo_line_t *a, const cairo_line_t *b) -{ - return a->p1.x == b->p1.x && a->p1.y == b->p1.y && - a->p2.x == b->p2.x && a->p2.y == b->p2.y; -} - -static int -_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, +_cairo_bo_sweep_line_compare_edges (const cairo_bo_sweep_line_t *sweep_line, const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) { int cmp; - /* compare the edges if not identical */ - if (! _line_equal (&a->edge.line, &b->edge.line)) { - cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); - if (cmp) + cmp = _cairo_lines_compare_at_y (&a->edge.line, + &b->edge.line, + sweep_line->current_y); + if (cmp) return cmp; - /* The two edges intersect exactly at y, so fall back on slope - * comparison. We know that this compare_edges function will be - * called only when starting a new edge, (not when stopping an - * edge), so we don't have to worry about conditionally inverting - * the sense of _slope_compare. */ - cmp = _slope_compare (a, b); - if (cmp) - return cmp; - } - /* We've got two collinear edges now. */ return b->edge.bottom - a->edge.bottom; } @@ -1040,9 +831,6 @@ _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, cairo_bo_event_t **start_events, int num_events) { - _cairo_bo_event_queue_sort (start_events, num_events); - start_events[num_events] = NULL; - event_queue->start_events = start_events; _cairo_freepool_init (&event_queue->pool, @@ -1080,7 +868,11 @@ _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_ { cairo_bo_point32_t intersection; - if (_line_equal (&left->edge.line, &right->edge.line)) + if (MAX (left->edge.line.p1.x, left->edge.line.p2.x) <= + MIN (right->edge.line.p1.x, right->edge.line.p2.x)) + return CAIRO_STATUS_SUCCESS; + + if (cairo_lines_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS; /* The names "left" and "right" here are correct descriptions of @@ -1109,7 +901,7 @@ _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) sweep_line->current_edge = NULL; } -static cairo_status_t +static void _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, cairo_bo_edge_t *edge) { @@ -1162,11 +954,10 @@ _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, } } else { sweep_line->head = edge; + edge->next = NULL; } sweep_line->current_edge = edge; - - return CAIRO_STATUS_SUCCESS; } static void @@ -1300,33 +1091,61 @@ event_log (const char *fmt, ...) } #endif -static inline cairo_bool_t -edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) -{ - if (_line_equal (&a->edge.line, &b->edge.line)) - return TRUE; +#define HAS_COLINEAR(a, b) ((cairo_bo_edge_t *)(((uintptr_t)(a))&~1) == (b)) +#define IS_COLINEAR(e) (((uintptr_t)(e))&1) +#define MARK_COLINEAR(e, v) ((cairo_bo_edge_t *)(((uintptr_t)(e))|(v))) - if (_slope_compare (a, b)) +static inline cairo_bool_t +edges_colinear (cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + unsigned p; + + if (HAS_COLINEAR(a->colinear, b)) + return IS_COLINEAR(a->colinear); + + if (HAS_COLINEAR(b->colinear, a)) { + p = IS_COLINEAR(b->colinear); + a->colinear = MARK_COLINEAR(b, p); + return p; + } + + p = 0; + p |= (a->edge.line.p1.x == b->edge.line.p1.x) << 0; + p |= (a->edge.line.p1.y == b->edge.line.p1.y) << 1; + p |= (a->edge.line.p2.x == b->edge.line.p2.x) << 3; + p |= (a->edge.line.p2.y == b->edge.line.p2.y) << 4; + if (p == ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 4))) { + a->colinear = MARK_COLINEAR(b, 1); + return TRUE; + } + + if (_slope_compare (a, b)) { + a->colinear = MARK_COLINEAR(b, 0); return FALSE; + } /* The choice of y is not truly arbitrary since we must guarantee that it * is greater than the start of either line. */ - if (a->edge.line.p1.y == b->edge.line.p1.y) { - return a->edge.line.p1.x == b->edge.line.p1.x; + if (p != 0) { + /* colinear if either end-point are coincident */ + p = (((p >> 1) & p) & 5) != 0; } else if (a->edge.line.p1.y < b->edge.line.p1.y) { - return edge_compare_for_y_against_x (b, - a->edge.line.p1.y, - a->edge.line.p1.x) == 0; + p = edge_compare_for_y_against_x (b, + a->edge.line.p1.y, + a->edge.line.p1.x) == 0; } else { - return edge_compare_for_y_against_x (a, - b->edge.line.p1.y, - b->edge.line.p1.x) == 0; + p = edge_compare_for_y_against_x (a, + b->edge.line.p1.y, + b->edge.line.p1.x) == 0; } + + a->colinear = MARK_COLINEAR(b, p); + return p; } /* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */ -static cairo_status_t +static void _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, int32_t bot, cairo_traps_t *traps) @@ -1358,8 +1177,6 @@ _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, } trap->right = NULL; - - return _cairo_traps_status (traps); } @@ -1368,31 +1185,28 @@ _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, * then either add it to the traps in `traps', if the trapezoid's * right edge differs from `edge->next', or do nothing if the new * trapezoid would be a continuation of the existing one. */ -static inline cairo_status_t +static inline void _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, cairo_bo_edge_t *right, int top, cairo_traps_t *traps) { - cairo_status_t status; - if (left->deferred_trap.right == right) - return CAIRO_STATUS_SUCCESS; + return; + assert (right); if (left->deferred_trap.right != NULL) { - if (right != NULL && edges_colinear (left->deferred_trap.right, right)) + if (edges_colinear (left->deferred_trap.right, right)) { /* continuation on right, so just swap edges */ left->deferred_trap.right = right; - return CAIRO_STATUS_SUCCESS; + return; } - status = _cairo_bo_edge_end_trap (left, top, traps); - if (unlikely (status)) - return status; + _cairo_bo_edge_end_trap (left, top, traps); } - if (right != NULL && ! edges_colinear (left, right)) { + if (! edges_colinear (left, right)) { left->deferred_trap.top = top; left->deferred_trap.right = right; @@ -1403,134 +1217,66 @@ _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, top); #endif } - - return CAIRO_STATUS_SUCCESS; } -static inline cairo_status_t -_active_edges_to_traps (cairo_bo_edge_t *left, - int32_t top, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps) +static inline void +_active_edges_to_traps (cairo_bo_edge_t *pos, + int32_t top, + unsigned mask, + cairo_traps_t *traps) { - cairo_bo_edge_t *right; - cairo_status_t status; + cairo_bo_edge_t *left; + int in_out; + #if DEBUG_PRINT_STATE printf ("Processing active edges for %x\n", top); #endif - if (fill_rule == CAIRO_FILL_RULE_WINDING) { - while (left != NULL) { - int in_out; - - /* Greedily search for the closing edge, so that we generate the - * maximal span width with the minimal number of trapezoids. + in_out = 0; + left = pos; + while (pos != NULL) { + if (pos != left && pos->deferred_trap.right) { + /* XXX It shouldn't be possible to here with 2 deferred traps + * on colinear edges... See bug-bo-rictoz. */ - in_out = left->edge.dir; - - /* Check if there is a co-linear edge with an existing trap */ - right = left->next; - if (left->deferred_trap.right == NULL) { - while (right != NULL && right->deferred_trap.right == NULL) - right = right->next; - - if (right != NULL && edges_colinear (left, right)) { - /* continuation on left */ - left->deferred_trap = right->deferred_trap; - right->deferred_trap.right = NULL; - } + if (left->deferred_trap.right == NULL && + edges_colinear (left, pos)) + { + /* continuation on left */ + left->deferred_trap = pos->deferred_trap; + pos->deferred_trap.right = NULL; } - - /* End all subsumed traps */ - right = left->next; - while (right != NULL) { - if (right->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (right, top, traps); - if (unlikely (status)) - return status; - } - - in_out += right->edge.dir; - if (in_out == 0) { - cairo_bo_edge_t *next; - cairo_bool_t skip = FALSE; - - /* skip co-linear edges */ - next = right->next; - if (next != NULL) - skip = edges_colinear (right, next); - - if (! skip) - break; - } - - right = right->next; + else + { + _cairo_bo_edge_end_trap (pos, top, traps); } - - status = _cairo_bo_edge_start_or_continue_trap (left, right, - top, traps); - if (unlikely (status)) - return status; - - left = right; - if (left != NULL) - left = left->next; } - } else { - while (left != NULL) { - int in_out = 0; - right = left->next; - while (right != NULL) { - if (right->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (right, top, traps); - if (unlikely (status)) - return status; - } - - if ((in_out++ & 1) == 0) { - cairo_bo_edge_t *next; - cairo_bool_t skip = FALSE; - - /* skip co-linear edges */ - next = right->next; - if (next != NULL) - skip = edges_colinear (right, next); - - if (! skip) - break; - } - - right = right->next; + in_out += pos->edge.dir; + if ((in_out & mask) == 0) { + /* skip co-linear edges */ + if (pos->next == NULL || ! edges_colinear (pos, pos->next)) { + _cairo_bo_edge_start_or_continue_trap (left, pos, top, traps); + left = pos->next; } - - status = _cairo_bo_edge_start_or_continue_trap (left, right, - top, traps); - if (unlikely (status)) - return status; - - left = right; - if (left != NULL) - left = left->next; } + + pos = pos->next; } - - return CAIRO_STATUS_SUCCESS; } - /* Execute a single pass of the Bentley-Ottmann algorithm on edges, * generating trapezoids according to the fill_rule and appending them * to traps. */ static cairo_status_t _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, int num_events, - cairo_fill_rule_t fill_rule, + unsigned fill_rule, cairo_traps_t *traps, int *num_intersections) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ + cairo_status_t status; int intersection_count = 0; cairo_bo_event_queue_t event_queue; cairo_bo_sweep_line_t sweep_line; @@ -1538,6 +1284,12 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, cairo_bo_edge_t *left, *right; cairo_bo_edge_t *e1, *e2; + /* convert the fill_rule into a winding mask */ + if (fill_rule == CAIRO_FILL_RULE_WINDING) + fill_rule = (unsigned) -1; + else + fill_rule = 1; + #if DEBUG_EVENTS { int i; @@ -1565,20 +1317,16 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, if (event->point.y != sweep_line.current_y) { for (e1 = sweep_line.stopped; e1; e1 = e1->next) { if (e1->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (e1, - e1->edge.bottom, - traps); - if (unlikely (status)) - goto unwind; + _cairo_bo_edge_end_trap (e1, + e1->edge.bottom, + traps); } } sweep_line.stopped = NULL; - status = _active_edges_to_traps (sweep_line.head, - sweep_line.current_y, - fill_rule, traps); - if (unlikely (status)) - goto unwind; + _active_edges_to_traps (sweep_line.head, + sweep_line.current_y, + fill_rule, traps); sweep_line.current_y = event->point.y; } @@ -1596,9 +1344,7 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, case CAIRO_BO_EVENT_TYPE_START: e1 = &((cairo_bo_start_event_t *) event)->edge; - status = _cairo_bo_sweep_line_insert (&sweep_line, e1); - if (unlikely (status)) - goto unwind; + _cairo_bo_sweep_line_insert (&sweep_line, e1); status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); if (unlikely (status)) @@ -1701,11 +1447,10 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, *num_intersections = intersection_count; for (e1 = sweep_line.stopped; e1; e1 = e1->next) { if (e1->deferred_trap.right != NULL) { - status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); - if (unlikely (status)) - break; + _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); } } + status = traps->status; unwind: _cairo_bo_event_queue_fini (&event_queue); @@ -1722,18 +1467,33 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { int intersections; - cairo_status_t status; cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; cairo_bo_start_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; - int num_events; - int i; + cairo_bo_start_event_t *stack_event_y[64]; + cairo_bo_start_event_t **event_y = NULL; + int i, num_events, y, ymin, ymax; + cairo_status_t status; num_events = polygon->num_edges; if (unlikely (0 == num_events)) return CAIRO_STATUS_SUCCESS; + if (polygon->num_limits) { + ymin = _cairo_fixed_integer_floor (polygon->limit.p1.y); + ymax = _cairo_fixed_integer_ceil (polygon->limit.p2.y) - ymin; + + if (ymax > 64) { + event_y = _cairo_malloc_ab(sizeof (cairo_bo_event_t*), ymax); + if (unlikely (event_y == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + event_y = stack_event_y; + } + memset (event_y, 0, ymax * sizeof(cairo_bo_event_t *)); + } + events = stack_events; event_ptrs = stack_event_ptrs; if (num_events > ARRAY_LENGTH (stack_events)) { @@ -1741,15 +1501,16 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, sizeof (cairo_bo_start_event_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); - if (unlikely (events == NULL)) + if (unlikely (events == NULL)) { + if (event_y != stack_event_y) + free (event_y); return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } event_ptrs = (cairo_bo_event_t **) (events + num_events); } for (i = 0; i < num_events; i++) { - event_ptrs[i] = (cairo_bo_event_t *) &events[i]; - events[i].type = CAIRO_BO_EVENT_TYPE_START; events[i].point.y = polygon->edges[i].top; events[i].point.x = @@ -1760,8 +1521,31 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, events[i].edge.deferred_trap.right = NULL; events[i].edge.prev = NULL; events[i].edge.next = NULL; + events[i].edge.colinear = NULL; + + if (event_y) { + y = _cairo_fixed_integer_floor (events[i].point.y) - ymin; + events[i].edge.next = (cairo_bo_edge_t *) event_y[y]; + event_y[y] = (cairo_bo_start_event_t *) &events[i]; + } else + event_ptrs[i] = (cairo_bo_event_t *) &events[i]; } + if (event_y) { + for (y = i = 0; y < ymax && i < num_events; y++) { + cairo_bo_start_event_t *e; + int j = i; + for (e = event_y[y]; e; e = (cairo_bo_start_event_t *)e->edge.next) + event_ptrs[i++] = (cairo_bo_event_t *) e; + if (i > j + 1) + _cairo_bo_event_queue_sort (event_ptrs+j, i-j); + } + if (event_y != stack_event_y) + free (event_y); + } else + _cairo_bo_event_queue_sort (event_ptrs, i); + event_ptrs[i] = NULL; + #if DEBUG_TRAPS dump_edges (events, num_events, "bo-polygon-edges.txt"); #endif @@ -1770,8 +1554,7 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, * passes of the Bentley-Ottmann algorithm. It would merely * require storing the results of each pass into a temporary * cairo_traps_t. */ - status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, - num_events, + status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events, fill_rule, traps, &intersections); #if DEBUG_TRAPS @@ -1799,8 +1582,7 @@ _cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, dump_traps (traps, "bo-traps-in.txt"); #endif - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); + _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); for (i = 0; i < traps->num_traps; i++) { status = _cairo_polygon_add_line (&polygon, diff --git a/gfx/cairo/cairo/src/cairo-beos-surface.cpp b/gfx/cairo/cairo/src/cairo-beos-surface.cpp index 5244334424a5..65db0b97acce 100644 --- a/gfx/cairo/cairo/src/cairo-beos-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-beos-surface.cpp @@ -1,4 +1,4 @@ -/* vim:set ts=8 sw=2 noet cin: */ +/* vim:set ts=8 sw=4 noet cin: */ /* cairo - a vector graphics library with display and print output * * Copyright © 2005 Christian Biesinger @@ -13,7 +13,7 @@ * * 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 + * 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 * @@ -40,6 +40,9 @@ #include "cairo-beos.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" + #include #include @@ -51,11 +54,23 @@ #include #include +/** + * SECTION:beos-surface + * @Title: BeOS Surfaces + * @Short_Description: BeOS surface support + * @See_Also: #cairo_surface_t + * + * The BeOS surface is used to render cairo graphics to BeOS views + * and bitmaps. + **/ + #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS) struct cairo_beos_surface_t { cairo_surface_t base; + cairo_region_t *clip_region; + BView* view; /* @@ -70,7 +85,6 @@ struct cairo_beos_surface_t { BBitmap* bitmap; - // If true, surface and view should be deleted when this surface is // destroyed bool owns_bitmap_view; @@ -101,27 +115,28 @@ _cairo_beos_surface_create_internal (BView* view, BBitmap* bmp, bool owns_bitmap_view = false); -static BRect -_cairo_rect_to_brect (const cairo_rectangle_int16_t* rect) +static inline BRect +_cairo_rectangle_to_brect (const cairo_rectangle_int_t* rect) { // A BRect is one pixel wider than you'd think - return BRect(rect->x, rect->y, rect->x + rect->width - 1, - rect->y + rect->height - 1); + return BRect (rect->x, rect->y, + rect->x + rect->width - 1, + rect->y + rect->height - 1); } -static cairo_rectangle_int16_t -_brect_to_cairo_rect (const BRect& rect) +static inline cairo_rectangle_int_t +_brect_to_cairo_rectangle (const BRect &rect) { - cairo_rectangle_int16_t retval; - retval.x = int(rect.left + 0.5); - retval.y = int(rect.top + 0.5); - retval.width = rect.IntegerWidth() + 1; - retval.height = rect.IntegerHeight() + 1; + cairo_rectangle_int_t retval; + retval.x = floor (rect.left); + retval.y = floor (rect.top); + retval.width = ceil (rect.right) - retval.x + 1; + retval.height = ceil (rect.bottom) - rectval.y + 1; return retval; } -static rgb_color -_cairo_color_to_be_color (const cairo_color_t* color) +static inline rgb_color +_cairo_color_to_be_color (const cairo_color_t *color) { // This factor ensures a uniform distribution of numbers const float factor = 256 - 1e-5; @@ -199,32 +214,8 @@ _cairo_beos_view_to_bitmap (BView* view, return ERROR; } -inline unsigned char -unpremultiply (unsigned char color, - unsigned char alpha) -{ - if (alpha == 0) - return 0; - // plus alpha/2 to round instead of truncate - return (color * 255 + alpha / 2) / alpha; -} - -inline unsigned char -premultiply (unsigned char color, - unsigned char alpha) -{ - // + 127 to round, instead of truncate - return (color * alpha + 127) / 255; -} - -/** - * unpremultiply_rgba: - * - * Takes an input in ABGR premultiplied image data and unmultiplies it. - * The result is stored in retdata. - **/ static void -unpremultiply_rgba (unsigned char* data, +unpremultiply_bgra (unsigned char* data, int width, int height, int stride, @@ -235,52 +226,108 @@ unpremultiply_rgba (unsigned char* data, in < end; in += stride, out += stride) { - for (int i = 0; i < width; ++i) { - // XXX for a big-endian platform this'd have to change - int idx = 4 * i; - unsigned char alpha = in[idx + 3]; - out[idx + 0] = unpremultiply(in[idx + 0], alpha); // B - out[idx + 1] = unpremultiply(in[idx + 1], alpha); // G - out[idx + 2] = unpremultiply(in[idx + 2], alpha); // R - out[idx + 3] = in[idx + 3]; // Alpha + for (int i = 0; i < width; i ++) { + uint8_t *b = &out[4*i]; + uint32_t pixel; + uint8_t alpha; + + memcpy (&pixel, &data[4*i], sizeof (uint32_t)); + alpha = pixel & 0xff; + if (alpha == 0) { + b[0] = b[1] = b[2] = b[3] = 0; + } else { + b[0] = (((pixel >> 24) & 0xff) * 255 + alpha / 2) / alpha; + b[1] = (((pixel >> 16) & 0xff) * 255 + alpha / 2) / alpha; + b[2] = (((pixel >> 8) & 0xff) * 255 + alpha / 2) / alpha; + b[3] = alpha; + } } } } -/** - * premultiply_rgba: - * - * Takes an input in ABGR non-premultiplied image data and premultiplies it. - * The returned data must be freed with free(). - **/ +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + static unsigned char* -premultiply_rgba (unsigned char* data, +premultiply_bgra (unsigned char* data, int width, int height, int stride) { - unsigned char* retdata = reinterpret_cast(_cairo_malloc_ab(height, stride)); + uint8_t * retdata = reinterpret_cast(_cairo_malloc_ab(height, stride)); if (!retdata) return NULL; - unsigned char* end = data + stride * height; - for (unsigned char* in = data, *out = retdata; + uint8_t * end = data + stride * height; + for (uint8_t * in = data, *out = retdata; in < end; in += stride, out += stride) { - for (int i = 0; i < width; ++i) { - // XXX for a big-endian platform this'd have to change - int idx = 4 * i; - unsigned char alpha = in[idx + 3]; - out[idx + 0] = premultiply(in[idx + 0], alpha); // B - out[idx + 1] = premultiply(in[idx + 1], alpha); // G - out[idx + 2] = premultiply(in[idx + 2], alpha); // R - out[idx + 3] = in[idx + 3]; // Alpha + for (int i = 0; i < width; i ++) { + uint8_t *base = &in[4*i]; + uint8_t alpha = base[3]; + uint32_t p; + + if (alpha == 0) { + p = 0; + } else { + uint8_t blue = base[0]; + uint8_t green = base[1]; + uint8_t red = base[2]; + + if (alpha != 0xff) { + blue = multiply_alpha (alpha, blue); + green = multiply_alpha (alpha, green); + red = multiply_alpha (alpha, red); + } + p = (alpha << 0) | (red << 8) | (green << 16) | ((uint32_t)blue << 24); + } + memcpy (&out[4*i], &p, sizeof (uint32_t)); } } return retdata; } +static cairo_int_status_t +_cairo_beos_surface_set_clip_region (cairo_beos_surface_t *surface, + cairo_region_t *region) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + AutoLockView locker(surface->view); + assert (locker); + + if (region == surface->clip_region) + return CAIRO_INT_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (region); + + if (region == NULL) { + // No clipping + surface->view->ConstrainClippingRegion(NULL); + return CAIRO_INT_STATUS_SUCCESS; + } + + int count = cairo_region_num_rectangles (region); + BRegion bregion; + for (int i = 0; i < count; ++i) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + // Have to subtract one, because for pixman, the second coordinate + // lies outside the rectangle. + bregion.Include (_cairo_rectangle_to_brect (&rect)); + } + surface->view->ConstrainClippingRegion(&bregion); + return CAIRO_INT_STATUS_SUCCESS; +} + + /** * _cairo_beos_bitmap_to_surface: * @@ -309,8 +356,8 @@ _cairo_beos_bitmap_to_surface (BBitmap* bitmap) return imgsurf; } - cairo_format_t cformat = format == B_RGB32 ? CAIRO_FORMAT_RGB24 - : CAIRO_FORMAT_ARGB32; + cairo_format_t cformat = format == B_RGB32 ? + CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; BRect bounds(bitmap->Bounds()); unsigned char* bits = reinterpret_cast(bitmap->Bits()); @@ -318,8 +365,8 @@ _cairo_beos_bitmap_to_surface (BBitmap* bitmap) int height = bounds.IntegerHeight() + 1; unsigned char* premultiplied; if (cformat == CAIRO_FORMAT_ARGB32) { - premultiplied = premultiply_rgba(bits, width, height, - bitmap->BytesPerRow()); + premultiplied = premultiply_bgra (bits, width, height, + bitmap->BytesPerRow()); } else { premultiplied = reinterpret_cast( _cairo_malloc_ab(bitmap->BytesPerRow(), height)); @@ -327,7 +374,7 @@ _cairo_beos_bitmap_to_surface (BBitmap* bitmap) memcpy(premultiplied, bits, bitmap->BytesPerRow() * height); } if (!premultiplied) - return NULL; + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); cairo_image_surface_t* surf = reinterpret_cast (cairo_image_surface_create_for_data(premultiplied, @@ -355,11 +402,11 @@ _cairo_image_surface_to_bitmap (cairo_image_surface_t* surface) switch (surface->format) { case CAIRO_FORMAT_ARGB32: { BBitmap* data = new BBitmap(size, B_RGBA32); - unpremultiply_rgba(surface->data, - surface->width, - surface->height, - surface->stride, - reinterpret_cast(data->Bits())); + unpremultiply_bgra (surface->data, + surface->width, + surface->height, + surface->stride, + reinterpret_cast(data->Bits())); return data; } case CAIRO_FORMAT_RGB24: { @@ -384,44 +431,44 @@ _cairo_op_to_be_op (cairo_operator_t cairo_op, drawing_mode* beos_op) { switch (cairo_op) { + case CAIRO_OPERATOR_SOURCE: + *beos_op = B_OP_COPY; + return true; + case CAIRO_OPERATOR_OVER: + *beos_op = B_OP_ALPHA; + return true; - case CAIRO_OPERATOR_SOURCE: - *beos_op = B_OP_COPY; - return true; - case CAIRO_OPERATOR_OVER: - *beos_op = B_OP_ALPHA; - return true; - - case CAIRO_OPERATOR_ADD: - // Does not actually work + case CAIRO_OPERATOR_ADD: + // Does not actually work + // XXX This is a fundamental compositing operator, it has to work! #if 1 - return false; + return false; #else - *beos_op = B_OP_ADD; - return true; + *beos_op = B_OP_ADD; + return true; #endif - case CAIRO_OPERATOR_CLEAR: - // Does not map to B_OP_ERASE - it replaces the dest with the low - // color, instead of transparency; could be done by setting low - // color appropriately. + case CAIRO_OPERATOR_CLEAR: + // Does not map to B_OP_ERASE - it replaces the dest with the low + // color, instead of transparency; could be done by setting low + // color appropriately. - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_SATURATE: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_SATURATE: - default: - return false; - }; + default: + return false; + } } static cairo_surface_t * @@ -430,8 +477,6 @@ _cairo_beos_surface_create_similar (void *abstract_surface, int width, int height) { - fprintf(stderr, "Creating similar\n"); - cairo_beos_surface_t *surface = reinterpret_cast( abstract_surface); @@ -444,9 +489,7 @@ _cairo_beos_surface_create_similar (void *abstract_surface, BBitmap* bmp; switch (content) { case CAIRO_CONTENT_ALPHA: - // Can't support this natively - return _cairo_image_surface_create_with_content(content, width, - height); + return NULL; case CAIRO_CONTENT_COLOR_ALPHA: bmp = new BBitmap(rect, B_RGBA32, true); break; @@ -470,10 +513,9 @@ _cairo_beos_surface_create_similar (void *abstract_surface, } break; default: - assert(0); + ASSERT_NOT_REACHED; return NULL; - - }; + } BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0); bmp->AddChild(view); return _cairo_beos_surface_create_internal(view, bmp, true); @@ -495,6 +537,8 @@ _cairo_beos_surface_finish (void *abstract_surface) surface->bitmap = NULL; } + cairo_region_destroy (surface->clip_region); + return CAIRO_STATUS_SUCCESS; } @@ -503,7 +547,6 @@ _cairo_beos_surface_acquire_source_image (void *abstract_surfa cairo_image_surface_t **image_out, void **image_extra) { - fprintf(stderr, "Getting source image\n"); cairo_beos_surface_t *surface = reinterpret_cast( abstract_surface); AutoLockView locker(surface->view); @@ -514,9 +557,9 @@ _cairo_beos_surface_acquire_source_image (void *abstract_surfa surface->view->Sync(); if (surface->bitmap) { - *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap); - if (!*image_out) - return CAIRO_STATUS_NO_MEMORY; + *image_out = _cairo_beos_bitmap_to_surface (surface->bitmap); + if (unlikely ((*image_out)->base.status)) + return (*image_out)->base.status; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; @@ -526,10 +569,10 @@ _cairo_beos_surface_acquire_source_image (void *abstract_surfa if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK) return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE - *image_out = _cairo_beos_bitmap_to_surface(bmp); - if (!*image_out) { + *image_out = _cairo_beos_bitmap_to_surface (bmp); + if (unlikely ((*image_out)->base.status)) { delete bmp; - return CAIRO_STATUS_NO_MEMORY; + return (*image_out)->base.status; } *image_extra = bmp; @@ -543,17 +586,17 @@ _cairo_beos_surface_release_source_image (void *abstract_surfac { cairo_surface_destroy (&image->base); - BBitmap* bmp = static_cast(image_extra); - delete bmp; + if (image_extra != NULL) { + BBitmap* bmp = static_cast(image_extra); + delete bmp; + } } - - static cairo_status_t _cairo_beos_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int16_t *interest_rect, + cairo_rectangle_int_t *interest_rect, cairo_image_surface_t **image_out, - cairo_rectangle_int16_t *image_rect, + cairo_rectangle_int_t *image_rect, void **image_extra) { cairo_beos_surface_t *surface = reinterpret_cast( @@ -563,14 +606,14 @@ _cairo_beos_surface_acquire_dest_image (void *abstract_surface, if (!locker) { *image_out = NULL; *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; + return (cairo_status_t) CAIRO_INT_STATUS_NOTHING_TO_DO; } if (surface->bitmap) { surface->view->Sync(); *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap); - if (!*image_out) - return CAIRO_STATUS_NO_MEMORY; + if (unlikely ((*image_out)->base.status)) + return (*image_out)->base.status; image_rect->x = 0; image_rect->y = 0; @@ -581,7 +624,7 @@ _cairo_beos_surface_acquire_dest_image (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } - BRect b_interest_rect(_cairo_rect_to_brect(interest_rect)); + BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect)); BRect rect; BBitmap* bitmap; @@ -595,18 +638,11 @@ _cairo_beos_surface_acquire_dest_image (void *abstract_surface, if (status == ERROR) return CAIRO_STATUS_NO_MEMORY; - *image_rect = _brect_to_cairo_rect(rect); - -#if 0 - fprintf(stderr, "Requested: (cairo rects) (%ix%i) dim (%u, %u) returning (%ix%i) dim (%u, %u)\n", - interest_rect->x, interest_rect->y, interest_rect->width, interest_rect->height, - image_rect->x, image_rect->y, image_rect->width, image_rect->height); -#endif - + *image_rect = _brect_to_cairo_rectangle(rect); *image_out = _cairo_beos_bitmap_to_surface(bitmap); delete bitmap; - if (!*image_out) - return CAIRO_STATUS_NO_MEMORY; + if (unlikely ((*image_out)->base.status)) + return (*image_out)->base.status; *image_extra = NULL; @@ -616,13 +652,11 @@ _cairo_beos_surface_acquire_dest_image (void *abstract_surface, static void _cairo_beos_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int16_t *intersect_rect, + cairo_rectangle_int_t *intersect_rect, cairo_image_surface_t *image, - cairo_rectangle_int16_t *image_rect, + cairo_rectangle_int_t *image_rect, void *image_extra) { - fprintf(stderr, "Fallback drawing\n"); - cairo_beos_surface_t *surface = reinterpret_cast( abstract_surface); @@ -634,9 +668,9 @@ _cairo_beos_surface_release_dest_image (void *abstract_surface, surface->view->PushState(); surface->view->SetDrawingMode(B_OP_COPY); - BRect rect(_cairo_rect_to_brect(image_rect)); - surface->view->DrawBitmap(bitmap_to_draw, rect); + surface->view->DrawBitmap (bitmap_to_draw, + _cairo_rectangle_to_brect (image_rect)); surface->view->PopState(); @@ -649,17 +683,19 @@ _cairo_beos_surface_composite (cairo_operator_t op, cairo_pattern_t *src, cairo_pattern_t *mask, void *dst, - int src_x, + int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, - unsigned int height) + unsigned int height, + cairo_region_t *clip_region) { cairo_beos_surface_t *surface = reinterpret_cast( dst); + cairo_int_status_t status; AutoLockView locker(surface->view); if (!locker) return CAIRO_INT_STATUS_SUCCESS; @@ -684,6 +720,10 @@ _cairo_beos_surface_composite (cairo_operator_t op, if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity)) return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_beos_surface_set_clip_region (surface, clip_region); + if (unlikely (status)) + return status; + BRect srcRect(src_x + itx, src_y + ity, src_x + itx + width - 1, @@ -731,8 +771,6 @@ _cairo_beos_surface_composite (cairo_operator_t op, return CAIRO_INT_STATUS_UNSUPPORTED; } - fprintf(stderr, "Composite\n"); - // Draw it on screen. surface->view->PushState(); @@ -767,24 +805,17 @@ _cairo_beos_surface_composite (cairo_operator_t op, } -static void -_cairo_beos_surface_fill_rectangle (cairo_beos_surface_t *surface, - cairo_rectangle_int16_t *rect) -{ - BRect brect(_cairo_rect_to_brect(rect)); - surface->view->FillRect(brect); -} - static cairo_int_status_t _cairo_beos_surface_fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, - cairo_rectangle_int16_t *rects, - int num_rects) + cairo_rectangle_int_t *rects, + int num_rects, + cairo_region_t *clip_region) { - fprintf(stderr, "Drawing %i rectangles\n", num_rects); cairo_beos_surface_t *surface = reinterpret_cast( abstract_surface); + cairo_int_status_t status; if (num_rects <= 0) return CAIRO_INT_STATUS_SUCCESS; @@ -797,6 +828,10 @@ _cairo_beos_surface_fill_rectangles (void *abstract_surface, if (!_cairo_op_to_be_op(op, &mode)) return CAIRO_INT_STATUS_UNSUPPORTED; + status = _cairo_beos_surface_set_clip_region (surface, clip_region); + if (unlikely (status)) + return status; + rgb_color be_color = _cairo_color_to_be_color(color); if (mode == B_OP_ALPHA && be_color.alpha == 0xFF) @@ -808,9 +843,9 @@ _cairo_beos_surface_fill_rectangles (void *abstract_surface, if (mode == B_OP_COPY && be_color.alpha != 0xFF && (!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32)) { - be_color.red = premultiply(be_color.red, be_color.alpha); - be_color.green = premultiply(be_color.green, be_color.alpha); - be_color.blue = premultiply(be_color.blue, be_color.alpha); + be_color.red = color->red_short >> 8; + be_color.green = color->green_short >> 8; + be_color.blue = color->blue_short >> 8; } surface->view->PushState(); @@ -822,65 +857,26 @@ _cairo_beos_surface_fill_rectangles (void *abstract_surface, else surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); - for (int i = 0; i < num_rects; ++i) { - _cairo_beos_surface_fill_rectangle(surface, &rects[i]); - } + for (int i = 0; i < num_rects; ++i) + surface->view->FillRect (_cairo_rectangle_to_brect (&rects[i])); surface->view->PopState(); return CAIRO_INT_STATUS_SUCCESS; } - - -static cairo_int_status_t -_cairo_beos_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - fprintf(stderr, "Setting clip region\n"); - cairo_beos_surface_t *surface = reinterpret_cast( - abstract_surface); - AutoLockView locker(surface->view); - if (!locker) - return CAIRO_INT_STATUS_SUCCESS; - - if (region == NULL) { - // No clipping - surface->view->ConstrainClippingRegion(NULL); - return CAIRO_INT_STATUS_SUCCESS; - } - - int count = pixman_region_num_rects(region); - pixman_box16_t* rects = pixman_region_rects(region); - BRegion bregion; - for (int i = 0; i < count; ++i) { - // Have to substract one, because for pixman, the second coordinate - // lies outside the rectangle. - bregion.Include(BRect(rects[i].x1, rects[i].y1, rects[i].x2 - 1, rects[i].y2 - 1)); - } - surface->view->ConstrainClippingRegion(&bregion); - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t +static cairo_bool_t _cairo_beos_surface_get_extents (void *abstract_surface, - cairo_rectangle_int16_t *rectangle) + cairo_rectangle_int_t *rectangle) { cairo_beos_surface_t *surface = reinterpret_cast( abstract_surface); AutoLockView locker(surface->view); if (!locker) - return CAIRO_INT_STATUS_UNSUPPORTED; + return FALSE; - BRect size = surface->view->Bounds(); - - *rectangle = _brect_to_cairo_rect(size); - - // Make sure to have our upperleft edge as (0,0) - rectangle->x = 0; - rectangle->y = 0; - - return CAIRO_INT_STATUS_SUCCESS; + *rectangle = _brect_to_cairo_rectangle (surface->view->Bounds()); + return TRUE; } static const struct _cairo_surface_backend cairo_beos_surface_backend = { @@ -899,8 +895,6 @@ static const struct _cairo_surface_backend cairo_beos_surface_backend = { NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ - _cairo_beos_surface_set_clip_region, - NULL, /* intersect_clip_path */ _cairo_beos_surface_get_extents, NULL, /* old_show_glyphs */ NULL, /* get_font_options */ @@ -932,13 +926,18 @@ _cairo_beos_surface_create_internal (BView* view, cairo_content_t content = CAIRO_CONTENT_COLOR; if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15)) content = CAIRO_CONTENT_COLOR_ALPHA; - _cairo_surface_init(&surface->base, &cairo_beos_surface_backend, content); + _cairo_surface_init (&surface->base, + &cairo_beos_surface_backend, + NULL, /* device */ + content); surface->view = view; surface->bitmap = bmp; surface->owns_bitmap_view = owns_bitmap_view; - return (cairo_surface_t *) surface; + surface->clip_region = NULL; + + return &surface->base; } /** @@ -949,6 +948,8 @@ _cairo_beos_surface_create_internal (BView* view, * The caller must ensure that the view does not get deleted before the surface. * If the view is attached to a bitmap rather than an on-screen window, use * cairo_beos_surface_create_for_bitmap() instead of this function. + * + * Since: TBD **/ cairo_surface_t * cairo_beos_surface_create (BView* view) @@ -972,6 +973,8 @@ cairo_beos_surface_create (BView* view) * * For now, only views that draw to the entire area of bmp are supported. * The view must already be attached to the bitmap. + * + * Since: TBD **/ cairo_surface_t * cairo_beos_surface_create_for_bitmap (BView* view, diff --git a/gfx/cairo/cairo/src/cairo-botor-scan-converter.c b/gfx/cairo/cairo/src/cairo-botor-scan-converter.c index 0778a5dcd9d9..438f96bb86f9 100644 --- a/gfx/cairo/cairo/src/cairo-botor-scan-converter.c +++ b/gfx/cairo/cairo/src/cairo-botor-scan-converter.c @@ -43,9 +43,9 @@ #include "cairoint.h" #include "cairo-error-private.h" -#include "cairo-list-private.h" +#include "cairo-list-inline.h" #include "cairo-freelist-private.h" -#include "cairo-combsort-private.h" +#include "cairo-combsort-inline.h" #include @@ -456,7 +456,7 @@ edges_compare_x_for_y (const cairo_edge_t *a, HAVE_BX = 0x2, HAVE_BOTH = HAVE_AX | HAVE_BX } have_ax_bx = HAVE_BOTH; - int32_t ax, bx; + int32_t ax = 0, bx = 0; /* XXX given we have x and dx? */ @@ -1072,7 +1072,7 @@ coverage_reset (struct coverage *cells) coverage_rewind (cells); } -inline static struct cell * +static struct cell * coverage_alloc (sweep_line_t *sweep_line, struct cell *tail, int x) @@ -1397,6 +1397,7 @@ render_rows (cairo_botor_scan_converter_t *self, if (x > prev_x) { spans[num_spans].x = prev_x; + spans[num_spans].inverse = 0; spans[num_spans].coverage = AREA_TO_ALPHA (cover); ++num_spans; } @@ -1413,12 +1414,14 @@ render_rows (cairo_botor_scan_converter_t *self, if (prev_x <= self->xmax) { spans[num_spans].x = prev_x; + spans[num_spans].inverse = 0; spans[num_spans].coverage = AREA_TO_ALPHA (cover); ++num_spans; } if (cover && prev_x < self->xmax) { spans[num_spans].x = self->xmax; + spans[num_spans].inverse = 1; spans[num_spans].coverage = 0; ++num_spans; } @@ -2125,6 +2128,7 @@ botor_add_edge (cairo_botor_scan_converter_t *self, return CAIRO_STATUS_SUCCESS; } +#if 0 static cairo_status_t _cairo_botor_scan_converter_add_edge (void *converter, const cairo_point_t *p1, @@ -2143,9 +2147,10 @@ _cairo_botor_scan_converter_add_edge (void *converter, return botor_add_edge (self, &edge); } +#endif -static cairo_status_t -_cairo_botor_scan_converter_add_polygon (void *converter, +cairo_status_t +_cairo_botor_scan_converter_add_polygon (cairo_botor_scan_converter_t *converter, const cairo_polygon_t *polygon) { cairo_botor_scan_converter_t *self = converter; @@ -2179,8 +2184,6 @@ _cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, cairo_fill_rule_t fill_rule) { self->base.destroy = _cairo_botor_scan_converter_destroy; - self->base.add_edge = _cairo_botor_scan_converter_add_edge; - self->base.add_polygon = _cairo_botor_scan_converter_add_polygon; self->base.generate = _cairo_botor_scan_converter_generate; self->extents = *extents; diff --git a/gfx/cairo/cairo/src/cairo-box-inline.h b/gfx/cairo/cairo/src/cairo-box-inline.h new file mode 100644 index 000000000000..024e7f151073 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-box-inline.h @@ -0,0 +1,131 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2010 Andrea Canciani + * + * 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): + * Andrea Canciani + */ + +#ifndef CAIRO_BOX_H +#define CAIRO_BOX_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-fixed-private.h" + +static inline void +_cairo_box_set (cairo_box_t *box, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + box->p1 = *p1; + box->p2 = *p2; +} + +static inline void +_cairo_box_from_integers (cairo_box_t *box, int x, int y, int w, int h) +{ + box->p1.x = _cairo_fixed_from_int (x); + box->p1.y = _cairo_fixed_from_int (y); + box->p2.x = _cairo_fixed_from_int (x + w); + box->p2.y = _cairo_fixed_from_int (y + h); +} + +static inline void +_cairo_box_from_rectangle_int (cairo_box_t *box, + const cairo_rectangle_int_t *rect) +{ + box->p1.x = _cairo_fixed_from_int (rect->x); + box->p1.y = _cairo_fixed_from_int (rect->y); + box->p2.x = _cairo_fixed_from_int (rect->x + rect->width); + box->p2.y = _cairo_fixed_from_int (rect->y + rect->height); +} + +/* assumes box->p1 is top-left, p2 bottom-right */ +static inline void +_cairo_box_add_point (cairo_box_t *box, + const cairo_point_t *point) +{ + if (point->x < box->p1.x) + box->p1.x = point->x; + else if (point->x > box->p2.x) + box->p2.x = point->x; + + if (point->y < box->p1.y) + box->p1.y = point->y; + else if (point->y > box->p2.y) + box->p2.y = point->y; +} + +static inline void +_cairo_box_add_box (cairo_box_t *box, + const cairo_box_t *add) +{ + if (add->p1.x < box->p1.x) + box->p1.x = add->p1.x; + if (add->p2.x > box->p2.x) + box->p2.x = add->p2.x; + + if (add->p1.y < box->p1.y) + box->p1.y = add->p1.y; + if (add->p2.y > box->p2.y) + box->p2.y = add->p2.y; +} + +/* assumes box->p1 is top-left, p2 bottom-right */ +static inline cairo_bool_t +_cairo_box_contains_point (const cairo_box_t *box, + const cairo_point_t *point) +{ + return box->p1.x <= point->x && point->x <= box->p2.x && + box->p1.y <= point->y && point->y <= box->p2.y; +} + +static inline cairo_bool_t +_cairo_box_is_pixel_aligned (const cairo_box_t *box) +{ +#if CAIRO_FIXED_FRAC_BITS <= 8 && 0 + return ((cairo_fixed_unsigned_t)(box->p1.x & CAIRO_FIXED_FRAC_MASK) << 24 | + (box->p1.y & CAIRO_FIXED_FRAC_MASK) << 16 | + (box->p2.x & CAIRO_FIXED_FRAC_MASK) << 8 | + (box->p2.y & CAIRO_FIXED_FRAC_MASK) << 0) == 0; +#else /* GCC on i7 prefers this variant (bizarrely according to the profiler) */ + cairo_fixed_t f; + + f = 0; + f |= box->p1.x & CAIRO_FIXED_FRAC_MASK; + f |= box->p1.y & CAIRO_FIXED_FRAC_MASK; + f |= box->p2.x & CAIRO_FIXED_FRAC_MASK; + f |= box->p2.y & CAIRO_FIXED_FRAC_MASK; + + return f == 0; +#endif +} + +#endif /* CAIRO_BOX_H */ diff --git a/gfx/cairo/cairo/src/cairo-boxes-intersect.c b/gfx/cairo/cairo/src/cairo-boxes-intersect.c new file mode 100644 index 000000000000..96ae66334e6a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-boxes-intersect.c @@ -0,0 +1,690 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2009 Chris Wilson + * 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 Carl Worth + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-combsort-inline.h" +#include "cairo-list-private.h" + +#include + +typedef struct _rectangle rectangle_t; +typedef struct _edge edge_t; + +struct _edge { + edge_t *next, *prev; + edge_t *right; + cairo_fixed_t x, top; + int a_or_b; + int dir; +}; + +struct _rectangle { + edge_t left, right; + int32_t top, bottom; +}; + +#define UNROLL3(x) x x x + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef struct _pqueue { + int size, max_size; + + rectangle_t **elements; + rectangle_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct _sweep_line { + rectangle_t **rectangles; + pqueue_t pq; + edge_t head, tail; + edge_t *insert_left, *insert_right; + int32_t current_y; + int32_t last_y; + + jmp_buf unwind; +} sweep_line_t; + +#define DEBUG_TRAPS 0 + +#if DEBUG_TRAPS +static void +dump_traps (cairo_traps_t *traps, const char *filename) +{ + FILE *file; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + + file = fopen (filename, "a"); + if (file != NULL) { + for (n = 0; n < traps->num_traps; n++) { + fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", + traps->traps[n].top, + traps->traps[n].bottom, + traps->traps[n].left.p1.x, + traps->traps[n].left.p1.y, + traps->traps[n].left.p2.x, + traps->traps[n].left.p2.y, + traps->traps[n].right.p1.x, + traps->traps[n].right.p1.y, + traps->traps[n].right.p2.x, + traps->traps[n].right.p2.y); + } + fprintf (file, "\n"); + fclose (file); + } +} +#else +#define dump_traps(traps, filename) +#endif + +static inline int +rectangle_compare_start (const rectangle_t *a, + const rectangle_t *b) +{ + return a->top - b->top; +} + +static inline int +rectangle_compare_stop (const rectangle_t *a, + const rectangle_t *b) +{ + return a->bottom - b->bottom; +} + +static inline void +pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; + pq->elements[PQ_FIRST_ENTRY] = NULL; +} + +static inline void +pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_bool_t +pqueue_grow (pqueue_t *pq) +{ + rectangle_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (rectangle_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (rectangle_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + } + + pq->elements = new_elements; + return TRUE; +} + +static inline void +pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle) +{ + rectangle_t **elements; + int i, parent; + + if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) { + if (unlikely (! pqueue_grow (&sweep->pq))) { + longjmp (sweep->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + elements = sweep->pq.elements; + for (i = ++sweep->pq.size; + i != PQ_FIRST_ENTRY && + rectangle_compare_stop (rectangle, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = rectangle; +} + +static inline void +pqueue_pop (pqueue_t *pq) +{ + rectangle_t **elements = pq->elements; + rectangle_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + rectangle_compare_stop (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (rectangle_compare_stop (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline rectangle_t * +rectangle_pop_start (sweep_line_t *sweep_line) +{ + return *sweep_line->rectangles++; +} + +static inline rectangle_t * +rectangle_peek_stop (sweep_line_t *sweep_line) +{ + return sweep_line->pq.elements[PQ_FIRST_ENTRY]; +} + +CAIRO_COMBSORT_DECLARE (_rectangle_sort, + rectangle_t *, + rectangle_compare_start) + +static void +sweep_line_init (sweep_line_t *sweep_line, + rectangle_t **rectangles, + int num_rectangles) +{ + _rectangle_sort (rectangles, num_rectangles); + rectangles[num_rectangles] = NULL; + sweep_line->rectangles = rectangles; + + sweep_line->head.x = INT32_MIN; + sweep_line->head.right = NULL; + sweep_line->head.dir = 0; + sweep_line->head.next = &sweep_line->tail; + sweep_line->tail.x = INT32_MAX; + sweep_line->tail.right = NULL; + sweep_line->tail.dir = 0; + sweep_line->tail.prev = &sweep_line->head; + + sweep_line->insert_left = &sweep_line->tail; + sweep_line->insert_right = &sweep_line->tail; + + sweep_line->current_y = INT32_MIN; + sweep_line->last_y = INT32_MIN; + + pqueue_init (&sweep_line->pq); +} + +static void +sweep_line_fini (sweep_line_t *sweep_line) +{ + pqueue_fini (&sweep_line->pq); +} + +static void +end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out) +{ + if (likely (left->top < bot)) { + cairo_status_t status; + cairo_box_t box; + + box.p1.x = left->x; + box.p1.y = left->top; + box.p2.x = left->right->x; + box.p2.y = bot; + + status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); + if (unlikely (status)) + longjmp (sweep_line->unwind, status); + } + + left->right = NULL; +} + +/* Start a new trapezoid at the given top y coordinate, whose edges + * are `edge' and `edge->next'. If `edge' already has a trapezoid, + * then either add it to the traps in `traps', if the trapezoid's + * right edge differs from `edge->next', or do nothing if the new + * trapezoid would be a continuation of the existing one. */ +static inline void +start_or_continue_box (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right, + int top, + cairo_boxes_t *out) +{ + if (left->right == right) + return; + + if (left->right != NULL) { + if (right != NULL && left->right->x == right->x) { + /* continuation on right, so just swap edges */ + left->right = right; + return; + } + + end_box (sweep_line, left, top, out); + } + + if (right != NULL && left->x != right->x) { + left->top = top; + left->right = right; + } +} + +static inline int is_zero(const int *winding) +{ + return winding[0] == 0 || winding[1] == 0; +} + +static inline void +active_edges (sweep_line_t *sweep, cairo_boxes_t *out) +{ + int top = sweep->current_y; + int winding[2] = { 0 }; + edge_t *pos; + + if (sweep->last_y == sweep->current_y) + return; + + pos = sweep->head.next; + if (pos == &sweep->tail) + return; + + do { + edge_t *left, *right; + + left = pos; + do { + winding[left->a_or_b] += left->dir; + if (!is_zero (winding)) + break; + if (left->next == &sweep->tail) + goto out; + + if (unlikely (left->right != NULL)) + end_box (sweep, left, top, out); + + left = left->next; + } while (1); + + right = left->next; + do { + if (unlikely (right->right != NULL)) + end_box (sweep, right, top, out); + + winding[right->a_or_b] += right->dir; + if (is_zero (winding)) { + /* skip co-linear edges */ + if (likely (right->x != right->next->x)) + break; + } + + right = right->next; + } while (TRUE); + + start_or_continue_box (sweep, left, right, top, out); + + pos = right->next; + } while (pos != &sweep->tail); + +out: + sweep->last_y = sweep->current_y; +} + +static inline void +sweep_line_delete_edge (sweep_line_t *sweep_line, edge_t *edge, cairo_boxes_t *out) +{ + if (edge->right != NULL) { + edge_t *next = edge->next; + if (next->x == edge->x) { + next->top = edge->top; + next->right = edge->right; + } else { + end_box (sweep_line, edge, sweep_line->current_y, out); + } + } + + if (sweep_line->insert_left == edge) + sweep_line->insert_left = edge->next; + if (sweep_line->insert_right == edge) + sweep_line->insert_right = edge->next; + + edge->prev->next = edge->next; + edge->next->prev = edge->prev; +} + +static inline void +sweep_line_delete (sweep_line_t *sweep, + rectangle_t *rectangle, + cairo_boxes_t *out) +{ + sweep_line_delete_edge (sweep, &rectangle->left, out); + sweep_line_delete_edge (sweep, &rectangle->right, out); + + pqueue_pop (&sweep->pq); +} + +static inline void +insert_edge (edge_t *edge, edge_t *pos) +{ + if (pos->x != edge->x) { + if (pos->x > edge->x) { + do { + UNROLL3({ + if (pos->prev->x <= edge->x) + break; + pos = pos->prev; + }) + } while (TRUE); + } else { + do { + UNROLL3({ + pos = pos->next; + if (pos->x >= edge->x) + break; + }) + } while (TRUE); + } + } + + pos->prev->next = edge; + edge->prev = pos->prev; + edge->next = pos; + pos->prev = edge; +} + +static inline void +sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) +{ + edge_t *pos; + + /* right edge */ + pos = sweep->insert_right; + insert_edge (&rectangle->right, pos); + sweep->insert_right = &rectangle->right; + + /* left edge */ + pos = sweep->insert_left; + if (pos->x > sweep->insert_right->x) + pos = sweep->insert_right->prev; + insert_edge (&rectangle->left, pos); + sweep->insert_left = &rectangle->left; + + pqueue_push (sweep, rectangle); +} + +static cairo_status_t +intersect (rectangle_t **rectangles, int num_rectangles, cairo_boxes_t *out) +{ + sweep_line_t sweep_line; + rectangle_t *rectangle; + cairo_status_t status; + + sweep_line_init (&sweep_line, rectangles, num_rectangles); + if ((status = setjmp (sweep_line.unwind))) + goto unwind; + + rectangle = rectangle_pop_start (&sweep_line); + do { + if (rectangle->top != sweep_line.current_y) { + rectangle_t *stop; + + stop = rectangle_peek_stop (&sweep_line); + while (stop != NULL && stop->bottom < rectangle->top) { + if (stop->bottom != sweep_line.current_y) { + active_edges (&sweep_line, out); + sweep_line.current_y = stop->bottom; + } + + sweep_line_delete (&sweep_line, stop, out); + + stop = rectangle_peek_stop (&sweep_line); + } + + active_edges (&sweep_line, out); + sweep_line.current_y = rectangle->top; + } + + sweep_line_insert (&sweep_line, rectangle); + } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL); + + while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) { + if (rectangle->bottom != sweep_line.current_y) { + active_edges (&sweep_line, out); + sweep_line.current_y = rectangle->bottom; + } + + sweep_line_delete (&sweep_line, rectangle, out); + } + +unwind: + sweep_line_fini (&sweep_line); + return status; +} + +static cairo_status_t +_cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes, + const cairo_box_t *box, + cairo_boxes_t *out) +{ + cairo_status_t status; + int i, j; + + if (out == boxes) { /* inplace update */ + struct _cairo_boxes_chunk *chunk; + + out->num_boxes = 0; + for (chunk = &out->chunks; chunk != NULL; chunk = chunk->next) { + for (i = j = 0; i < chunk->count; i++) { + cairo_box_t *b = &chunk->base[i]; + + b->p1.x = MAX (b->p1.x, box->p1.x); + b->p1.y = MAX (b->p1.y, box->p1.y); + b->p2.x = MIN (b->p2.x, box->p2.x); + b->p2.y = MIN (b->p2.y, box->p2.y); + if (b->p1.x < b->p2.x && b->p1.y < b->p2.y) { + if (i != j) + chunk->base[j] = *b; + j++; + } + } + /* XXX unlink empty chains? */ + chunk->count = j; + out->num_boxes += j; + } + } else { + const struct _cairo_boxes_chunk *chunk; + + _cairo_boxes_clear (out); + _cairo_boxes_limit (out, box, 1); + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (out, + CAIRO_ANTIALIAS_DEFAULT, + &chunk->base[i]); + if (unlikely (status)) + return status; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_boxes_intersect (const cairo_boxes_t *a, + const cairo_boxes_t *b, + cairo_boxes_t *out) +{ + rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; + rectangle_t *rectangles; + rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1]; + rectangle_t **rectangles_ptrs; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + int i, j, count; + + if (unlikely (a->num_boxes == 0 || b->num_boxes == 0)) { + _cairo_boxes_clear (out); + return CAIRO_STATUS_SUCCESS; + } + + if (a->num_boxes == 1) { + cairo_box_t box = a->chunks.base[0]; + return _cairo_boxes_intersect_with_box (b, &box, out); + } + if (b->num_boxes == 1) { + cairo_box_t box = b->chunks.base[0]; + return _cairo_boxes_intersect_with_box (a, &box, out); + } + + rectangles = stack_rectangles; + rectangles_ptrs = stack_rectangles_ptrs; + count = a->num_boxes + b->num_boxes; + if (count > ARRAY_LENGTH (stack_rectangles)) { + rectangles = _cairo_malloc_ab_plus_c (count, + sizeof (rectangle_t) + + sizeof (rectangle_t *), + sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + rectangles_ptrs = (rectangle_t **) (rectangles + count); + } + + j = 0; + for (chunk = &a->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + if (box[i].p1.x < box[i].p2.x) { + rectangles[j].left.x = box[i].p1.x; + rectangles[j].left.dir = 1; + + rectangles[j].right.x = box[i].p2.x; + rectangles[j].right.dir = -1; + } else { + rectangles[j].right.x = box[i].p1.x; + rectangles[j].right.dir = 1; + + rectangles[j].left.x = box[i].p2.x; + rectangles[j].left.dir = -1; + } + + rectangles[j].left.a_or_b = 0; + rectangles[j].left.right = NULL; + rectangles[j].right.a_or_b = 0; + rectangles[j].right.right = NULL; + + rectangles[j].top = box[i].p1.y; + rectangles[j].bottom = box[i].p2.y; + + rectangles_ptrs[j] = &rectangles[j]; + j++; + } + } + for (chunk = &b->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + if (box[i].p1.x < box[i].p2.x) { + rectangles[j].left.x = box[i].p1.x; + rectangles[j].left.dir = 1; + + rectangles[j].right.x = box[i].p2.x; + rectangles[j].right.dir = -1; + } else { + rectangles[j].right.x = box[i].p1.x; + rectangles[j].right.dir = 1; + + rectangles[j].left.x = box[i].p2.x; + rectangles[j].left.dir = -1; + } + + rectangles[j].left.a_or_b = 1; + rectangles[j].left.right = NULL; + rectangles[j].right.a_or_b = 1; + rectangles[j].right.right = NULL; + + rectangles[j].top = box[i].p1.y; + rectangles[j].bottom = box[i].p2.y; + + rectangles_ptrs[j] = &rectangles[j]; + j++; + } + } + assert (j == count); + + _cairo_boxes_clear (out); + status = intersect (rectangles_ptrs, j, out); + if (rectangles != stack_rectangles) + free (rectangles); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-boxes-private.h b/gfx/cairo/cairo/src/cairo-boxes-private.h index 3af0fbdefe7f..fd622aec3162 100644 --- a/gfx/cairo/cairo/src/cairo-boxes-private.h +++ b/gfx/cairo/cairo/src/cairo-boxes-private.h @@ -37,13 +37,19 @@ #include "cairo-types-private.h" #include "cairo-compiler-private.h" +#include +#include + struct _cairo_boxes_t { cairo_status_t status; + cairo_box_t limit; const cairo_box_t *limits; int num_limits; + int num_boxes; - unsigned int is_pixel_aligned : 1; + + unsigned int is_pixel_aligned; struct _cairo_boxes_chunk { struct _cairo_boxes_chunk *next; @@ -57,11 +63,19 @@ struct _cairo_boxes_t { cairo_private void _cairo_boxes_init (cairo_boxes_t *boxes); +cairo_private void +_cairo_boxes_init_with_clip (cairo_boxes_t *boxes, + cairo_clip_t *clip); + cairo_private void _cairo_boxes_init_for_array (cairo_boxes_t *boxes, cairo_box_t *array, int num_boxes); +cairo_private void +_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes, + int x, int y, int w, int h); + cairo_private void _cairo_boxes_limit (cairo_boxes_t *boxes, const cairo_box_t *limits, @@ -69,16 +83,40 @@ _cairo_boxes_limit (cairo_boxes_t *boxes, cairo_private cairo_status_t _cairo_boxes_add (cairo_boxes_t *boxes, + cairo_antialias_t antialias, const cairo_box_t *box); cairo_private void _cairo_boxes_extents (const cairo_boxes_t *boxes, - cairo_rectangle_int_t *extents); + cairo_box_t *box); + +cairo_private cairo_box_t * +_cairo_boxes_to_array (const cairo_boxes_t *boxes, + int *num_boxes); + +cairo_private cairo_status_t +_cairo_boxes_intersect (const cairo_boxes_t *a, + const cairo_boxes_t *b, + cairo_boxes_t *out); cairo_private void _cairo_boxes_clear (cairo_boxes_t *boxes); +cairo_private_no_warn cairo_bool_t +_cairo_boxes_for_each_box (cairo_boxes_t *boxes, + cairo_bool_t (*func) (cairo_box_t *box, void *data), + void *data); + +cairo_private cairo_status_t +_cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes); + cairo_private void _cairo_boxes_fini (cairo_boxes_t *boxes); +cairo_private void +_cairo_debug_print_boxes (FILE *stream, + const cairo_boxes_t *boxes); + #endif /* CAIRO_BOXES_H */ diff --git a/gfx/cairo/cairo/src/cairo-boxes.c b/gfx/cairo/cairo/src/cairo-boxes.c index 31bfc0e4ebb6..3c5d2a750295 100644 --- a/gfx/cairo/cairo/src/cairo-boxes.c +++ b/gfx/cairo/cairo/src/cairo-boxes.c @@ -33,6 +33,7 @@ #include "cairoint.h" +#include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" @@ -52,6 +53,25 @@ _cairo_boxes_init (cairo_boxes_t *boxes) boxes->is_pixel_aligned = TRUE; } +void +_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes, + int x, int y, int w, int h) +{ + _cairo_boxes_init (boxes); + + _cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h); + boxes->num_boxes = 1; +} + +void +_cairo_boxes_init_with_clip (cairo_boxes_t *boxes, + cairo_clip_t *clip) +{ + _cairo_boxes_init (boxes); + if (clip) + _cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes); +} + void _cairo_boxes_init_for_array (cairo_boxes_t *boxes, cairo_box_t *array, @@ -82,6 +102,17 @@ _cairo_boxes_init_for_array (cairo_boxes_t *boxes, boxes->is_pixel_aligned = n == num_boxes; } +/** _cairo_boxes_limit: + * + * 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, const cairo_box_t *limits, @@ -145,19 +176,25 @@ _cairo_boxes_add_internal (cairo_boxes_t *boxes, chunk->base[chunk->count++] = *box; boxes->num_boxes++; - if (boxes->is_pixel_aligned) { - boxes->is_pixel_aligned = - _cairo_fixed_is_integer (box->p1.x) && - _cairo_fixed_is_integer (box->p1.y) && - _cairo_fixed_is_integer (box->p2.x) && - _cairo_fixed_is_integer (box->p2.y); - } + if (boxes->is_pixel_aligned) + boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box); } cairo_status_t _cairo_boxes_add (cairo_boxes_t *boxes, + cairo_antialias_t antialias, const cairo_box_t *box) { + cairo_box_t b; + + if (antialias == CAIRO_ANTIALIAS_NONE) { + b.p1.x = _cairo_fixed_round_down (box->p1.x); + b.p1.y = _cairo_fixed_round_down (box->p1.y); + b.p2.x = _cairo_fixed_round_down (box->p2.x); + b.p2.y = _cairo_fixed_round_down (box->p2.y); + box = &b; + } + if (box->p1.y == box->p2.y) return CAIRO_STATUS_SUCCESS; @@ -239,35 +276,44 @@ _cairo_boxes_add (cairo_boxes_t *boxes, return boxes->status; } +/** _cairo_boxes_extents: + * + * 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, - cairo_rectangle_int_t *extents) + cairo_box_t *box) { const struct _cairo_boxes_chunk *chunk; - cairo_box_t box; + cairo_box_t b; int i; - box.p1.y = box.p1.x = INT_MAX; - box.p2.y = box.p2.x = INT_MIN; - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *b = chunk->base; - for (i = 0; i < chunk->count; i++) { - if (b[i].p1.x < box.p1.x) - box.p1.x = b[i].p1.x; - - if (b[i].p1.y < box.p1.y) - box.p1.y = b[i].p1.y; - - if (b[i].p2.x > box.p2.x) - box.p2.x = b[i].p2.x; - - if (b[i].p2.y > box.p2.y) - box.p2.y = b[i].p2.y; - } + if (boxes->num_boxes == 0) { + box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0; + return; } - _cairo_box_round_to_rectangle (&box, extents); + b = boxes->chunks.base[0]; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + if (chunk->base[i].p1.x < b.p1.x) + b.p1.x = chunk->base[i].p1.x; + + if (chunk->base[i].p1.y < b.p1.y) + b.p1.y = chunk->base[i].p1.y; + + if (chunk->base[i].p2.x > b.p2.x) + b.p2.x = chunk->base[i].p2.x; + + if (chunk->base[i].p2.y > b.p2.y) + b.p2.y = chunk->base[i].p2.y; + } + } + *box = b; } void @@ -283,11 +329,48 @@ _cairo_boxes_clear (cairo_boxes_t *boxes) boxes->tail = &boxes->chunks; boxes->chunks.next = 0; boxes->chunks.count = 0; + boxes->chunks.base = boxes->boxes_embedded; + boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded); boxes->num_boxes = 0; boxes->is_pixel_aligned = TRUE; } +/** _cairo_boxes_to_array: + * + * 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). + */ +cairo_box_t * +_cairo_boxes_to_array (const cairo_boxes_t *boxes, + int *num_boxes) +{ + const struct _cairo_boxes_chunk *chunk; + cairo_box_t *box; + int i, j; + + *num_boxes = boxes->num_boxes; + + box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t)); + if (box == NULL) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + j = 0; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) + box[j++] = chunk->base[i]; + } + + return box; +} + void _cairo_boxes_fini (cairo_boxes_t *boxes) { @@ -298,3 +381,106 @@ _cairo_boxes_fini (cairo_boxes_t *boxes) free (chunk); } } + +cairo_bool_t +_cairo_boxes_for_each_box (cairo_boxes_t *boxes, + cairo_bool_t (*func) (cairo_box_t *box, void *data), + void *data) +{ + struct _cairo_boxes_chunk *chunk; + int i; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) + if (! func (&chunk->base[i], data)) + return FALSE; + } + + return TRUE; +} + +struct cairo_box_renderer { + cairo_span_renderer_t base; + cairo_boxes_t *boxes; +}; + +static cairo_status_t +span_to_boxes (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + struct cairo_box_renderer *r = abstract_renderer; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_box_t box; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + box.p1.y = _cairo_fixed_from_int (y); + box.p2.y = _cairo_fixed_from_int (y + h); + do { + if (spans[0].coverage) { + box.p1.x = _cairo_fixed_from_int(spans[0].x); + box.p2.x = _cairo_fixed_from_int(spans[1].x); + status = _cairo_boxes_add (r->boxes, CAIRO_ANTIALIAS_DEFAULT, &box); + } + spans++; + } while (--num_spans > 1 && status == CAIRO_STATUS_SUCCESS); + + return status; +} + +cairo_status_t +_cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes) +{ + struct cairo_box_renderer renderer; + cairo_scan_converter_t *converter; + cairo_int_status_t status; + cairo_rectangle_int_t r; + + TRACE ((stderr, "%s: fill_rule=%d\n", __FUNCTION__, fill_rule)); + + _cairo_box_round_to_rectangle (&polygon->extents, &r); + converter = _cairo_mono_scan_converter_create (r.x, r.y, + r.x + r.width, + r.y + r.height, + fill_rule); + status = _cairo_mono_scan_converter_add_polygon (converter, polygon); + if (unlikely (status)) + goto cleanup_converter; + + renderer.boxes = boxes; + renderer.base.render_rows = span_to_boxes; + + status = converter->generate (converter, &renderer.base); +cleanup_converter: + converter->destroy (converter); + return status; +} + +void +_cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes) +{ + const struct _cairo_boxes_chunk *chunk; + cairo_box_t extents; + int i; + + _cairo_boxes_extents (boxes, &extents); + fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n", + boxes->num_boxes, + _cairo_fixed_to_double (extents.p1.x), + _cairo_fixed_to_double (extents.p1.y), + _cairo_fixed_to_double (extents.p2.x), + _cairo_fixed_to_double (extents.p2.y)); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + fprintf (stderr, " box[%d]: (%f, %f), (%f, %f)\n", i, + _cairo_fixed_to_double (chunk->base[i].p1.x), + _cairo_fixed_to_double (chunk->base[i].p1.y), + _cairo_fixed_to_double (chunk->base[i].p2.x), + _cairo_fixed_to_double (chunk->base[i].p2.y)); + } + } +} diff --git a/gfx/cairo/cairo/src/cairo-cache-private.h b/gfx/cairo/cairo/src/cairo-cache-private.h index 927ff0c0b963..24b6d0b2092f 100644 --- a/gfx/cairo/cairo/src/cairo-cache-private.h +++ b/gfx/cairo/cairo/src/cairo-cache-private.h @@ -43,7 +43,7 @@ #include "cairo-types-private.h" /** - * cairo_cache_entry_t: + * _cairo_cache_entry: * * A #cairo_cache_entry_t contains both a key and a value for * #cairo_cache_t. User-derived types for #cairo_cache_entry_t must @@ -84,7 +84,7 @@ * not be initialized if so desired. **/ typedef struct _cairo_cache_entry { - uintptr_t hash; + unsigned long hash; unsigned long size; } cairo_cache_entry_t; diff --git a/gfx/cairo/cairo/src/cairo-cff-subset.c b/gfx/cairo/cairo/src/cairo-cff-subset.c index f9b03681480d..d0597c213855 100644 --- a/gfx/cairo/cairo/src/cairo-cff-subset.c +++ b/gfx/cairo/cairo/src/cairo-cff-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 © 2006 Adrian Johnson @@ -36,11 +37,14 @@ /* * Useful links: - * http://www.adobe.com/devnet/font/pdfs/5176.CFF.pdf + * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf + * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf */ -#define _BSD_SOURCE /* for snprintf(), strdup() */ +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET @@ -48,33 +52,65 @@ #include "cairo-scaled-font-subsets-private.h" #include "cairo-truetype-subset-private.h" #include +#include /* CFF Dict Operators. If the high byte is 0 the command is encoded * with a single byte. */ -#define BASEFONTNAME_OP 0x0c16 -#define CIDCOUNT_OP 0x0c22 -#define CHARSET_OP 0x000f -#define CHARSTRINGS_OP 0x0011 -#define COPYRIGHT_OP 0x0c00 -#define ENCODING_OP 0x0010 -#define FAMILYNAME_OP 0x0003 -#define FDARRAY_OP 0x0c24 -#define FDSELECT_OP 0x0c25 -#define FONTBBOX_OP 0x0005 -#define FONTNAME_OP 0x0c26 -#define FULLNAME_OP 0x0002 -#define LOCAL_SUB_OP 0x0013 -#define NOTICE_OP 0x0001 -#define POSTSCRIPT_OP 0x0c15 -#define PRIVATE_OP 0x0012 -#define ROS_OP 0x0c1e -#define UNIQUEID_OP 0x000d -#define VERSION_OP 0x0000 -#define WEIGHT_OP 0x0004 -#define XUID_OP 0x000e +#define BASEFONTNAME_OP 0x0c16 +#define CIDCOUNT_OP 0x0c22 +#define CHARSET_OP 0x000f +#define CHARSTRINGS_OP 0x0011 +#define COPYRIGHT_OP 0x0c00 +#define DEFAULTWIDTH_OP 0x0014 +#define ENCODING_OP 0x0010 +#define FAMILYNAME_OP 0x0003 +#define FDARRAY_OP 0x0c24 +#define FDSELECT_OP 0x0c25 +#define FONTBBOX_OP 0x0005 +#define FONTMATRIX_OP 0x0c07 +#define FONTNAME_OP 0x0c26 +#define FULLNAME_OP 0x0002 +#define LOCAL_SUB_OP 0x0013 +#define NOMINALWIDTH_OP 0x0015 +#define NOTICE_OP 0x0001 +#define POSTSCRIPT_OP 0x0c15 +#define PRIVATE_OP 0x0012 +#define ROS_OP 0x0c1e +#define UNIQUEID_OP 0x000d +#define VERSION_OP 0x0000 +#define WEIGHT_OP 0x0004 +#define XUID_OP 0x000e +#define BLUEVALUES_OP 0x0006 +#define OTHERBLUES_OP 0x0007 +#define FAMILYBLUES_OP 0x0008 +#define FAMILYOTHERBLUES_OP 0x0009 +#define STEMSNAPH_OP 0x0c0c +#define STEMSNAPV_OP 0x0c0d #define NUM_STD_STRINGS 391 +/* Type 2 Charstring operators */ +#define TYPE2_hstem 0x0001 +#define TYPE2_vstem 0x0003 +#define TYPE2_callsubr 0x000a + +#define TYPE2_return 0x000b +#define TYPE2_endchar 0x000e + +#define TYPE2_hstemhm 0x0012 +#define TYPE2_hintmask 0x0013 +#define TYPE2_cntrmask 0x0014 +#define TYPE2_vstemhm 0x0017 +#define TYPE2_callgsubr 0x001d + +#define TYPE2_rmoveto 0x0015 +#define TYPE2_hmoveto 0x0016 +#define TYPE2_vmoveto 0x0004 + + +#define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */ + + typedef struct _cff_header { uint8_t major; uint8_t minor; @@ -116,9 +152,15 @@ typedef struct _cairo_cff_font { cairo_array_t charstrings_index; cairo_array_t global_sub_index; cairo_array_t local_sub_index; + unsigned char *charset; int num_glyphs; cairo_bool_t is_cid; + cairo_bool_t is_opentype; int units_per_em; + int global_sub_bias; + int local_sub_bias; + double default_width; + double nominal_width; /* CID Font Data */ int *fdselect; @@ -126,15 +168,23 @@ typedef struct _cairo_cff_font { cairo_hash_table_t **fd_dict; cairo_hash_table_t **fd_private_dict; cairo_array_t *fd_local_sub_index; + int *fd_local_sub_bias; + double *fd_default_width; + double *fd_nominal_width; /* Subsetted Font Data */ char *subset_font_name; cairo_array_t charstrings_subset_index; cairo_array_t strings_subset_index; + int euro_sid; int *fdselect_subset; unsigned int num_subset_fontdicts; int *fd_subset_map; int *private_dict_offset; + cairo_bool_t subset_subroutines; + cairo_bool_t *global_subs_used; + cairo_bool_t *local_subs_used; + cairo_bool_t **fd_local_subs_used; cairo_array_t output; /* Subset Metrics */ @@ -142,6 +192,19 @@ typedef struct _cairo_cff_font { int x_min, y_min, x_max, y_max; int ascent, descent; + /* Type 2 charstring data */ + int type2_stack_size; + int type2_stack_top_value; + cairo_bool_t type2_stack_top_is_int; + int type2_num_hints; + int type2_hintmask_bytes; + int type2_nesting_level; + cairo_bool_t type2_seen_first_int; + cairo_bool_t type2_find_width; + cairo_bool_t type2_found_width; + int type2_width; + cairo_bool_t type2_has_path; + } cairo_cff_font_t; /* Encoded integer using maximum sized encoding. This is required for @@ -187,7 +250,7 @@ decode_integer (unsigned char *p, int *integer) *integer = (int)(p[1]<<8 | p[2]); p += 3; } else if (*p == 29) { - *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); + *integer = (int)(((uint32_t)p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); p += 5; } else if (*p >= 32 && *p <= 246) { *integer = *p++ - 139; @@ -204,6 +267,78 @@ decode_integer (unsigned char *p, int *integer) return p; } +static char * +decode_nibble (int n, char *buf) +{ + switch (n) + { + case 0xa: + *buf++ = '.'; + break; + case 0xb: + *buf++ = 'E'; + break; + case 0xc: + *buf++ = 'E'; + *buf++ = '-'; + break; + case 0xd: + *buf++ = '-'; + break; + case 0xe: + *buf++ = '-'; + break; + case 0xf: + break; + default: + *buf++ = '0' + n; + break; + } + + return buf; +} + +static unsigned char * +decode_real (unsigned char *p, double *real) +{ + char buffer[100]; + char *buf = buffer; + char *buf_end = buffer + sizeof (buffer); + char *end; + int n; + + p++; + while (buf + 2 < buf_end) { + n = *p >> 4; + buf = decode_nibble (n, buf); + n = *p & 0x0f; + buf = decode_nibble (n, buf); + if ((*p & 0x0f) == 0x0f) { + p++; + break; + } + p++; + }; + *buf = 0; + + *real = _cairo_strtod (buffer, &end); + + return p; +} + +static unsigned char * +decode_number (unsigned char *p, double *number) +{ + if (*p == 30) { + p = decode_real (p, number); + } else { + int i; + p = decode_integer (p, &i); + *number = i; + } + return p; +} + static unsigned char * decode_operator (unsigned char *p, unsigned short *operator) { @@ -283,7 +418,7 @@ cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_pt p = *ptr; if (p + 2 > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; - count = be16_to_cpu( *((uint16_t *)p) ); + count = get_unaligned_be16 (p); p += 2; if (count > 0) { offset_size = *p++; @@ -370,15 +505,32 @@ cff_index_write (cairo_array_t *index, cairo_array_t *output) for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); - status = _cairo_array_append_multiple (output, - element->data, - element->length); + if (element->length > 0) { + status = _cairo_array_append_multiple (output, + element->data, + element->length); + } if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } +static void +cff_index_set_object (cairo_array_t *index, int obj_index, + unsigned char *object , int length) +{ + cff_index_element_t *element; + + element = _cairo_array_index (index, obj_index); + if (element->is_copy) + free (element->data); + + element->data = object; + element->length = length; + element->is_copy = FALSE; +} + static cairo_status_t cff_index_append (cairo_array_t *index, unsigned char *object , int length) { @@ -401,7 +553,7 @@ cff_index_append_copy (cairo_array_t *index, element.length = length; element.is_copy = TRUE; - element.data = malloc (element.length); + element.data = _cairo_malloc (element.length); if (unlikely (element.data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -420,11 +572,11 @@ static void cff_index_fini (cairo_array_t *index) { cff_index_element_t *element; - int i; + unsigned int i; for (i = 0; i < _cairo_array_num_elements (index); i++) { element = _cairo_array_index (index, i); - if (element->is_copy) + if (element->is_copy && element->data) free (element->data); } _cairo_array_fini (index); @@ -464,18 +616,32 @@ cff_dict_create_operator (int operator, { cff_dict_operator_t *op; - op = malloc (sizeof (cff_dict_operator_t)); + op = _cairo_malloc (sizeof (cff_dict_operator_t)); if (unlikely (op == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_dict_init_key (op, operator); - op->operand = malloc (size); - if (unlikely (op->operand == NULL)) { - free (op); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (size != 0) { + op->operand = _cairo_malloc (size); + if (unlikely (op->operand == NULL)) { + free (op); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (op->operand, operand, size); + } else { + op->operand = NULL; + /* Delta-encoded arrays can be empty. */ + if (operator != BLUEVALUES_OP && + operator != OTHERBLUES_OP && + operator != FAMILYBLUES_OP && + operator != FAMILYOTHERBLUES_OP && + operator != STEMSNAPH_OP && + operator != STEMSNAPV_OP) { + free (op); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } } - memcpy (op->operand, operand, size); op->operand_length = size; op->operand_offset = -1; @@ -570,7 +736,7 @@ cff_dict_set_operands (cairo_hash_table_t *dict, op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { free (op->operand); - op->operand = malloc (size); + op->operand = _cairo_malloc (size); if (unlikely (op->operand == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -691,6 +857,7 @@ cairo_cff_font_read_header (cairo_cff_font_t *font) if (font->data_length < sizeof (cff_header_t)) return CAIRO_INT_STATUS_UNSUPPORTED; + font->header = (cff_header_t *) font->data; font->current_ptr = font->data + font->header->header_size; @@ -702,11 +869,36 @@ cairo_cff_font_read_name (cairo_cff_font_t *font) { cairo_array_t index; cairo_int_status_t status; + cff_index_element_t *element; + unsigned char *p; + int i, len; - /* The original font name is not used in the subset. Read the name - * index to skip over it. */ cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); + if (!font->is_opentype) { + element = _cairo_array_index (&index, 0); + p = element->data; + len = element->length; + + /* If font name is prefixed with a subset tag, strip it off. */ + if (len > 7 && p[6] == '+') { + for (i = 0; i < 6; i++) + if (p[i] < 'A' || p[i] > 'Z') + break; + if (i == 6) { + p += 7; + 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; + + status = _cairo_escape_ps_name (&font->ps_name); + } cff_index_fini (&index); return status; @@ -716,6 +908,10 @@ static cairo_int_status_t cairo_cff_font_read_private_dict (cairo_cff_font_t *font, cairo_hash_table_t *private_dict, cairo_array_t *local_sub_index, + int *local_sub_bias, + cairo_bool_t **local_subs_used, + double *default_width, + double *nominal_width, unsigned char *ptr, int size) { @@ -726,6 +922,7 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t *font, int i; unsigned char *operand; unsigned char *p; + int num_subs; status = cff_dict_read (private_dict, ptr, size); if (unlikely (status)) @@ -739,13 +936,35 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t *font, if (unlikely (status)) return status; - /* Use maximum sized encoding to reserve space for later modification. */ - end_buf = encode_integer_max (buf, 0); - status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); + /* Use maximum sized encoding to reserve space for later modification. */ + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); if (unlikely (status)) return status; } + *default_width = 0; + operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i); + if (operand) + decode_number (operand, default_width); + + *nominal_width = 0; + operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i); + if (operand) + decode_number (operand, nominal_width); + + num_subs = _cairo_array_num_elements (local_sub_index); + *local_subs_used = calloc (num_subs, sizeof (cairo_bool_t)); + if (unlikely (*local_subs_used == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (num_subs < 1240) + *local_sub_bias = 107; + else if (num_subs < 33900) + *local_sub_bias = 1131; + else + *local_sub_bias = 32768; + return CAIRO_STATUS_SUCCESS; } @@ -764,14 +983,16 @@ cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) for (i = 0; i < font->num_glyphs; i++) font->fdselect[i] = *p++; } else if (type == 3) { - num_ranges = be16_to_cpu( *((uint16_t *)p) ); + num_ranges = get_unaligned_be16 (p); p += 2; for (i = 0; i < num_ranges; i++) { - first = be16_to_cpu( *((uint16_t *)p) ); + first = get_unaligned_be16 (p); p += 2; fd = *p++; - last = be16_to_cpu( *((uint16_t *)p) ); + last = get_unaligned_be16 (p); + if (last > font->num_glyphs) + return CAIRO_INT_STATUS_UNSUPPORTED; for (j = first; j < last; j++) font->fdselect[j] = fd; } @@ -820,6 +1041,30 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) goto fail; } + font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts); + if (unlikely (font->fd_local_sub_bias == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts); + if (unlikely (font->fd_local_subs_used == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_default_width = calloc (font->num_fontdicts, sizeof (double)); + if (unlikely (font->fd_default_width == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_nominal_width = calloc (font->num_fontdicts, sizeof (double)); + if (unlikely (font->fd_nominal_width == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + for (i = 0; i < font->num_fontdicts; i++) { status = cff_dict_init (&font->fd_dict[i]); if (unlikely (status)) @@ -845,12 +1090,16 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) status = cairo_cff_font_read_private_dict (font, font->fd_private_dict[i], &font->fd_local_sub_index[i], + &font->fd_local_sub_bias[i], + &font->fd_local_subs_used[i], + &font->fd_default_width[i], + &font->fd_nominal_width[i], font->data + offset, size); if (unlikely (status)) goto fail; - /* Set integer operand to max value to use max size encoding to reserve + /* Set integer operand to max value to use max size encoding to reserve * space for any value later */ end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); @@ -867,6 +1116,58 @@ fail: return status; } +static void +cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t *top_dict) +{ + unsigned char *p; + unsigned char *end; + int size; + double x_min, y_min, x_max, y_max; + double xx, yx, xy, yy; + + x_min = 0.0; + y_min = 0.0; + x_max = 0.0; + y_max = 0.0; + p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size); + if (p) { + end = p + size; + if (p < end) + p = decode_number (p, &x_min); + if (p < end) + p = decode_number (p, &y_min); + if (p < end) + p = decode_number (p, &x_max); + if (p < end) + p = decode_number (p, &y_max); + } + font->x_min = floor (x_min); + font->y_min = floor (y_min); + font->x_max = floor (x_max); + font->y_max = floor (y_max); + font->ascent = font->y_max; + font->descent = font->y_min; + + xx = 0.001; + yx = 0.0; + xy = 0.0; + yy = 0.001; + p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size); + if (p) { + end = p + size; + if (p < end) + p = decode_number (p, &xx); + if (p < end) + p = decode_number (p, &yx); + if (p < end) + p = decode_number (p, &xy); + if (p < end) + p = decode_number (p, &yy); + } + /* Freetype uses 1/abs(yy) to get units per EM */ + font->units_per_em = _cairo_round(1.0/fabs(yy)); +} + static cairo_int_status_t cairo_cff_font_read_top_dict (cairo_cff_font_t *font) { @@ -903,6 +1204,20 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) goto fail; font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); + if (font->is_cid) { + operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size); + if (!operand) + return CAIRO_INT_STATUS_UNSUPPORTED; + + decode_integer (operand, &offset); + font->charset = font->data + offset; + if (font->charset >= font->data_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (!font->is_opentype) + cairo_cff_font_read_font_metrics (font, font->top_dict); + if (font->is_cid) { operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size); decode_integer (operand, &offset); @@ -919,9 +1234,13 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size); operand = decode_integer (operand, &size); decode_integer (operand, &offset); - status = cairo_cff_font_read_private_dict (font, + status = cairo_cff_font_read_private_dict (font, font->private_dict, &font->local_sub_index, + &font->local_sub_bias, + &font->local_subs_used, + &font->default_width, + &font->nominal_width, font->data + offset, size); if (unlikely (status)) @@ -935,23 +1254,37 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) if (unlikely (status)) goto fail; - status = cff_dict_set_operands (font->top_dict, - FDSELECT_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - - status = cff_dict_set_operands (font->top_dict, - FDARRAY_OP, buf, end_buf - buf); - if (unlikely (status)) - goto fail; - status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; - cff_dict_remove (font->top_dict, ENCODING_OP); - cff_dict_remove (font->top_dict, PRIVATE_OP); + if (font->scaled_font_subset->is_latin) { + status = cff_dict_set_operands (font->top_dict, + ENCODING_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + /* Private has two operands - size and offset */ + end_buf = encode_integer_max (end_buf, 0); + cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + } else { + status = cff_dict_set_operands (font->top_dict, + FDSELECT_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + status = cff_dict_set_operands (font->top_dict, + FDARRAY_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + + cff_dict_remove (font->top_dict, ENCODING_OP); + cff_dict_remove (font->top_dict, PRIVATE_OP); + } /* Remove the unique identifier operators as the subsetted font is * not the same is the original font. */ @@ -973,7 +1306,26 @@ cairo_cff_font_read_strings (cairo_cff_font_t *font) static cairo_int_status_t cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font) { - return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end); + cairo_int_status_t status; + int num_subs; + + status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end); + if (unlikely (status)) + return status; + + num_subs = _cairo_array_num_elements (&font->global_sub_index); + font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t)); + if (unlikely (font->global_subs_used == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (num_subs < 1240) + font->global_sub_bias = 107; + else if (num_subs < 33900) + font->global_sub_bias = 1131; + else + font->global_sub_bias = 32768; + + return CAIRO_STATUS_SUCCESS; } typedef cairo_int_status_t @@ -1103,21 +1455,365 @@ cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, return CAIRO_STATUS_SUCCESS; } +static unsigned char * +type2_decode_integer (unsigned char *p, int *integer) +{ + if (*p == 28) { + *integer = p[1] << 8 | p[2]; + p += 3; + } else if (*p <= 246) { + *integer = *p++ - 139; + } else if (*p <= 250) { + *integer = (p[0] - 247) * 256 + p[1] + 108; + p += 2; + } else if (*p <= 254) { + *integer = -(p[0] - 251) * 256 - p[1] - 108; + p += 2; + } else { /* *p == 255 */ + /* 16.16 fixed-point number. The fraction is ignored. */ + *integer = (int16_t)((p[1] << 8) | p[2]); + p += 5; + } + return p; +} + +/* Type 2 charstring parser for finding calls to local or global + * subroutines. For non Opentype CFF fonts it also gets the glyph + * widths. + * + * When we find a subroutine operator, the subroutine is marked as in + * use and recursively followed. The subroutine number is the value on + * the top of the stack when the subroutine operator is executed. In + * most fonts the subroutine number is encoded in an integer + * immediately preceding the subroutine operator. However it is + * possible for the subroutine number on the stack to be the result of + * a computation (in which case there will be an operator preceding + * the subroutine operator). If this occurs, subroutine subsetting is + * disabled since we can't easily determine which subroutines are + * used. + * + * The width, if present, is the first integer in the charstring. The + * only way to confirm if the integer at the start of the charstring is + * the width is when the first stack clearing operator is parsed, + * check if there is an extra integer left over on the stack. + * + * When the first stack clearing operator is encountered + * type2_find_width is set to FALSE and type2_found_width is set to + * TRUE if an extra argument is found, otherwise FALSE. + */ static cairo_status_t -cairo_cff_font_subset_charstrings (cairo_cff_font_t *font) +cairo_cff_parse_charstring (cairo_cff_font_t *font, + unsigned char *charstring, int length, + int glyph_id, + cairo_bool_t need_width) +{ + unsigned char *p = charstring; + unsigned char *end = charstring + length; + int integer; + int hint_bytes; + int sub_num; + cff_index_element_t *element; + int fd; + + while (p < end) { + if (*p == 28 || *p >= 32) { + /* Integer value */ + p = type2_decode_integer (p, &integer); + font->type2_stack_size++; + font->type2_stack_top_value = integer; + font->type2_stack_top_is_int = TRUE; + if (!font->type2_seen_first_int) { + font->type2_width = integer; + font->type2_seen_first_int = TRUE; + } + } else if (*p == TYPE2_hstem || *p == TYPE2_vstem || + *p == TYPE2_hstemhm || *p == TYPE2_vstemhm) { + /* Hint operator. The number of hints declared by the + * operator depends on the size of the stack. */ + font->type2_stack_top_is_int = FALSE; + font->type2_num_hints += font->type2_stack_size/2; + if (font->type2_find_width && font->type2_stack_size % 2) + font->type2_found_width = TRUE; + + font->type2_stack_size = 0; + font->type2_find_width = FALSE; + p++; + } else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) { + /* Hintmask operator. These operators are followed by a + * variable length mask where the length depends on the + * number of hints declared. The first time this is called + * it is also an implicit vstem if there are arguments on + * the stack. */ + if (font->type2_hintmask_bytes == 0) { + font->type2_stack_top_is_int = FALSE; + font->type2_num_hints += font->type2_stack_size/2; + if (font->type2_find_width && font->type2_stack_size % 2) + font->type2_found_width = TRUE; + + font->type2_stack_size = 0; + font->type2_find_width = FALSE; + font->type2_hintmask_bytes = (font->type2_num_hints+7)/8; + } + + hint_bytes = font->type2_hintmask_bytes; + p++; + p += hint_bytes; + } else if (*p == TYPE2_rmoveto) { + if (font->type2_find_width && font->type2_stack_size > 2) + font->type2_found_width = TRUE; + + font->type2_stack_size = 0; + font->type2_find_width = FALSE; + font->type2_has_path = TRUE; + p++; + } else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) { + if (font->type2_find_width && font->type2_stack_size > 1) + font->type2_found_width = TRUE; + + font->type2_stack_size = 0; + font->type2_find_width = FALSE; + font->type2_has_path = TRUE; + p++; + } else if (*p == TYPE2_endchar) { + if (!font->type2_has_path && font->type2_stack_size > 3) + return CAIRO_INT_STATUS_UNSUPPORTED; /* seac (Ref Appendix C of Type 2 Charstring Format */ + + if (font->type2_find_width && font->type2_stack_size > 0) + font->type2_found_width = TRUE; + + return CAIRO_STATUS_SUCCESS; + } else if (*p == TYPE2_callsubr) { + /* call to local subroutine */ + if (! font->type2_stack_top_is_int) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p++; + font->type2_stack_top_is_int = FALSE; + font->type2_stack_size--; + if (font->type2_find_width && font->type2_stack_size == 0) + font->type2_seen_first_int = FALSE; + + if (font->is_cid) { + fd = font->fdselect[glyph_id]; + sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd]; + element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num); + if (! font->fd_local_subs_used[fd][sub_num]) { + font->fd_local_subs_used[fd][sub_num] = TRUE; + cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); + } + } else { + sub_num = font->type2_stack_top_value + font->local_sub_bias; + if (sub_num >= _cairo_array_num_elements(&font->local_sub_index)) + return CAIRO_INT_STATUS_UNSUPPORTED; + element = _cairo_array_index (&font->local_sub_index, sub_num); + if (! font->local_subs_used[sub_num] || + (need_width && !font->type2_found_width)) + { + font->local_subs_used[sub_num] = TRUE; + cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); + } + } + font->type2_nesting_level--; + } else if (*p == TYPE2_callgsubr) { + /* call to global subroutine */ + if (! font->type2_stack_top_is_int) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p++; + font->type2_stack_size--; + font->type2_stack_top_is_int = FALSE; + if (font->type2_find_width && font->type2_stack_size == 0) + font->type2_seen_first_int = FALSE; + + sub_num = font->type2_stack_top_value + font->global_sub_bias; + element = _cairo_array_index (&font->global_sub_index, sub_num); + if (! font->global_subs_used[sub_num] || + (need_width && !font->type2_found_width)) + { + font->global_subs_used[sub_num] = TRUE; + cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); + } + font->type2_nesting_level--; + } else if (*p == 12) { + /* 2 byte instruction */ + + /* All the 2 byte operators are either not valid before a + * stack clearing operator or they are one of the + * arithmetic, storage, or conditional operators. */ + if (need_width && font->type2_find_width) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 2; + font->type2_stack_top_is_int = FALSE; + } else { + /* 1 byte instruction */ + p++; + font->type2_stack_top_is_int = FALSE; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_find_width_and_subroutines_used (cairo_cff_font_t *font, + unsigned char *charstring, int length, + int glyph_id, int subset_id) +{ + cairo_status_t status; + int width; + int fd; + + font->type2_stack_size = 0; + font->type2_stack_top_value = 0;; + font->type2_stack_top_is_int = FALSE; + font->type2_num_hints = 0; + font->type2_hintmask_bytes = 0; + font->type2_nesting_level = 0; + font->type2_seen_first_int = FALSE; + font->type2_find_width = TRUE; + font->type2_found_width = FALSE; + font->type2_width = 0; + font->type2_has_path = FALSE; + + status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE); + if (status) + return status; + + if (!font->is_opentype) { + if (font->is_cid) { + fd = font->fdselect[glyph_id]; + if (font->type2_found_width) + width = font->fd_nominal_width[fd] + font->type2_width; + else + width = font->fd_default_width[fd]; + } else { + if (font->type2_found_width) + width = font->nominal_width + font->type2_width; + else + width = font->default_width; + } + font->widths[subset_id] = width; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_get_gid_for_cid (cairo_cff_font_t *font, unsigned long cid, unsigned long *gid) +{ + unsigned char *p; + unsigned long first_gid; + unsigned long first_cid; + int num_left; + unsigned long c, g; + + if (cid == 0) { + *gid = 0; + return CAIRO_STATUS_SUCCESS; + } + + switch (font->charset[0]) { + /* Format 0 */ + case 0: + p = font->charset + 1; + g = 1; + while (g <= (unsigned)font->num_glyphs && p < font->data_end) { + c = get_unaligned_be16 (p); + if (c == cid) { + *gid = g; + return CAIRO_STATUS_SUCCESS; + } + g++; + p += 2; + } + break; + + /* Format 1 */ + case 1: + first_gid = 1; + p = font->charset + 1; + while (first_gid <= (unsigned)font->num_glyphs && p + 2 < font->data_end) { + first_cid = get_unaligned_be16 (p); + num_left = p[2]; + if (cid >= first_cid && cid <= first_cid + num_left) { + *gid = first_gid + cid - first_cid; + return CAIRO_STATUS_SUCCESS; + } + first_gid += num_left + 1; + p += 3; + } + break; + + /* Format 2 */ + case 2: + first_gid = 1; + p = font->charset + 1; + while (first_gid <= (unsigned)font->num_glyphs && p + 3 < font->data_end) { + first_cid = get_unaligned_be16 (p); + num_left = get_unaligned_be16 (p+2); + if (cid >= first_cid && cid <= first_cid + num_left) { + *gid = first_gid + cid - first_cid; + return CAIRO_STATUS_SUCCESS; + } + first_gid += num_left + 1; + p += 4; + } + break; + + default: + break; + } + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font) { cff_index_element_t *element; unsigned int i; - cairo_status_t status; + cairo_int_status_t status; + unsigned long glyph, cid; + font->subset_subroutines = TRUE; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - element = _cairo_array_index (&font->charstrings_index, - font->scaled_font_subset->glyphs[i]); + if (font->is_cid && !font->is_opentype) { + cid = font->scaled_font_subset->glyphs[i]; + status = cairo_cff_font_get_gid_for_cid (font, cid, &glyph); + if (unlikely (status)) + return status; + } else { + glyph = font->scaled_font_subset->glyphs[i]; + } + element = _cairo_array_index (&font->charstrings_index, glyph); status = cff_index_append (&font->charstrings_subset_index, element->data, element->length); if (unlikely (status)) return status; + + if (font->subset_subroutines) { + status = cairo_cff_find_width_and_subroutines_used (font, + element->data, element->length, + glyph, i); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* If parsing the charstrings fails we embed all the + * subroutines. But if the font is not opentype we + * need to successfully parse all charstrings to get + * the widths. */ + font->subset_subroutines = FALSE; + if (!font->is_opentype) + return status; + } else if (unlikely (status)) { + return status; + } + } } return CAIRO_STATUS_SUCCESS; @@ -1129,6 +1825,8 @@ cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) unsigned int i; int fd; int *reverse_map; + unsigned long cid, gid; + cairo_int_status_t status; font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); @@ -1152,7 +1850,18 @@ cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) font->num_subset_fontdicts = 0; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { - fd = font->fdselect[font->scaled_font_subset->glyphs[i]]; + if (font->is_opentype) { + gid = font->scaled_font_subset->glyphs[i]; + } else { + cid = font->scaled_font_subset->glyphs[i]; + status = cairo_cff_font_get_gid_for_cid (font, cid, &gid); + if (unlikely (status)) { + free (reverse_map); + return status; + } + } + + fd = font->fdselect[gid]; if (reverse_map[fd] < 0) { font->fd_subset_map[font->num_subset_fontdicts] = fd; reverse_map[fd] = font->num_subset_fontdicts++; @@ -1173,7 +1882,7 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) cairo_status_t status; font->num_fontdicts = 1; - font->fd_dict = malloc (sizeof (cairo_hash_table_t *)); + font->fd_dict = _cairo_malloc (sizeof (cairo_hash_table_t *)); if (unlikely (font->fd_dict == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1184,11 +1893,11 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - font->fd_subset_map = malloc (sizeof (int)); + font->fd_subset_map = _cairo_malloc (sizeof (int)); if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font->private_dict_offset = malloc (sizeof (int)); + font->private_dict_offset = _cairo_malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1233,30 +1942,68 @@ cairo_cff_font_subset_strings (cairo_cff_font_t *font) return status; } +/* The Euro is the only the only character in the winansi encoding + * with a glyph name that is not a CFF standard string. As the strings + * are written before the charset, we need to check during the + * subsetting phase if the Euro glyph is required and add the + * glyphname to the list of strings to write out. + */ +static cairo_status_t +cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font) +{ + cairo_status_t status; + unsigned int i; + int ch; + const char *euro = "Euro"; + + for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + ch = font->scaled_font_subset->to_latin_char[i]; + if (ch == 128) { + font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append_copy (&font->strings_subset_index, + (unsigned char *)euro, strlen(euro)); + return status; + } + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t cairo_cff_font_subset_font (cairo_cff_font_t *font) { cairo_status_t status; - status = cairo_cff_font_set_ros_strings (font); - if (unlikely (status)) - return status; + if (!font->scaled_font_subset->is_latin) { + status = cairo_cff_font_set_ros_strings (font); + if (unlikely (status)) + return status; + } - status = cairo_cff_font_subset_charstrings (font); + status = cairo_cff_font_subset_charstrings_and_subroutines (font); if (unlikely (status)) return status; - if (font->is_cid) - status = cairo_cff_font_subset_fontdict (font); - else - status = cairo_cff_font_create_cid_fontdict (font); - if (unlikely (status)) - return status; + if (!font->scaled_font_subset->is_latin) { + if (font->is_cid) + status = cairo_cff_font_subset_fontdict (font); + else + status = cairo_cff_font_create_cid_fontdict (font); + if (unlikely (status)) + return status; + } else { + font->private_dict_offset = _cairo_malloc (sizeof (int)); + if (unlikely (font->private_dict_offset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } status = cairo_cff_font_subset_strings (font); if (unlikely (status)) return status; + if (font->scaled_font_subset->is_latin) + status = cairo_cff_font_add_euro_charset_string (font); + return status; } @@ -1300,8 +2047,8 @@ cairo_cff_font_write_name (cairo_cff_font_t *font) cff_index_init (&index); status = cff_index_append_copy (&index, - (unsigned char *) font->subset_font_name, - strlen(font->subset_font_name)); + (unsigned char *) font->ps_name, + strlen(font->ps_name)); if (unlikely (status)) goto FAIL; @@ -1370,9 +2117,45 @@ cairo_cff_font_write_strings (cairo_cff_font_t *font) static cairo_status_t cairo_cff_font_write_global_subrs (cairo_cff_font_t *font) { + unsigned int i; + unsigned char return_op = TYPE2_return; + + /* poppler and fontforge don't like zero length subroutines so we + * replace unused subroutines with a 'return' instruction. */ + if (font->subset_subroutines) { + for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) { + if (! font->global_subs_used[i]) + cff_index_set_object (&font->global_sub_index, i, &return_op, 1); + } + } + return cff_index_write (&font->global_sub_index, &font->output); } +static cairo_status_t +cairo_cff_font_write_encoding (cairo_cff_font_t *font) +{ + unsigned char buf[2]; + cairo_status_t status; + unsigned int i; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP); + buf[0] = 0; /* Format 0 */ + buf[1] = font->scaled_font_subset->num_glyphs - 1; + status = _cairo_array_append_multiple (&font->output, buf, 2); + if (unlikely (status)) + return status; + + for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + unsigned char ch = font->scaled_font_subset->to_latin_char[i]; + status = _cairo_array_append (&font->output, &ch); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t cairo_cff_font_write_fdselect (cairo_cff_font_t *font) { @@ -1404,30 +2187,115 @@ cairo_cff_font_write_fdselect (cairo_cff_font_t *font) byte = 3; status = _cairo_array_append (&font->output, &byte); - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (0); status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); byte = 0; status = _cairo_array_append (&font->output, &byte); - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (font->scaled_font_subset->num_glyphs); status = _cairo_array_append_multiple (&font->output, &word, 2); - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Winansi to CFF standard strings mapping for characters 128 to 255 */ +static const int winansi_to_cff_std_string[] = { + /* 128 */ + 0, 0, 117, 101, 118, 121, 112, 113, + 126, 122, 192, 107, 142, 0, 199, 0, + /* 144 */ + 0, 65, 8, 105, 119, 116, 111, 137, + 127, 153, 221, 108, 148, 0, 228, 198, + /* 160 */ + 0, 96, 97, 98, 103, 100, 160, 102, + 131, 170, 139, 106, 151, 0, 165, 128, + /* 176 */ + 161, 156, 164, 169, 125, 152, 115, 114, + 133, 150, 143, 120, 158, 155, 163, 123, + /* 192 */ + 174, 171, 172, 176, 173, 175, 138, 177, + 181, 178, 179, 180, 185, 182, 183, 184, + /* 208 */ + 154, 186, 190, 187, 188, 191, 189, 168, + 141, 196, 193, 194, 195, 197, 157, 149, + /* 224 */ + 203, 200, 201, 205, 202, 204, 144, 206, + 210, 207, 208, 209, 214, 211, 212, 213, + /* 240 */ + 167, 215, 219, 216, 217, 220, 218, 159, + 147, 225, 222, 223, 224, 226, 162, 227, +}; + +static int +cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t *font, int ch) +{ + int sid; + + if (ch == 39) { + sid = 104; + + } else if (ch == 96) { + sid = 124; + + } else if (ch >= 32 && ch <= 126) { + sid = ch - 31; + + } else if (ch == 128) { + assert (font->euro_sid >= NUM_STD_STRINGS); + sid = font->euro_sid; + + } else if (ch >= 128 && ch <= 255) { + sid = winansi_to_cff_std_string[ch - 128]; + + } else { + sid = 0; + } + + return sid; +} + +static cairo_status_t +cairo_cff_font_write_type1_charset (cairo_cff_font_t *font) +{ + unsigned char format = 0; + unsigned int i; + int ch, sid; + cairo_status_t status; + uint16_t sid_be16; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); + status = _cairo_array_append (&font->output, &format); + if (unlikely (status)) + return status; + + for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + ch = font->scaled_font_subset->to_latin_char[i]; + sid = cairo_cff_font_get_sid_for_winansi_char (font, ch); + if (unlikely (status)) + return status; + + sid_be16 = cpu_to_be16(sid); + status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16)); + if (unlikely (status)) + return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t -cairo_cff_font_write_charset (cairo_cff_font_t *font) +cairo_cff_font_write_cid_charset (cairo_cff_font_t *font) { unsigned char byte; uint16_t word; @@ -1467,7 +2335,7 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) unsigned int i; cairo_int_status_t status; unsigned int offset_array; - uint32_t *offset_array_ptr; + unsigned char *offset_array_ptr; int offset_base; uint16_t count; uint8_t offset_size = 4; @@ -1488,7 +2356,7 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) if (unlikely (status)) return status; offset_base = _cairo_array_num_elements (&font->output) - 1; - *offset_array_ptr = cpu_to_be32(1); + put_unaligned_be32(1, offset_array_ptr); offset_array += sizeof(uint32_t); for (i = 0; i < font->num_subset_fontdicts; i++) { status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]], @@ -1496,8 +2364,9 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) if (unlikely (status)) return status; - offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array); - *offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base); + offset_array_ptr = _cairo_array_index (&font->output, offset_array); + put_unaligned_be32 (_cairo_array_num_elements (&font->output) - offset_base, + offset_array_ptr); offset_array += sizeof(uint32_t); } @@ -1506,9 +2375,9 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) static cairo_status_t cairo_cff_font_write_private_dict (cairo_cff_font_t *font, - int dict_num, - cairo_hash_table_t *parent_dict, - cairo_hash_table_t *private_dict) + int dict_num, + cairo_hash_table_t *parent_dict, + cairo_hash_table_t *private_dict) { int offset; int size; @@ -1539,7 +2408,8 @@ static cairo_status_t cairo_cff_font_write_local_sub (cairo_cff_font_t *font, int dict_num, cairo_hash_table_t *private_dict, - cairo_array_t *local_sub_index) + cairo_array_t *local_sub_index, + cairo_bool_t *local_subs_used) { int offset; int size; @@ -1547,6 +2417,8 @@ cairo_cff_font_write_local_sub (cairo_cff_font_t *font, unsigned char *buf_end; unsigned char *p; cairo_status_t status; + unsigned int i; + unsigned char return_op = TYPE2_return; if (_cairo_array_num_elements (local_sub_index) > 0) { /* Write local subroutines and update offset in private @@ -1558,6 +2430,16 @@ cairo_cff_font_write_local_sub (cairo_cff_font_t *font, assert (offset > 0); p = _cairo_array_index (&font->output, offset); memcpy (p, buf, buf_end - buf); + + /* poppler and fontforge don't like zero length subroutines so + * we replace unused subroutines with a 'return' instruction. + */ + if (font->subset_subroutines) { + for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) { + if (! local_subs_used[i]) + cff_index_set_object (local_sub_index, i, &return_op, 1); + } + } status = cff_index_write (local_sub_index, &font->output); if (unlikely (status)) return status; @@ -1589,7 +2471,8 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) font, i, font->fd_private_dict[font->fd_subset_map[i]], - &font->fd_local_sub_index[font->fd_subset_map[i]]); + &font->fd_local_sub_index[font->fd_subset_map[i]], + font->fd_local_subs_used[font->fd_subset_map[i]]); if (unlikely (status)) return status; } @@ -1604,7 +2487,8 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) status = cairo_cff_font_write_local_sub (font, 0, font->private_dict, - &font->local_sub_index); + &font->local_sub_index, + font->local_subs_used); if (unlikely (status)) return status; } @@ -1612,32 +2496,76 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t *font) +{ + cairo_int_status_t status; + + status = cairo_cff_font_write_private_dict (font, + 0, + font->top_dict, + font->private_dict); + if (unlikely (status)) + return status; + + status = cairo_cff_font_write_local_sub (font, + 0, + font->private_dict, + &font->local_sub_index, + font->local_subs_used); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + + typedef cairo_status_t (*font_write_t) (cairo_cff_font_t *font); -static const font_write_t font_write_funcs[] = { +static const font_write_t font_write_cid_funcs[] = { cairo_cff_font_write_header, cairo_cff_font_write_name, cairo_cff_font_write_top_dict, cairo_cff_font_write_strings, cairo_cff_font_write_global_subrs, - cairo_cff_font_write_charset, + cairo_cff_font_write_cid_charset, cairo_cff_font_write_fdselect, cairo_cff_font_write_charstrings, cairo_cff_font_write_cid_fontdict, cairo_cff_font_write_cid_private_dict_and_local_sub, }; +static const font_write_t font_write_type1_funcs[] = { + cairo_cff_font_write_header, + cairo_cff_font_write_name, + cairo_cff_font_write_top_dict, + cairo_cff_font_write_strings, + cairo_cff_font_write_global_subrs, + cairo_cff_font_write_encoding, + cairo_cff_font_write_type1_charset, + cairo_cff_font_write_charstrings, + cairo_cff_font_write_type1_private_dict_and_local_sub, +}; + static cairo_status_t cairo_cff_font_write_subset (cairo_cff_font_t *font) { cairo_int_status_t status; unsigned int i; - for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) { - status = font_write_funcs[i] (font); - if (unlikely (status)) - return status; + if (font->scaled_font_subset->is_latin) { + for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) { + status = font_write_type1_funcs[i] (font); + if (unlikely (status)) + return status; + } + } else { + for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) { + status = font_write_cid_funcs[i] (font); + if (unlikely (status)) + return status; + } } return CAIRO_STATUS_SUCCESS; @@ -1654,6 +2582,17 @@ cairo_cff_font_generate (cairo_cff_font_t *font, if (unlikely (status)) return status; + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->ps_name == NULL) { + font->ps_name = _cairo_malloc (30); + if (unlikely (font->ps_name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + snprintf(font->ps_name, 30, "CairoFont-%u-%u", + font->scaled_font_subset->font_id, + font->scaled_font_subset->subset_id); + } + status = cairo_cff_font_subset_font (font); if (unlikely (status)) return status; @@ -1662,6 +2601,7 @@ cairo_cff_font_generate (cairo_cff_font_t *font, if (unlikely (status)) return status; + *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); @@ -1677,7 +2617,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) unsigned int i; tt_hhea_t hhea; int num_hmetrics; - unsigned char buf[10]; + uint16_t short_entry; int glyph_index; cairo_int_status_t status; @@ -1689,7 +2629,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) return status; num_hmetrics = be16_to_cpu (hhea.num_hmetrics); - for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { glyph_index = font->scaled_font_subset->glyphs[i]; long_entry_size = 2 * sizeof (int16_t); short_entry_size = sizeof (int16_t); @@ -1697,7 +2637,8 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, glyph_index * long_entry_size, - buf, &short_entry_size); + (unsigned char *) &short_entry, + &short_entry_size); if (unlikely (status)) return status; } @@ -1706,75 +2647,74 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, - buf, &short_entry_size); + (unsigned char *) &short_entry, + &short_entry_size); if (unlikely (status)) return status; } - font->widths[i] = be16_to_cpu (*((int16_t*)buf)); + font->widths[i] = be16_to_cpu (short_entry); } return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, - cairo_cff_font_t **font_return, - const char *subset_name) +static cairo_bool_t +check_fontdata_is_cff (const unsigned char *data, long length) { - const cairo_scaled_font_backend_t *backend; + cff_header_t *header; + + if (length < (long)sizeof (cff_header_t)) + return FALSE; + + header = (cff_header_t *) data; + if (header->major == 1 && + header->minor == 0 && + header->header_size == 4) + { + return TRUE; + } + + return FALSE; +} + +static cairo_int_status_t +_cairo_cff_font_load_opentype_cff (cairo_cff_font_t *font) +{ + const cairo_scaled_font_backend_t *backend = font->backend; cairo_status_t status; - cairo_cff_font_t *font; tt_head_t head; tt_hhea_t hhea; unsigned long size, data_length; - backend = scaled_font_subset->scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; data_length = 0; - status = backend->load_truetype_table( scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_CFF, 0, NULL, &data_length); - if (unlikely (status)) + if (status) return status; size = sizeof (tt_head_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char *) &head, &size); if (unlikely (status)) return status; size = sizeof (tt_hhea_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); if (unlikely (status)) return status; size = 0; - status = backend->load_truetype_table (scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, 0, NULL, &size); if (unlikely (status)) return status; - font = malloc (sizeof (cairo_cff_font_t)); - if (unlikely (font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->backend = backend; - font->scaled_font_subset = scaled_font_subset; - - _cairo_array_init (&font->output, sizeof (char)); - status = _cairo_array_grow_by (&font->output, 4096); - if (unlikely (status)) - goto fail2; - - font->subset_font_name = strdup (subset_name); - if (unlikely (font->subset_font_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } font->x_min = (int16_t) be16_to_cpu (head.x_min); font->y_min = (int16_t) be16_to_cpu (head.y_min); font->x_max = (int16_t) be16_to_cpu (head.x_max); @@ -1786,56 +2726,130 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, font->units_per_em = 1000; font->font_name = NULL; - status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font, &font->ps_name, &font->font_name); if (_cairo_status_is_error (status)) - goto fail3; + return status; - /* If the PS name is not found, create a CairoFont-x-y name. */ - if (font->ps_name == NULL) { - font->ps_name = malloc (30); - if (unlikely (font->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } + font->is_opentype = TRUE; + font->data_length = data_length; + font->data = _cairo_malloc (data_length); + if (unlikely (font->data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - snprintf(font->ps_name, 30, "CairoFont-%u-%u", - scaled_font_subset->font_id, - scaled_font_subset->subset_id); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_CFF, 0, font->data, + &font->data_length); + if (unlikely (status)) + return status; + + if (!check_fontdata_is_cff (font->data, data_length)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_cff_font_load_cff (cairo_cff_font_t *font) +{ + const cairo_scaled_font_backend_t *backend = font->backend; + cairo_status_t status; + unsigned long data_length; + + if (!backend->load_type1_data) + return CAIRO_INT_STATUS_UNSUPPORTED; + + data_length = 0; + status = backend->load_type1_data (font->scaled_font_subset->scaled_font, + 0, NULL, &data_length); + if (unlikely (status)) + return status; + + font->font_name = NULL; + font->is_opentype = FALSE; + font->data_length = data_length; + font->data = _cairo_malloc (data_length); + if (unlikely (font->data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font, + 0, font->data, &font->data_length); + if (unlikely (status)) + return status; + + if (!check_fontdata_is_cff (font->data, data_length)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_cff_font_t **font_return, + const char *subset_name) +{ + const cairo_scaled_font_backend_t *backend; + cairo_int_status_t status; + cairo_bool_t is_synthetic; + cairo_cff_font_t *font; + + backend = scaled_font_subset->scaled_font->backend; + + /* We need to use a fallback font if this font differs from the CFF outlines. */ + if (backend->is_synthetic) { + status = backend->is_synthetic (scaled_font_subset->scaled_font, &is_synthetic); + if (unlikely (status)) + return status; + + if (is_synthetic) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + font = calloc (1, sizeof (cairo_cff_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = backend; + font->scaled_font_subset = scaled_font_subset; + + status = _cairo_cff_font_load_opentype_cff (font); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_cff_font_load_cff (font); + if (status) + goto fail1; + + font->data_end = font->data + font->data_length; + _cairo_array_init (&font->output, sizeof (char)); + status = _cairo_array_grow_by (&font->output, 4096); + if (unlikely (status)) + goto fail2; + + font->subset_font_name = strdup (subset_name); + if (unlikely (font->subset_font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; } font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; + goto fail3; } - status = cairo_cff_font_create_set_widths (font); - if (unlikely (status)) - goto fail5; - - font->data_length = data_length; - font->data = malloc (data_length); - if (unlikely (font->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail5; + if (font->is_opentype) { + status = cairo_cff_font_create_set_widths (font); + if (unlikely (status)) + goto fail4; } - status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font, - TT_TAG_CFF, 0, font->data, - &font->data_length); - if (unlikely (status)) - goto fail6; - - font->data_end = font->data + font->data_length; status = cff_dict_init (&font->top_dict); if (unlikely (status)) - goto fail6; + goto fail4; status = cff_dict_init (&font->private_dict); if (unlikely (status)) - goto fail7; + goto fail5; cff_index_init (&font->strings_index); cff_index_init (&font->charstrings_index); @@ -1843,31 +2857,35 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cff_index_init (&font->local_sub_index); cff_index_init (&font->charstrings_subset_index); cff_index_init (&font->strings_subset_index); + font->euro_sid = 0; font->fdselect = NULL; font->fd_dict = NULL; font->fd_private_dict = NULL; font->fd_local_sub_index = NULL; + font->fd_local_sub_bias = NULL; font->fdselect_subset = NULL; font->fd_subset_map = NULL; font->private_dict_offset = NULL; + font->global_subs_used = NULL; + font->local_subs_used = NULL; + font->fd_local_subs_used = NULL; *font_return = font; return CAIRO_STATUS_SUCCESS; -fail7: - _cairo_hash_table_destroy (font->top_dict); -fail6: - free (font->data); fail5: - free (font->widths); + _cairo_hash_table_destroy (font->top_dict); fail4: - if (font->font_name) - free (font->font_name); + free (font->widths); fail3: free (font->subset_font_name); fail2: + free (font->ps_name); _cairo_array_fini (&font->output); +fail1: + free (font->data); + free (font->font_name); free (font); return status; @@ -1879,8 +2897,7 @@ cairo_cff_font_destroy (cairo_cff_font_t *font) unsigned int i; free (font->widths); - if (font->font_name) - free (font->font_name); + free (font->font_name); free (font->ps_name); free (font->subset_font_name); _cairo_array_fini (&font->output); @@ -1902,16 +2919,14 @@ cairo_cff_font_destroy (cairo_cff_font_t *font) } free (font->fd_dict); } - if (font->fd_subset_map) - free (font->fd_subset_map); - if (font->private_dict_offset) - free (font->private_dict_offset); + free (font->global_subs_used); + free (font->local_subs_used); + free (font->fd_subset_map); + free (font->private_dict_offset); if (font->is_cid) { - if (font->fdselect) - free (font->fdselect); - if (font->fdselect_subset) - free (font->fdselect_subset); + free (font->fdselect); + free (font->fdselect_subset); if (font->fd_private_dict) { for (i = 0; i < font->num_fontdicts; i++) { if (font->fd_private_dict[i]) @@ -1924,10 +2939,18 @@ cairo_cff_font_destroy (cairo_cff_font_t *font) cff_index_fini (&font->fd_local_sub_index[i]); free (font->fd_local_sub_index); } + free (font->fd_local_sub_bias); + if (font->fd_local_subs_used) { + for (i = 0; i < font->num_fontdicts; i++) { + free (font->fd_local_subs_used[i]); + } + free (font->fd_local_subs_used); + } + free (font->fd_default_width); + free (font->fd_nominal_width); } - if (font->data) - free (font->data); + free (font->data); free (font); } @@ -1958,13 +2981,13 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, } if (font->font_name) { - cff_subset->font_name = strdup (font->font_name); - if (cff_subset->font_name == NULL) { + cff_subset->family_name_utf8 = strdup (font->font_name); + if (cff_subset->family_name_utf8 == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } } else { - cff_subset->font_name = NULL; + cff_subset->family_name_utf8 = NULL; } cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); @@ -1982,7 +3005,7 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, cff_subset->ascent = (double)font->ascent/font->units_per_em; cff_subset->descent = (double)font->descent/font->units_per_em; - cff_subset->data = malloc (length); + cff_subset->data = _cairo_malloc (length); if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; @@ -1998,8 +3021,7 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, fail4: free (cff_subset->widths); fail3: - if (cff_subset->font_name) - free (cff_subset->font_name); + free (cff_subset->family_name_utf8); fail2: free (cff_subset->ps_name); fail1: @@ -2012,12 +3034,118 @@ void _cairo_cff_subset_fini (cairo_cff_subset_t *subset) { free (subset->ps_name); - if (subset->font_name) - free (subset->font_name); + free (subset->family_name_utf8); free (subset->widths); free (subset->data); } +cairo_bool_t +_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_int_status_t status; + unsigned char *data; + unsigned long data_length; + unsigned char *current_ptr; + unsigned char *data_end; + cff_header_t *header; + cff_index_element_t *element; + cairo_hash_table_t *top_dict; + cairo_array_t index; + int size; + cairo_bool_t is_cid = FALSE; + + backend = scaled_font->backend; + data = NULL; + data_length = 0; + status = CAIRO_INT_STATUS_UNSUPPORTED; + /* Try to load an OpenType/CFF font */ + if (backend->load_truetype_table && + (status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, + 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS) + { + data = _cairo_malloc (data_length); + if (unlikely (data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, + 0, data, &data_length); + if (unlikely (status)) + goto fail1; + } + /* Try to load a CFF font */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED && + backend->load_type1_data && + (status = backend->load_type1_data (scaled_font, + 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS) + { + data = _cairo_malloc (data_length); + if (unlikely (data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + status = backend->load_type1_data (scaled_font, 0, data, &data_length); + if (unlikely (status)) + goto fail1; + } + if (status) + goto fail1; + + /* Check if it looks like a CFF font */ + if (!check_fontdata_is_cff (data, data_length)) + goto fail1; + + data_end = data + data_length; + + /* skip header */ + if (data_length < sizeof (cff_header_t)) + goto fail1; + + header = (cff_header_t *) data; + current_ptr = data + header->header_size; + + /* skip name */ + cff_index_init (&index); + status = cff_index_read (&index, ¤t_ptr, data_end); + cff_index_fini (&index); + + if (status) + goto fail1; + + /* read top dict */ + cff_index_init (&index); + status = cff_index_read (&index, ¤t_ptr, data_end); + if (unlikely (status)) + goto fail2; + + status = cff_dict_init (&top_dict); + if (unlikely (status)) + goto fail2; + + element = _cairo_array_index (&index, 0); + status = cff_dict_read (top_dict, element->data, element->length); + if (unlikely (status)) + goto fail3; + + /* check for ROS operator indicating a CID font */ + if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL) + is_cid = TRUE; + +fail3: + cff_dict_fini (top_dict); + +fail2: + cff_index_fini (&index); + +fail1: + free (data); + + return is_cid; +} + static cairo_int_status_t _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_cff_font_t **font_return, @@ -2026,7 +3154,7 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset cairo_status_t status; cairo_cff_font_t *font; - font = malloc (sizeof (cairo_cff_font_t)); + font = _cairo_malloc (sizeof (cairo_cff_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2082,6 +3210,9 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset cff_index_init (&font->local_sub_index); cff_index_init (&font->charstrings_subset_index); cff_index_init (&font->strings_subset_index); + font->global_subs_used = NULL; + font->local_subs_used = NULL; + font->subset_subroutines = FALSE; font->fdselect = NULL; font->fd_dict = NULL; font->fd_private_dict = NULL; @@ -2099,8 +3230,7 @@ fail5: fail4: free (font->widths); fail3: - if (font->font_name) - free (font->font_name); + free (font->font_name); free (font->ps_name); fail2: free (font->subset_font_name); @@ -2120,8 +3250,9 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font, cff_header_t header; cairo_array_t *charstring; unsigned char buf[40]; - unsigned char *end_buf; + unsigned char *end_buf, *end_buf2; unsigned int i; + int sid; /* Create header */ header.major = 1; @@ -2132,6 +3263,28 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font, /* Create Top Dict */ font->is_cid = FALSE; + + snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u", + font->scaled_font_subset->font_id, + font->scaled_font_subset->subset_id); + sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append_copy (&font->strings_subset_index, + (unsigned char *)buf, + strlen((char*)buf)); + if (unlikely (status)) + return status; + + end_buf = encode_integer (buf, sid); + status = cff_dict_set_operands (font->top_dict, FULLNAME_OP, + buf, end_buf - buf); + if (unlikely (status)) + return status; + + status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP, + buf, end_buf - buf); + if (unlikely (status)) + return status; + end_buf = encode_integer (buf, type2_subset->x_min); end_buf = encode_integer (end_buf, type2_subset->y_min); end_buf = encode_integer (end_buf, type2_subset->x_max); @@ -2147,29 +3300,50 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font, if (unlikely (status)) return status; - status = cff_dict_set_operands (font->top_dict, - FDSELECT_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; - status = cff_dict_set_operands (font->top_dict, - FDARRAY_OP, buf, end_buf - buf); - if (unlikely (status)) - return status; + if (font->scaled_font_subset->is_latin) { + status = cff_dict_set_operands (font->top_dict, + ENCODING_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + /* Private has two operands - size and offset */ + end_buf2 = encode_integer_max (end_buf, 0); + cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf); + if (unlikely (status)) + return status; + + } else { + status = cff_dict_set_operands (font->top_dict, + FDSELECT_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + status = cff_dict_set_operands (font->top_dict, + FDARRAY_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + } status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); if (unlikely (status)) return status; - status = cairo_cff_font_set_ros_strings (font); - if (unlikely (status)) - return status; + if (!font->scaled_font_subset->is_latin) { + status = cairo_cff_font_set_ros_strings (font); + if (unlikely (status)) + return status; - /* Create CID FD dictionary */ - status = cairo_cff_font_create_cid_fontdict (font); - if (unlikely (status)) - return status; + /* Create CID FD dictionary */ + status = cairo_cff_font_create_cid_fontdict (font); + if (unlikely (status)) + return status; + } else { + font->private_dict_offset = _cairo_malloc (sizeof (int)); + if (unlikely (font->private_dict_offset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } /* Create charstrings */ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { @@ -2183,6 +3357,9 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font, return status; } + if (font->scaled_font_subset->is_latin) + status = cairo_cff_font_add_euro_charset_string (font); + status = cairo_cff_font_write_subset (font); if (unlikely (status)) return status; @@ -2217,7 +3394,7 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, if (unlikely (status)) goto fail2; - cff_subset->font_name = NULL; + cff_subset->family_name_utf8 = NULL; cff_subset->ps_name = strdup (font->ps_name); if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2240,7 +3417,7 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, cff_subset->ascent = (double)type2_subset.y_max/1000; cff_subset->descent = (double)type2_subset.y_min/1000; - cff_subset->data = malloc (length); + cff_subset->data = _cairo_malloc (length); if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; @@ -2248,7 +3425,6 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, memcpy (cff_subset->data, data, length); cff_subset->data_length = length; - cff_subset->data_length = length; _cairo_type2_charstrings_fini (&type2_subset); cairo_cff_font_destroy (font); diff --git a/gfx/cairo/cairo/src/cairo-clip-boxes.c b/gfx/cairo/cairo/src/cairo-clip-boxes.c new file mode 100644 index 000000000000..aaddeb7f7be5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-boxes.c @@ -0,0 +1,609 @@ +/* -*- 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 © 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): + * Carl D. Worth + * Kristian Høgsberg + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-box-inline.h" +#include "cairo-clip-inline.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-pattern-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-region-private.h" + +static inline int +pot (int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static cairo_bool_t +_cairo_clip_contains_rectangle_box (const cairo_clip_t *clip, + const cairo_rectangle_int_t *rect, + const cairo_box_t *box) +{ + int i; + + /* clip == NULL means no clip, so the clip contains everything */ + if (clip == NULL) + return TRUE; + + if (_cairo_clip_is_all_clipped (clip)) + return FALSE; + + /* If we have a non-trivial path, just say no */ + if (clip->path) + return FALSE; + + if (! _cairo_rectangle_contains_rectangle (&clip->extents, rect)) + return FALSE; + + if (clip->num_boxes == 0) + return TRUE; + + /* Check for a clip-box that wholly contains the rectangle */ + for (i = 0; i < clip->num_boxes; i++) { + if (box->p1.x >= clip->boxes[i].p1.x && + box->p1.y >= clip->boxes[i].p1.y && + box->p2.x <= clip->boxes[i].p2.x && + box->p2.y <= clip->boxes[i].p2.y) + { + return TRUE; + } + } + + return FALSE; +} + +cairo_bool_t +_cairo_clip_contains_box (const cairo_clip_t *clip, + const cairo_box_t *box) +{ + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (box, &rect); + return _cairo_clip_contains_rectangle_box(clip, &rect, box); +} + +cairo_bool_t +_cairo_clip_contains_rectangle (const cairo_clip_t *clip, + const cairo_rectangle_int_t *rect) +{ + cairo_box_t box; + + _cairo_box_from_rectangle_int (&box, rect); + return _cairo_clip_contains_rectangle_box (clip, rect, &box); +} + +cairo_clip_t * +_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) +{ + cairo_status_t status; + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes)) + clip = _cairo_clip_intersect_boxes (clip, &boxes); + else + clip = _cairo_clip_set_all_clipped (clip); + _cairo_boxes_fini (&boxes); + + return clip; +} + +static cairo_clip_t * +_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip, + const cairo_rectangle_int_t *r, + const cairo_box_t *box) +{ + cairo_box_t extents_box; + cairo_bool_t changed = FALSE; + int i, j; + + if (clip == NULL) { + clip = _cairo_clip_create (); + if (clip == NULL) + return _cairo_clip_set_all_clipped (clip); + } + + if (clip->num_boxes == 0) { + clip->boxes = &clip->embedded_box; + clip->boxes[0] = *box; + clip->num_boxes = 1; + if (clip->path == NULL) { + clip->extents = *r; + } else { + if (! _cairo_rectangle_intersect (&clip->extents, r)) + return _cairo_clip_set_all_clipped (clip); + } + if (clip->path == NULL) + clip->is_region = _cairo_box_is_pixel_aligned (box); + return clip; + } + + /* Does the new box wholly subsume the clip? Perform a cheap check + * for the common condition of a single clip rectangle. + */ + if (clip->num_boxes == 1 && + clip->boxes[0].p1.x >= box->p1.x && + clip->boxes[0].p1.y >= box->p1.y && + clip->boxes[0].p2.x <= box->p2.x && + clip->boxes[0].p2.y <= box->p2.y) + { + return clip; + } + + for (i = j = 0; i < clip->num_boxes; i++) { + cairo_box_t *b = &clip->boxes[j]; + + if (j != i) + *b = clip->boxes[i]; + + if (box->p1.x > b->p1.x) + b->p1.x = box->p1.x, changed = TRUE; + if (box->p2.x < b->p2.x) + b->p2.x = box->p2.x, changed = TRUE; + + if (box->p1.y > b->p1.y) + b->p1.y = box->p1.y, changed = TRUE; + if (box->p2.y < b->p2.y) + b->p2.y = box->p2.y, changed = TRUE; + + j += b->p2.x > b->p1.x && b->p2.y > b->p1.y; + } + clip->num_boxes = j; + + if (clip->num_boxes == 0) + return _cairo_clip_set_all_clipped (clip); + + if (! changed) + return clip; + + extents_box = clip->boxes[0]; + for (i = 1; i < clip->num_boxes; i++) { + if (clip->boxes[i].p1.x < extents_box.p1.x) + extents_box.p1.x = clip->boxes[i].p1.x; + + if (clip->boxes[i].p1.y < extents_box.p1.y) + extents_box.p1.y = clip->boxes[i].p1.y; + + if (clip->boxes[i].p2.x > extents_box.p2.x) + extents_box.p2.x = clip->boxes[i].p2.x; + + if (clip->boxes[i].p2.y > extents_box.p2.y) + extents_box.p2.y = clip->boxes[i].p2.y; + } + + if (clip->path == NULL) { + _cairo_box_round_to_rectangle (&extents_box, &clip->extents); + } else { + cairo_rectangle_int_t extents_rect; + + _cairo_box_round_to_rectangle (&extents_box, &extents_rect); + if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect)) + return _cairo_clip_set_all_clipped (clip); + } + + if (clip->region) { + cairo_region_destroy (clip->region); + clip->region = NULL; + } + + clip->is_region = FALSE; + return clip; +} + +cairo_clip_t * +_cairo_clip_intersect_box (cairo_clip_t *clip, + const cairo_box_t *box) +{ + cairo_rectangle_int_t r; + + if (_cairo_clip_is_all_clipped (clip)) + return clip; + + _cairo_box_round_to_rectangle (box, &r); + if (r.width == 0 || r.height == 0) + return _cairo_clip_set_all_clipped (clip); + + return _cairo_clip_intersect_rectangle_box (clip, &r, box); +} + +/* Copy a box set to a clip + * + * @param boxes The box set to copy from. + * @param clip The clip to copy to (return buffer). + * @returns Zero if the allocation failed (the clip will be set to + * all-clipped), otherwise non-zero. + */ +static cairo_bool_t +_cairo_boxes_copy_to_clip (const cairo_boxes_t *boxes, cairo_clip_t *clip) +{ + /* XXX cow-boxes? */ + if (boxes->num_boxes == 1) { + clip->boxes = &clip->embedded_box; + clip->boxes[0] = boxes->chunks.base[0]; + clip->num_boxes = 1; + return TRUE; + } + + clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes); + if (unlikely (clip->boxes == NULL)) + { + _cairo_clip_set_all_clipped (clip); + return FALSE; + } + + return TRUE; +} + +cairo_clip_t * +_cairo_clip_intersect_boxes (cairo_clip_t *clip, + const cairo_boxes_t *boxes) +{ + cairo_boxes_t clip_boxes; + cairo_box_t limits; + cairo_rectangle_int_t extents; + + if (_cairo_clip_is_all_clipped (clip)) + return clip; + + if (boxes->num_boxes == 0) + return _cairo_clip_set_all_clipped (clip); + + if (boxes->num_boxes == 1) + return _cairo_clip_intersect_box (clip, boxes->chunks.base); + + if (clip == NULL) + clip = _cairo_clip_create (); + + if (clip->num_boxes) { + _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes); + if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) { + clip = _cairo_clip_set_all_clipped (clip); + goto out; + } + + if (clip->boxes != &clip->embedded_box) + free (clip->boxes); + + clip->boxes = NULL; + boxes = &clip_boxes; + } + + if (boxes->num_boxes == 0) { + clip = _cairo_clip_set_all_clipped (clip); + goto out; + } + + _cairo_boxes_copy_to_clip (boxes, clip); + + _cairo_boxes_extents (boxes, &limits); + + _cairo_box_round_to_rectangle (&limits, &extents); + if (clip->path == NULL) { + clip->extents = extents; + } else if (! _cairo_rectangle_intersect (&clip->extents, &extents)) { + clip = _cairo_clip_set_all_clipped (clip); + goto out; + } + + if (clip->region) { + cairo_region_destroy (clip->region); + clip->region = NULL; + } + clip->is_region = FALSE; + +out: + if (boxes == &clip_boxes) + _cairo_boxes_fini (&clip_boxes); + + return clip; +} + +cairo_clip_t * +_cairo_clip_intersect_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *r) +{ + cairo_box_t box; + + if (_cairo_clip_is_all_clipped (clip)) + return clip; + + if (r->width == 0 || r->height == 0) + return _cairo_clip_set_all_clipped (clip); + + _cairo_box_from_rectangle_int (&box, r); + + return _cairo_clip_intersect_rectangle_box (clip, r, &box); +} + +struct reduce { + cairo_clip_t *clip; + cairo_box_t limit; + cairo_box_t extents; + cairo_bool_t inside; + + cairo_point_t current_point; + cairo_point_t last_move_to; +}; + +static void +_add_clipped_edge (struct reduce *r, + const cairo_point_t *p1, + const cairo_point_t *p2, + int y1, int y2) +{ + cairo_fixed_t x; + + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1); + if (x < r->extents.p1.x) + r->extents.p1.x = x; + + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2); + if (x > r->extents.p2.x) + r->extents.p2.x = x; + + if (y1 < r->extents.p1.y) + r->extents.p1.y = y1; + + if (y2 > r->extents.p2.y) + r->extents.p2.y = y2; + + r->inside = TRUE; +} + +static void +_add_edge (struct reduce *r, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int top, bottom; + int top_y, bot_y; + int n; + + if (p1->y < p2->y) { + top = p1->y; + bottom = p2->y; + } else { + top = p2->y; + bottom = p1->y; + } + + if (bottom < r->limit.p1.y || top > r->limit.p2.y) + return; + + if (p1->x > p2->x) { + const cairo_point_t *t = p1; + p1 = p2; + p2 = t; + } + + if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x) + return; + + for (n = 0; n < r->clip->num_boxes; n++) { + const cairo_box_t *limits = &r->clip->boxes[n]; + + if (bottom < limits->p1.y || top > limits->p2.y) + continue; + + if (p2->x <= limits->p1.x || p1->x >= limits->p2.x) + continue; + + if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) { + top_y = top; + bot_y = bottom; + } else { + int p1_y, p2_y; + + p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p1.x); + p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p2.x); + if (p1_y < p2_y) { + top_y = p1_y; + bot_y = p2_y; + } else { + top_y = p2_y; + bot_y = p1_y; + } + + if (top_y < top) + top_y = top; + if (bot_y > bottom) + bot_y = bottom; + } + + if (top_y < limits->p1.y) + top_y = limits->p1.y; + + if (bot_y > limits->p2.y) + bot_y = limits->p2.y; + if (bot_y > top_y) + _add_clipped_edge (r, p1, p2, top_y, bot_y); + } +} + +static cairo_status_t +_reduce_line_to (void *closure, + const cairo_point_t *point) +{ + struct reduce *r = closure; + + _add_edge (r, &r->current_point, point); + r->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_reduce_close (void *closure) +{ + struct reduce *r = closure; + + return _reduce_line_to (r, &r->last_move_to); +} + +static cairo_status_t +_reduce_move_to (void *closure, + const cairo_point_t *point) +{ + struct reduce *r = closure; + cairo_status_t status; + + /* close current subpath */ + status = _reduce_close (closure); + + /* make sure that the closure represents a degenerate path */ + r->current_point = *point; + r->last_move_to = *point; + + return status; +} + +static cairo_clip_t * +_cairo_clip_reduce_to_boxes (cairo_clip_t *clip) +{ + struct reduce r; + cairo_clip_path_t *clip_path; + cairo_status_t status; + + return clip; + if (clip->path == NULL) + return clip; + + r.clip = clip; + r.extents.p1.x = r.extents.p1.y = INT_MAX; + r.extents.p2.x = r.extents.p2.y = INT_MIN; + r.inside = FALSE; + + r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x); + r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y); + r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width); + r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height); + + clip_path = clip->path; + do { + r.current_point.x = 0; + r.current_point.y = 0; + r.last_move_to = r.current_point; + + status = _cairo_path_fixed_interpret_flat (&clip_path->path, + _reduce_move_to, + _reduce_line_to, + _reduce_close, + &r, + clip_path->tolerance); + assert (status == CAIRO_STATUS_SUCCESS); + _reduce_close (&r); + } while ((clip_path = clip_path->prev)); + + if (! r.inside) { + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + } + + return _cairo_clip_intersect_box (clip, &r.extents); +} + +cairo_clip_t * +_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip, + const cairo_rectangle_int_t *r) +{ + cairo_clip_t *copy; + + if (_cairo_clip_is_all_clipped (clip)) + return (cairo_clip_t *) clip; + + if (_cairo_clip_contains_rectangle (clip, r)) + return _cairo_clip_intersect_rectangle (NULL, r); + + copy = _cairo_clip_copy_intersect_rectangle (clip, r); + if (_cairo_clip_is_all_clipped (copy)) + return copy; + + return _cairo_clip_reduce_to_boxes (copy); +} + +cairo_clip_t * +_cairo_clip_reduce_for_composite (const cairo_clip_t *clip, + cairo_composite_rectangles_t *extents) +{ + const cairo_rectangle_int_t *r; + + r = extents->is_bounded ? &extents->bounded : &extents->unbounded; + return _cairo_clip_reduce_to_rectangle (clip, r); +} + +cairo_clip_t * +_cairo_clip_from_boxes (const cairo_boxes_t *boxes) +{ + cairo_box_t extents; + cairo_clip_t *clip = _cairo_clip_create (); + if (clip == NULL) + return _cairo_clip_set_all_clipped (clip); + + if (unlikely (! _cairo_boxes_copy_to_clip (boxes, clip))) + return clip; + + _cairo_boxes_extents (boxes, &extents); + _cairo_box_round_to_rectangle (&extents, &clip->extents); + + return clip; +} diff --git a/gfx/cairo/cairo/src/cairo-clip-inline.h b/gfx/cairo/cairo/src/cairo-clip-inline.h new file mode 100644 index 000000000000..d52afd3136cf --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-inline.h @@ -0,0 +1,96 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Kristian Høgsberg + * Chris Wilson + */ + +#ifndef CAIRO_CLIP_INLINE_H +#define CAIRO_CLIP_INLINE_H + +#include "cairo-clip-private.h" + +static inline cairo_bool_t _cairo_clip_is_all_clipped(const cairo_clip_t *clip) +{ + return clip == &__cairo_clip_all; +} + +static inline cairo_clip_t * +_cairo_clip_set_all_clipped (cairo_clip_t *clip) +{ + _cairo_clip_destroy (clip); + return (cairo_clip_t *) &__cairo_clip_all; +} + +static inline cairo_clip_t * +_cairo_clip_copy_intersect_rectangle (const cairo_clip_t *clip, + const cairo_rectangle_int_t *r) +{ + return _cairo_clip_intersect_rectangle (_cairo_clip_copy (clip), r); +} + +static inline cairo_clip_t * +_cairo_clip_copy_intersect_clip (const cairo_clip_t *clip, + const cairo_clip_t *other) +{ + return _cairo_clip_intersect_clip (_cairo_clip_copy (clip), other); +} + +static inline void +_cairo_clip_steal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes) +{ + cairo_box_t *array = clip->boxes; + + if (array == &clip->embedded_box) { + assert (clip->num_boxes == 1); + boxes->boxes_embedded[0] = clip->embedded_box; + array = &boxes->boxes_embedded[0]; + } + _cairo_boxes_init_for_array (boxes, array, clip->num_boxes); + clip->boxes = NULL; + clip->num_boxes = 0; +} + +static inline void +_cairo_clip_unsteal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes) +{ + if (boxes->chunks.base == &boxes->boxes_embedded[0]) { + assert(boxes->num_boxes == 1); + clip->embedded_box = *boxes->chunks.base; + clip->boxes = &clip->embedded_box; + } else { + clip->boxes = boxes->chunks.base; + } + clip->num_boxes = boxes->num_boxes; +} + +#endif /* CAIRO_CLIP_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-clip-polygon.c b/gfx/cairo/cairo/src/cairo-clip-polygon.c new file mode 100644 index 000000000000..f40faefba808 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-polygon.c @@ -0,0 +1,156 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" +#include "cairo-clip-inline.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-pattern-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-region-private.h" + +static cairo_bool_t +can_convert_to_polygon (const cairo_clip_t *clip) +{ + cairo_clip_path_t *clip_path = clip->path; + cairo_antialias_t antialias = clip_path->antialias; + + while ((clip_path = clip_path->prev) != NULL) { + if (clip_path->antialias != antialias) + return FALSE; + } + + return TRUE; +} + +cairo_int_status_t +_cairo_clip_get_polygon (const cairo_clip_t *clip, + cairo_polygon_t *polygon, + cairo_fill_rule_t *fill_rule, + cairo_antialias_t *antialias) +{ + cairo_status_t status; + cairo_clip_path_t *clip_path; + + if (_cairo_clip_is_all_clipped (clip)) { + _cairo_polygon_init (polygon, NULL, 0); + return CAIRO_INT_STATUS_SUCCESS; + } + + /* If there is no clip, we need an infinite polygon */ + assert (clip && (clip->path || clip->num_boxes)); + + if (clip->path == NULL) { + *fill_rule = CAIRO_FILL_RULE_WINDING; + *antialias = CAIRO_ANTIALIAS_DEFAULT; + return _cairo_polygon_init_box_array (polygon, + clip->boxes, + clip->num_boxes); + } + + /* check that residual is all of the same type/tolerance */ + if (! can_convert_to_polygon (clip)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (clip->num_boxes < 2) + _cairo_polygon_init_with_clip (polygon, clip); + else + _cairo_polygon_init_with_clip (polygon, NULL); + + clip_path = clip->path; + *fill_rule = clip_path->fill_rule; + *antialias = clip_path->antialias; + + status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, + clip_path->tolerance, + polygon); + if (unlikely (status)) + goto err; + + if (clip->num_boxes > 1) { + status = _cairo_polygon_intersect_with_boxes (polygon, fill_rule, + clip->boxes, clip->num_boxes); + if (unlikely (status)) + goto err; + } + + polygon->limits = NULL; + polygon->num_limits = 0; + + while ((clip_path = clip_path->prev) != NULL) { + cairo_polygon_t next; + + _cairo_polygon_init (&next, NULL, 0); + status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, + clip_path->tolerance, + &next); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = _cairo_polygon_intersect (polygon, *fill_rule, + &next, clip_path->fill_rule); + _cairo_polygon_fini (&next); + if (unlikely (status)) + goto err; + + *fill_rule = CAIRO_FILL_RULE_WINDING; + } + + return CAIRO_STATUS_SUCCESS; + +err: + _cairo_polygon_fini (polygon); + return status; +} + +cairo_bool_t +_cairo_clip_is_polygon (const cairo_clip_t *clip) +{ + if (_cairo_clip_is_all_clipped (clip)) + return TRUE; + + /* If there is no clip, we need an infinite polygon */ + if (clip == NULL) + return FALSE; + + if (clip->path == NULL) + return TRUE; + + /* check that residual is all of the same type/tolerance */ + return can_convert_to_polygon (clip); +} diff --git a/gfx/cairo/cairo/src/cairo-clip-private.h b/gfx/cairo/cairo/src/cairo-clip-private.h index faf486409954..5fc05a64e938 100644 --- a/gfx/cairo/cairo/src/cairo-clip-private.h +++ b/gfx/cairo/cairo/src/cairo-clip-private.h @@ -31,24 +31,23 @@ * * Contributor(s): * Kristian Høgsberg + * Chris Wilson */ #ifndef CAIRO_CLIP_PRIVATE_H #define CAIRO_CLIP_PRIVATE_H #include "cairo-types-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" #include "cairo-compiler-private.h" +#include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-reference-count-private.h" extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; -enum { - CAIRO_CLIP_PATH_HAS_REGION = 0x1, - CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED = 0x2, - CAIRO_CLIP_PATH_IS_BOX = 0x4 -}; - struct _cairo_clip_path { cairo_reference_count_t ref_count; cairo_path_fixed_t path; @@ -56,96 +55,144 @@ struct _cairo_clip_path { double tolerance; cairo_antialias_t antialias; cairo_clip_path_t *prev; - - cairo_rectangle_int_t extents; - - /* partial caches */ - unsigned int flags; - cairo_region_t *region; - cairo_surface_t *surface; }; struct _cairo_clip { - /* can be used as a cairo_hash_entry_t for live clips */ + cairo_rectangle_int_t extents; cairo_clip_path_t *path; - cairo_bool_t all_clipped; + cairo_box_t *boxes; + int num_boxes; + cairo_region_t *region; + cairo_bool_t is_region; + + cairo_box_t embedded_box; }; -cairo_private void -_cairo_clip_init (cairo_clip_t *clip); +cairo_private cairo_clip_t * +_cairo_clip_create (void); -cairo_private_no_warn cairo_clip_t * -_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other); - -cairo_private cairo_status_t -_cairo_clip_init_copy_transformed (cairo_clip_t *clip, - cairo_clip_t *other, - const cairo_matrix_t *matrix); +cairo_private cairo_clip_path_t * +_cairo_clip_path_reference (cairo_clip_path_t *clip_path); cairo_private void -_cairo_clip_reset (cairo_clip_t *clip); +_cairo_clip_path_destroy (cairo_clip_path_t *clip_path); + +cairo_private void +_cairo_clip_destroy (cairo_clip_t *clip); + +cairo_private extern const cairo_clip_t __cairo_clip_all; + +cairo_private cairo_clip_t * +_cairo_clip_copy (const cairo_clip_t *clip); + +cairo_private cairo_clip_t * +_cairo_clip_copy_region (const cairo_clip_t *clip); + +cairo_private cairo_clip_t * +_cairo_clip_copy_path (const cairo_clip_t *clip); + +cairo_private cairo_clip_t * +_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty); + +cairo_private cairo_clip_t * +_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m); + +cairo_private cairo_clip_t * +_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty); cairo_private cairo_bool_t _cairo_clip_equal (const cairo_clip_t *clip_a, const cairo_clip_t *clip_b); -#define _cairo_clip_fini(clip) _cairo_clip_reset (clip) +cairo_private cairo_clip_t * +_cairo_clip_intersect_rectangle (cairo_clip_t *clip, + const cairo_rectangle_int_t *rectangle); -cairo_private cairo_status_t -_cairo_clip_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rectangle); +cairo_private cairo_clip_t * +_cairo_clip_intersect_clip (cairo_clip_t *clip, + const cairo_clip_t *other); -cairo_private cairo_status_t -_cairo_clip_clip (cairo_clip_t *clip, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias); +cairo_private cairo_clip_t * +_cairo_clip_intersect_box (cairo_clip_t *clip, + const cairo_box_t *box); -cairo_private cairo_status_t -_cairo_clip_apply_clip (cairo_clip_t *clip, - const cairo_clip_t *other); +cairo_private cairo_clip_t * +_cairo_clip_intersect_boxes (cairo_clip_t *clip, + const cairo_boxes_t *boxes); + +cairo_private cairo_clip_t * +_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias); + +cairo_private cairo_clip_t * +_cairo_clip_intersect_path (cairo_clip_t *clip, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias); cairo_private const cairo_rectangle_int_t * _cairo_clip_get_extents (const cairo_clip_t *clip); cairo_private cairo_surface_t * -_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty); +_cairo_clip_get_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty); + +cairo_private cairo_surface_t * +_cairo_clip_get_image (const cairo_clip_t *clip, + cairo_surface_t *target, + const cairo_rectangle_int_t *extents); cairo_private cairo_status_t -_cairo_clip_combine_with_surface (cairo_clip_t *clip, +_cairo_clip_combine_with_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int dst_x, int dst_y); -cairo_private cairo_int_status_t -_cairo_clip_get_region (cairo_clip_t *clip, - cairo_region_t **region); +cairo_private cairo_clip_t * +_cairo_clip_from_boxes (const cairo_boxes_t *boxes); -cairo_private cairo_int_status_t -_cairo_clip_get_boxes (cairo_clip_t *clip, - cairo_box_t **boxes, - int *count); - -cairo_private cairo_status_t -_cairo_clip_to_boxes (cairo_clip_t **clip, - cairo_composite_rectangles_t *extents, - cairo_box_t **boxes, - int *num_boxes); +cairo_private cairo_region_t * +_cairo_clip_get_region (const cairo_clip_t *clip); cairo_private cairo_bool_t -_cairo_clip_contains_rectangle (cairo_clip_t *clip, +_cairo_clip_is_region (const cairo_clip_t *clip); + +cairo_private cairo_clip_t * +_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip, + const cairo_rectangle_int_t *r); + +cairo_private cairo_clip_t * +_cairo_clip_reduce_for_composite (const cairo_clip_t *clip, + cairo_composite_rectangles_t *extents); + +cairo_private cairo_bool_t +_cairo_clip_contains_rectangle (const cairo_clip_t *clip, const cairo_rectangle_int_t *rect); cairo_private cairo_bool_t -_cairo_clip_contains_extents (cairo_clip_t *clip, - const cairo_composite_rectangles_t *extents); +_cairo_clip_contains_box (const cairo_clip_t *clip, + const cairo_box_t *box); -cairo_private void -_cairo_clip_drop_cache (cairo_clip_t *clip); +cairo_private cairo_bool_t +_cairo_clip_contains_extents (const cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents); cairo_private cairo_rectangle_list_t* _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate); +cairo_private cairo_rectangle_list_t * +_cairo_rectangle_list_create_in_error (cairo_status_t status); + +cairo_private cairo_bool_t +_cairo_clip_is_polygon (const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_clip_get_polygon (const cairo_clip_t *clip, + cairo_polygon_t *polygon, + cairo_fill_rule_t *fill_rule, + cairo_antialias_t *antialias); + #endif /* CAIRO_CLIP_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-clip-region.c b/gfx/cairo/cairo/src/cairo-clip-region.c new file mode 100644 index 000000000000..e3f4891e3f25 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-region.c @@ -0,0 +1,123 @@ +/* -*- 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 © 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): + * Carl D. Worth + * Kristian Høgsberg + * Chris Wilson + */ + +#include "cairoint.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-pattern-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-region-private.h" + +static void +_cairo_clip_extract_region (cairo_clip_t *clip) +{ + cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; + cairo_rectangle_int_t *r = stack_rects; + cairo_bool_t is_region; + int i; + + if (clip->num_boxes == 0) + return; + + if (clip->num_boxes > ARRAY_LENGTH (stack_rects)) { + r = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_rectangle_int_t)); + if (r == NULL){ + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return; + } + } + + is_region = clip->path == NULL; + for (i = 0; i < clip->num_boxes; i++) { + cairo_box_t *b = &clip->boxes[i]; + if (is_region) + is_region = + _cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y); + r[i].x = _cairo_fixed_integer_floor (b->p1.x); + r[i].y = _cairo_fixed_integer_floor (b->p1.y); + r[i].width = _cairo_fixed_integer_ceil (b->p2.x) - r[i].x; + r[i].height = _cairo_fixed_integer_ceil (b->p2.y) - r[i].y; + } + clip->is_region = is_region; + + clip->region = cairo_region_create_rectangles (r, i); + + if (r != stack_rects) + free (r); +} + +cairo_region_t * +_cairo_clip_get_region (const cairo_clip_t *clip) +{ + if (clip == NULL) + return NULL; + + if (clip->region == NULL) + _cairo_clip_extract_region ((cairo_clip_t *) clip); + + return clip->region; +} + +cairo_bool_t +_cairo_clip_is_region (const cairo_clip_t *clip) +{ + if (clip == NULL) + return TRUE; + + if (clip->is_region) + return TRUE; + + /* XXX Geometric reduction? */ + + if (clip->path) + return FALSE; + + if (clip->num_boxes == 0) + return TRUE; + + if (clip->region == NULL) + _cairo_clip_extract_region ((cairo_clip_t *) clip); + + return clip->is_region; +} diff --git a/gfx/cairo/cairo/src/cairo-clip-surface.c b/gfx/cairo/cairo/src/cairo-clip-surface.c new file mode 100644 index 000000000000..85feaa649eb3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-surface.c @@ -0,0 +1,240 @@ +/* -*- 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 © 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): + * Carl D. Worth + * Kristian Høgsberg + * Chris Wilson + */ + +#include "cairoint.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-pattern-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-region-private.h" + +cairo_status_t +_cairo_clip_combine_with_surface (const cairo_clip_t *clip, + cairo_surface_t *dst, + int dst_x, int dst_y) +{ + cairo_clip_path_t *copy_path; + cairo_clip_path_t *clip_path; + cairo_clip_t *copy; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + copy = _cairo_clip_copy_with_translation (clip, -dst_x, -dst_y); + copy_path = copy->path; + copy->path = NULL; + + if (copy->boxes) { + status = _cairo_surface_paint (dst, + CAIRO_OPERATOR_IN, + &_cairo_pattern_white.base, + copy); + } + + clip = NULL; + if (_cairo_clip_is_region (copy)) + clip = copy; + clip_path = copy_path; + while (status == CAIRO_STATUS_SUCCESS && clip_path) { + status = _cairo_surface_fill (dst, + CAIRO_OPERATOR_IN, + &_cairo_pattern_white.base, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + clip); + clip_path = clip_path->prev; + } + + copy->path = copy_path; + _cairo_clip_destroy (copy); + return status; +} + +static cairo_status_t +_cairo_path_fixed_add_box (cairo_path_fixed_t *path, + const cairo_box_t *box, + cairo_fixed_t fx, + cairo_fixed_t fy) +{ + cairo_status_t status; + + status = _cairo_path_fixed_move_to (path, box->p1.x + fx, box->p1.y + fy); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p1.y + fy); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p2.y + fy); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p1.x + fx, box->p2.y + fy); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_close_path (path); +} + +cairo_surface_t * +_cairo_clip_get_surface (const cairo_clip_t *clip, + cairo_surface_t *target, + int *tx, int *ty) +{ + cairo_surface_t *surface; + cairo_status_t status; + cairo_clip_t *copy, *region; + cairo_clip_path_t *copy_path, *clip_path; + + if (clip->num_boxes) { + cairo_path_fixed_t path; + int i; + + surface = _cairo_surface_create_scratch (target, + CAIRO_CONTENT_ALPHA, + clip->extents.width, + clip->extents.height, + CAIRO_COLOR_TRANSPARENT); + if (unlikely (surface->status)) + return surface; + + _cairo_path_fixed_init (&path); + status = CAIRO_STATUS_SUCCESS; + for (i = 0; status == CAIRO_STATUS_SUCCESS && i < clip->num_boxes; i++) { + status = _cairo_path_fixed_add_box (&path, &clip->boxes[i], + -_cairo_fixed_from_int (clip->extents.x), + -_cairo_fixed_from_int (clip->extents.y)); + } + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_surface_fill (surface, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &path, + CAIRO_FILL_RULE_WINDING, + 1., + CAIRO_ANTIALIAS_DEFAULT, + NULL); + _cairo_path_fixed_fini (&path); + if (unlikely (status)) { + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + } else { + surface = _cairo_surface_create_scratch (target, + CAIRO_CONTENT_ALPHA, + clip->extents.width, + clip->extents.height, + CAIRO_COLOR_WHITE); + if (unlikely (surface->status)) + return surface; + } + + copy = _cairo_clip_copy_with_translation (clip, + -clip->extents.x, + -clip->extents.y); + copy_path = copy->path; + copy->path = NULL; + + region = copy; + if (! _cairo_clip_is_region (copy)) + region = _cairo_clip_copy_region (copy); + + status = CAIRO_STATUS_SUCCESS; + clip_path = copy_path; + while (status == CAIRO_STATUS_SUCCESS && clip_path) { + status = _cairo_surface_fill (surface, + CAIRO_OPERATOR_IN, + &_cairo_pattern_white.base, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + region); + clip_path = clip_path->prev; + } + + copy->path = copy_path; + _cairo_clip_destroy (copy); + if (region != copy) + _cairo_clip_destroy (region); + + if (unlikely (status)) { + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + + *tx = clip->extents.x; + *ty = clip->extents.y; + return surface; +} + +cairo_surface_t * +_cairo_clip_get_image (const cairo_clip_t *clip, + cairo_surface_t *target, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *surface; + cairo_status_t status; + + surface = cairo_surface_create_similar_image (target, + CAIRO_FORMAT_A8, + extents->width, + extents->height); + if (unlikely (surface->status)) + return surface; + + status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, NULL); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = _cairo_clip_combine_with_surface (clip, surface, + extents->x, extents->y); + + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + return surface; +} diff --git a/gfx/cairo/cairo/src/cairo-clip-tor-scan-converter.c b/gfx/cairo/cairo/src/cairo-clip-tor-scan-converter.c new file mode 100644 index 000000000000..2ac1d32b138d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-tor-scan-converter.c @@ -0,0 +1,1845 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* glitter-paths - polygon scan converter + * + * Copyright (c) 2008 M Joonas Pihlaja + * Copyright (c) 2007 David Turner + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/* This is the Glitter paths scan converter incorporated into cairo. + * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 + * of + * + * https://gitweb.freedesktop.org/?p=users/joonas/glitter-paths + */ +/* Glitter-paths is a stand alone polygon rasteriser derived from + * David Turner's reimplementation of Tor Anderssons's 15x17 + * supersampling rasteriser from the Apparition graphics library. The + * main new feature here is cheaply choosing per-scan line between + * doing fully analytical coverage computation for an entire row at a + * time vs. using a supersampling approach. + * + * David Turner's code can be found at + * + * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2 + * + * In particular this file incorporates large parts of ftgrays_tor10.h + * from raster-comparison-20070813.tar.bz2 + */ +/* Overview + * + * A scan converter's basic purpose to take polygon edges and convert + * them into an RLE compressed A8 mask. This one works in two phases: + * gathering edges and generating spans. + * + * 1) As the user feeds the scan converter edges they are vertically + * clipped and bucketted into a _polygon_ data structure. The edges + * are also snapped from the user's coordinates to the subpixel grid + * coordinates used during scan conversion. + * + * user + * | + * | edges + * V + * polygon buckets + * + * 2) Generating spans works by performing a vertical sweep of pixel + * rows from top to bottom and maintaining an _active_list_ of edges + * that intersect the row. From the active list the fill rule + * determines which edges are the left and right edges of the start of + * each span, and their contribution is then accumulated into a pixel + * coverage list (_cell_list_) as coverage deltas. Once the coverage + * deltas of all edges are known we can form spans of constant pixel + * coverage by summing the deltas during a traversal of the cell list. + * At the end of a pixel row the cell list is sent to a coverage + * blitter for rendering to some target surface. + * + * The pixel coverages are computed by either supersampling the row + * and box filtering a mono rasterisation, or by computing the exact + * coverages of edges in the active list. The supersampling method is + * used whenever some edge starts or stops within the row or there are + * edge intersections in the row. + * + * polygon bucket for \ + * current pixel row | + * | | + * | activate new edges | Repeat GRID_Y times if we + * V \ are supersampling this row, + * active list / or just once if we're computing + * | | analytical coverage. + * | coverage deltas | + * V | + * pixel coverage list / + * | + * V + * coverage blitter + */ +#include "cairoint.h" +#include "cairo-spans-private.h" +#include "cairo-error-private.h" + +#include +#include +#include +#include +#include + +/* The input coordinate scale and the rasterisation grid scales. */ +#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS +#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS +#define GRID_Y 15 + +/* Set glitter up to use a cairo span renderer to do the coverage + * blitting. */ +struct pool; +struct cell_list; + +/*------------------------------------------------------------------------- + * glitter-paths.h + */ + +/* "Input scaled" numbers are fixed precision reals with multiplier + * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as + * pixel scaled numbers. These get converted to the internal grid + * scaled numbers as soon as possible. Internal overflow is possible + * if GRID_X/Y inside glitter-paths.c is larger than + * 1< +#include +#include + +/* All polygon coordinates are snapped onto a subsample grid. "Grid + * scaled" numbers are fixed precision reals with multiplier GRID_X or + * GRID_Y. */ +typedef int grid_scaled_t; +typedef int grid_scaled_x_t; +typedef int grid_scaled_y_t; + +/* Default x/y scale factors. + * You can either define GRID_X/Y_BITS to get a power-of-two scale + * or define GRID_X/Y separately. */ +#if !defined(GRID_X) && !defined(GRID_X_BITS) +# define GRID_X_BITS 8 +#endif +#if !defined(GRID_Y) && !defined(GRID_Y_BITS) +# define GRID_Y 15 +#endif + +/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ +#ifdef GRID_X_BITS +# define GRID_X (1 << GRID_X_BITS) +#endif +#ifdef GRID_Y_BITS +# define GRID_Y (1 << GRID_Y_BITS) +#endif + +/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into + * integer and fractional parts. The integer part is floored. */ +#if defined(GRID_X_TO_INT_FRAC) + /* do nothing */ +#elif defined(GRID_X_BITS) +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) +#else +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) +#endif + +#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ + (i) = (t) / (m); \ + (f) = (t) % (m); \ + if ((f) < 0) { \ + --(i); \ + (f) += (m); \ + } \ +} while (0) + +#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ + (f) = (t) & ((1 << (b)) - 1); \ + (i) = (t) >> (b); \ +} while (0) + +/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want + * to be able to represent exactly areas of subpixel trapezoids whose + * vertices are given in grid scaled coordinates. The scale factor + * comes from needing to accurately represent the area 0.5*dx*dy of a + * triangle with base dx and height dy in grid scaled numbers. */ +typedef int grid_area_t; +#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ + +/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ +#if GRID_XY == 510 +# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) +#elif GRID_XY == 255 +# define GRID_AREA_TO_ALPHA(c) (c) +#elif GRID_XY == 64 +# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) +#elif GRID_XY == 128 +# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) +#elif GRID_XY == 256 +# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) +#elif GRID_XY == 15 +# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) +#elif GRID_XY == 2*256*15 +# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) +#else +# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) +#endif + +#define UNROLL3(x) x x x + +struct quorem { + int32_t quo; + int32_t rem; +}; + +/* Header for a chunk of memory in a memory pool. */ +struct _pool_chunk { + /* # bytes used in this chunk. */ + size_t size; + + /* # bytes total in this chunk */ + size_t capacity; + + /* Pointer to the previous chunk or %NULL if this is the sentinel + * chunk in the pool header. */ + struct _pool_chunk *prev_chunk; + + /* Actual data starts here. Well aligned for pointers. */ +}; + +/* A memory pool. This is supposed to be embedded on the stack or + * within some other structure. It may optionally be followed by an + * embedded array from which requests are fulfilled until + * malloc needs to be called to allocate a first real chunk. */ +struct pool { + /* Chunk we're allocating from. */ + struct _pool_chunk *current; + + jmp_buf *jmp; + + /* Free list of previously allocated chunks. All have >= default + * capacity. */ + struct _pool_chunk *first_free; + + /* The default capacity of a chunk. */ + size_t default_capacity; + + /* Header for the sentinel chunk. Directly following the pool + * struct should be some space for embedded elements from which + * the sentinel chunk allocates from. */ + struct _pool_chunk sentinel[1]; +}; + +/* A polygon edge. */ +struct edge { + /* Next in y-bucket or active list. */ + struct edge *next; + + /* Current x coordinate while the edge is on the active + * list. Initialised to the x coordinate of the top of the + * edge. The quotient is in grid_scaled_x_t units and the + * remainder is mod dy in grid_scaled_y_t units.*/ + struct quorem x; + + /* Advance of the current x when moving down a subsample line. */ + struct quorem dxdy; + + /* Advance of the current x when moving down a full pixel + * row. Only initialised when the height of the edge is large + * enough that there's a chance the edge could be stepped by a + * full row's worth of subsample rows at a time. */ + struct quorem dxdy_full; + + /* The clipped y of the top of the edge. */ + grid_scaled_y_t ytop; + + /* y2-y1 after orienting the edge downwards. */ + grid_scaled_y_t dy; + + /* Number of subsample rows remaining to scan convert of this + * edge. */ + grid_scaled_y_t height_left; + + /* Original sign of the edge: +1 for downwards, -1 for upwards + * edges. */ + int dir; + int vertical; + int clip; +}; + +/* Number of subsample rows per y-bucket. Must be GRID_Y. */ +#define EDGE_Y_BUCKET_HEIGHT GRID_Y + +#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) + +/* A collection of sorted and vertically clipped edges of the polygon. + * Edges are moved from the polygon to an active list while scan + * converting. */ +struct polygon { + /* The vertical clip extents. */ + grid_scaled_y_t ymin, ymax; + + /* Array of edges all starting in the same bucket. An edge is put + * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when + * it is added to the polygon. */ + struct edge **y_buckets; + struct edge *y_buckets_embedded[64]; + + struct { + struct pool base[1]; + struct edge embedded[32]; + } edge_pool; +}; + +/* A cell records the effect on pixel coverage of polygon edges + * passing through a pixel. It contains two accumulators of pixel + * coverage. + * + * Consider the effects of a polygon edge on the coverage of a pixel + * it intersects and that of the following one. The coverage of the + * following pixel is the height of the edge multiplied by the width + * of the pixel, and the coverage of the pixel itself is the area of + * the trapezoid formed by the edge and the right side of the pixel. + * + * +-----------------------+-----------------------+ + * | | | + * | | | + * |_______________________|_______________________| + * | \...................|.......................|\ + * | \..................|.......................| | + * | \.................|.......................| | + * | \....covered.....|.......................| | + * | \....area.......|.......................| } covered height + * | \..............|.......................| | + * |uncovered\.............|.......................| | + * | area \............|.......................| | + * |___________\...........|.......................|/ + * | | | + * | | | + * | | | + * +-----------------------+-----------------------+ + * + * Since the coverage of the following pixel will always be a multiple + * of the width of the pixel, we can store the height of the covered + * area instead. The coverage of the pixel itself is the total + * coverage minus the area of the uncovered area to the left of the + * edge. As it's faster to compute the uncovered area we only store + * that and subtract it from the total coverage later when forming + * spans to blit. + * + * The heights and areas are signed, with left edges of the polygon + * having positive sign and right edges having negative sign. When + * two edges intersect they swap their left/rightness so their + * contribution above and below the intersection point must be + * computed separately. */ +struct cell { + struct cell *next; + int x; + grid_area_t uncovered_area; + grid_scaled_y_t covered_height; + grid_scaled_y_t clipped_height; +}; + +/* A cell list represents the scan line sparsely as cells ordered by + * ascending x. It is geared towards scanning the cells in order + * using an internal cursor. */ +struct cell_list { + /* Sentinel nodes */ + struct cell head, tail; + + /* Cursor state for iterating through the cell list. */ + struct cell *cursor; + + /* Cells in the cell list are owned by the cell list and are + * allocated from this pool. */ + struct { + struct pool base[1]; + struct cell embedded[32]; + } cell_pool; +}; + +struct cell_pair { + struct cell *cell1; + struct cell *cell2; +}; + +/* The active list contains edges in the current scan line ordered by + * the x-coordinate of the intercept of the edge and the scan line. */ +struct active_list { + /* Leftmost edge on the current scan line. */ + struct edge *head; + + /* A lower bound on the height of the active edges is used to + * estimate how soon some active edge ends. We can't advance the + * scan conversion by a full pixel row if an edge ends somewhere + * within it. */ + grid_scaled_y_t min_height; +}; + +struct glitter_scan_converter { + struct polygon polygon[1]; + struct active_list active[1]; + struct cell_list coverages[1]; + + /* Clip box. */ + grid_scaled_y_t ymin, ymax; +}; + +/* Compute the floored division a/b. Assumes / and % perform symmetric + * division. */ +inline static struct quorem +floored_divrem(int a, int b) +{ + struct quorem qr; + qr.quo = a/b; + qr.rem = a%b; + if ((a^b)<0 && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric + * division. */ +static struct quorem +floored_muldivrem(int x, int a, int b) +{ + struct quorem qr; + long long xa = (long long)x*a; + qr.quo = xa/b; + qr.rem = xa%b; + if ((xa>=0) != (b>=0) && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +static struct _pool_chunk * +_pool_chunk_init( + struct _pool_chunk *p, + struct _pool_chunk *prev_chunk, + size_t capacity) +{ + p->prev_chunk = prev_chunk; + p->size = 0; + p->capacity = capacity; + return p; +} + +static struct _pool_chunk * +_pool_chunk_create(struct pool *pool, size_t size) +{ + struct _pool_chunk *p; + + p = _cairo_malloc (size + sizeof(struct _pool_chunk)); + if (unlikely (NULL == p)) + longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _pool_chunk_init(p, pool->current, size); +} + +static void +pool_init(struct pool *pool, + jmp_buf *jmp, + size_t default_capacity, + size_t embedded_capacity) +{ + pool->jmp = jmp; + pool->current = pool->sentinel; + pool->first_free = NULL; + pool->default_capacity = default_capacity; + _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); +} + +static void +pool_fini(struct pool *pool) +{ + struct _pool_chunk *p = pool->current; + do { + while (NULL != p) { + struct _pool_chunk *prev = p->prev_chunk; + if (p != pool->sentinel) + free(p); + p = prev; + } + p = pool->first_free; + pool->first_free = NULL; + } while (NULL != p); +} + +/* Satisfy an allocation by first allocating a new large enough chunk + * and adding it to the head of the pool's chunk list. This function + * is called as a fallback if pool_alloc() couldn't do a quick + * allocation from the current chunk in the pool. */ +static void * +_pool_alloc_from_new_chunk( + struct pool *pool, + size_t size) +{ + struct _pool_chunk *chunk; + void *obj; + size_t capacity; + + /* If the allocation is smaller than the default chunk size then + * try getting a chunk off the free list. Force alloc of a new + * chunk for large requests. */ + capacity = size; + chunk = NULL; + if (size < pool->default_capacity) { + capacity = pool->default_capacity; + chunk = pool->first_free; + if (chunk) { + pool->first_free = chunk->prev_chunk; + _pool_chunk_init(chunk, pool->current, chunk->capacity); + } + } + + if (NULL == chunk) + chunk = _pool_chunk_create (pool, capacity); + pool->current = chunk; + + obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; +} + +/* Allocate size bytes from the pool. The first allocated address + * returned from a pool is aligned to sizeof(void*). Subsequent + * addresses will maintain alignment as long as multiples of void* are + * allocated. Returns the address of a new memory area or %NULL on + * allocation failures. The pool retains ownership of the returned + * memory. */ +inline static void * +pool_alloc (struct pool *pool, size_t size) +{ + struct _pool_chunk *chunk = pool->current; + + if (size <= chunk->capacity - chunk->size) { + void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; + } else { + return _pool_alloc_from_new_chunk(pool, size); + } +} + +/* Relinquish all pool_alloced memory back to the pool. */ +static void +pool_reset (struct pool *pool) +{ + /* Transfer all used chunks to the chunk free list. */ + struct _pool_chunk *chunk = pool->current; + if (chunk != pool->sentinel) { + while (chunk->prev_chunk != pool->sentinel) { + chunk = chunk->prev_chunk; + } + chunk->prev_chunk = pool->first_free; + pool->first_free = pool->current; + } + /* Reset the sentinel as the current chunk. */ + pool->current = pool->sentinel; + pool->sentinel->size = 0; +} + +/* Rewinds the cell list's cursor to the beginning. After rewinding + * we're good to cell_list_find() the cell any x coordinate. */ +inline static void +cell_list_rewind (struct cell_list *cells) +{ + cells->cursor = &cells->head; +} + +/* Rewind the cell list if its cursor has been advanced past x. */ +inline static void +cell_list_maybe_rewind (struct cell_list *cells, int x) +{ + struct cell *tail = cells->cursor; + if (tail->x > x) + cell_list_rewind (cells); +} + +static void +cell_list_init(struct cell_list *cells, jmp_buf *jmp) +{ + pool_init(cells->cell_pool.base, jmp, + 256*sizeof(struct cell), + sizeof(cells->cell_pool.embedded)); + cells->tail.next = NULL; + cells->tail.x = INT_MAX; + cells->head.x = INT_MIN; + cells->head.next = &cells->tail; + cell_list_rewind (cells); +} + +static void +cell_list_fini(struct cell_list *cells) +{ + pool_fini (cells->cell_pool.base); +} + +/* Empty the cell list. This is called at the start of every pixel + * row. */ +inline static void +cell_list_reset (struct cell_list *cells) +{ + cell_list_rewind (cells); + cells->head.next = &cells->tail; + pool_reset (cells->cell_pool.base); +} + +static struct cell * +cell_list_alloc (struct cell_list *cells, + struct cell *tail, + int x) +{ + struct cell *cell; + + cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); + cell->next = tail->next; + tail->next = cell; + cell->x = x; + cell->uncovered_area = 0; + cell->covered_height = 0; + cell->clipped_height = 0; + return cell; +} + +/* Find a cell at the given x-coordinate. Returns %NULL if a new cell + * needed to be allocated but couldn't be. Cells must be found with + * non-decreasing x-coordinate until the cell list is rewound using + * cell_list_rewind(). Ownership of the returned cell is retained by + * the cell list. */ +inline static struct cell * +cell_list_find (struct cell_list *cells, int x) +{ + struct cell *tail = cells->cursor; + + while (1) { + UNROLL3({ + if (tail->next->x > x) + break; + tail = tail->next; + }); + } + + if (tail->x != x) + tail = cell_list_alloc (cells, tail, x); + return cells->cursor = tail; + +} + +/* Find two cells at x1 and x2. This is exactly equivalent + * to + * + * pair.cell1 = cell_list_find(cells, x1); + * pair.cell2 = cell_list_find(cells, x2); + * + * except with less function call overhead. */ +inline static struct cell_pair +cell_list_find_pair(struct cell_list *cells, int x1, int x2) +{ + struct cell_pair pair; + + pair.cell1 = cells->cursor; + while (1) { + UNROLL3({ + if (pair.cell1->next->x > x1) + break; + pair.cell1 = pair.cell1->next; + }); + } + if (pair.cell1->x != x1) { + struct cell *cell = pool_alloc (cells->cell_pool.base, + sizeof (struct cell)); + cell->x = x1; + cell->uncovered_area = 0; + cell->covered_height = 0; + cell->clipped_height = 0; + cell->next = pair.cell1->next; + pair.cell1->next = cell; + pair.cell1 = cell; + } + + pair.cell2 = pair.cell1; + while (1) { + UNROLL3({ + if (pair.cell2->next->x > x2) + break; + pair.cell2 = pair.cell2->next; + }); + } + if (pair.cell2->x != x2) { + struct cell *cell = pool_alloc (cells->cell_pool.base, + sizeof (struct cell)); + cell->uncovered_area = 0; + cell->covered_height = 0; + cell->clipped_height = 0; + cell->x = x2; + cell->next = pair.cell2->next; + pair.cell2->next = cell; + pair.cell2 = cell; + } + + cells->cursor = pair.cell2; + return pair; +} + +/* Add a subpixel span covering [x1, x2) to the coverage cells. */ +inline static void +cell_list_add_subspan(struct cell_list *cells, + grid_scaled_x_t x1, + grid_scaled_x_t x2) +{ + int ix1, fx1; + int ix2, fx2; + + GRID_X_TO_INT_FRAC(x1, ix1, fx1); + GRID_X_TO_INT_FRAC(x2, ix2, fx2); + + if (ix1 != ix2) { + struct cell_pair p; + p = cell_list_find_pair(cells, ix1, ix2); + p.cell1->uncovered_area += 2*fx1; + ++p.cell1->covered_height; + p.cell2->uncovered_area -= 2*fx2; + --p.cell2->covered_height; + } else { + struct cell *cell = cell_list_find(cells, ix1); + cell->uncovered_area += 2*(fx1-fx2); + } +} + +/* Adds the analytical coverage of an edge crossing the current pixel + * row to the coverage cells and advances the edge's x position to the + * following row. + * + * This function is only called when we know that during this pixel row: + * + * 1) The relative order of all edges on the active list doesn't + * change. In particular, no edges intersect within this row to pixel + * precision. + * + * 2) No new edges start in this row. + * + * 3) No existing edges end mid-row. + * + * This function depends on being called with all edges from the + * active list in the order they appear on the list (i.e. with + * non-decreasing x-coordinate.) */ +static void +cell_list_render_edge( + struct cell_list *cells, + struct edge *edge, + int sign) +{ + grid_scaled_y_t y1, y2, dy; + grid_scaled_x_t dx; + int ix1, ix2; + grid_scaled_x_t fx1, fx2; + + struct quorem x1 = edge->x; + struct quorem x2 = x1; + + if (! edge->vertical) { + x2.quo += edge->dxdy_full.quo; + x2.rem += edge->dxdy_full.rem; + if (x2.rem >= 0) { + ++x2.quo; + x2.rem -= edge->dy; + } + + edge->x = x2; + } + + GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); + GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); + + /* Edge is entirely within a column? */ + if (ix1 == ix2) { + /* We always know that ix1 is >= the cell list cursor in this + * case due to the no-intersections precondition. */ + struct cell *cell = cell_list_find(cells, ix1); + cell->covered_height += sign*GRID_Y; + cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; + return; + } + + /* Orient the edge left-to-right. */ + dx = x2.quo - x1.quo; + if (dx >= 0) { + y1 = 0; + y2 = GRID_Y; + } else { + int tmp; + tmp = ix1; ix1 = ix2; ix2 = tmp; + tmp = fx1; fx1 = fx2; fx2 = tmp; + dx = -dx; + sign = -sign; + y1 = GRID_Y; + y2 = 0; + } + dy = y2 - y1; + + /* Add coverage for all pixels [ix1,ix2] on this row crossed + * by the edge. */ + { + struct cell_pair pair; + struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); + + /* When rendering a previous edge on the active list we may + * advance the cell list cursor past the leftmost pixel of the + * current edge even though the two edges don't intersect. + * e.g. consider two edges going down and rightwards: + * + * --\_+---\_+-----+-----+---- + * \_ \_ | | + * | \_ | \_ | | + * | \_| \_| | + * | \_ \_ | + * ----+-----+-\---+-\---+---- + * + * The left edge touches cells past the starting cell of the + * right edge. Fortunately such cases are rare. + * + * The rewinding is never necessary if the current edge stays + * within a single column because we've checked before calling + * this function that the active list order won't change. */ + cell_list_maybe_rewind(cells, ix1); + + pair = cell_list_find_pair(cells, ix1, ix1+1); + pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); + pair.cell1->covered_height += sign*y.quo; + y.quo += y1; + + if (ix1+1 < ix2) { + struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); + struct cell *cell = pair.cell2; + + ++ix1; + do { + grid_scaled_y_t y_skip = dydx_full.quo; + y.rem += dydx_full.rem; + if (y.rem >= dx) { + ++y_skip; + y.rem -= dx; + } + + y.quo += y_skip; + + y_skip *= sign; + cell->uncovered_area += y_skip*GRID_X; + cell->covered_height += y_skip; + + ++ix1; + cell = cell_list_find(cells, ix1); + } while (ix1 != ix2); + + pair.cell2 = cell; + } + pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; + pair.cell2->covered_height += sign*(y2 - y.quo); + } +} + +static void +polygon_init (struct polygon *polygon, jmp_buf *jmp) +{ + polygon->ymin = polygon->ymax = 0; + polygon->y_buckets = polygon->y_buckets_embedded; + pool_init (polygon->edge_pool.base, jmp, + 8192 - sizeof (struct _pool_chunk), + sizeof (polygon->edge_pool.embedded)); +} + +static void +polygon_fini (struct polygon *polygon) +{ + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + pool_fini (polygon->edge_pool.base); +} + +/* Empties the polygon of all edges. The polygon is then prepared to + * receive new edges and clip them to the vertical range + * [ymin,ymax). */ +static cairo_status_t +polygon_reset (struct polygon *polygon, + grid_scaled_y_t ymin, + grid_scaled_y_t ymax) +{ + unsigned h = ymax - ymin; + unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, + ymin); + + pool_reset(polygon->edge_pool.base); + + if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)) + goto bail_no_mem; /* even if you could, you wouldn't want to. */ + + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + polygon->y_buckets = polygon->y_buckets_embedded; + if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { + polygon->y_buckets = _cairo_malloc_ab (num_buckets, + sizeof (struct edge *)); + if (unlikely (NULL == polygon->y_buckets)) + goto bail_no_mem; + } + memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); + + polygon->ymin = ymin; + polygon->ymax = ymax; + return CAIRO_STATUS_SUCCESS; + + bail_no_mem: + polygon->ymin = 0; + polygon->ymax = 0; + return CAIRO_STATUS_NO_MEMORY; +} + +static void +_polygon_insert_edge_into_its_y_bucket( + struct polygon *polygon, + struct edge *e) +{ + unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); + struct edge **ptail = &polygon->y_buckets[ix]; + e->next = *ptail; + *ptail = e; +} + +inline static void +polygon_add_edge (struct polygon *polygon, + const cairo_edge_t *edge, + int clip) +{ + struct edge *e; + grid_scaled_x_t dx; + grid_scaled_y_t dy; + grid_scaled_y_t ytop, ybot; + grid_scaled_y_t ymin = polygon->ymin; + grid_scaled_y_t ymax = polygon->ymax; + + assert (edge->bottom > edge->top); + + if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) + return; + + e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); + + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + e->dy = dy; + e->dir = edge->dir; + e->clip = clip; + + ytop = edge->top >= ymin ? edge->top : ymin; + ybot = edge->bottom <= ymax ? edge->bottom : ymax; + e->ytop = ytop; + e->height_left = ybot - ytop; + + if (dx == 0) { + e->vertical = TRUE; + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + e->dxdy.quo = 0; + e->dxdy.rem = 0; + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } else { + e->vertical = FALSE; + e->dxdy = floored_divrem (dx, dy); + if (ytop == edge->line.p1.y) { + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + } else { + e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); + e->x.quo += edge->line.p1.x; + } + + if (e->height_left >= GRID_Y) { + e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); + } else { + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } + } + + _polygon_insert_edge_into_its_y_bucket (polygon, e); + + e->x.rem -= dy; /* Bias the remainder for faster + * edge advancement. */ +} + +static void +active_list_reset (struct active_list *active) +{ + active->head = NULL; + active->min_height = 0; +} + +static void +active_list_init(struct active_list *active) +{ + active_list_reset(active); +} + +/* + * Merge two sorted edge lists. + * Input: + * - head_a: The head of the first list. + * - head_b: The head of the second list; head_b cannot be NULL. + * Output: + * Returns the head of the merged list. + * + * Implementation notes: + * To make it fast (in particular, to reduce to an insertion sort whenever + * one of the two input lists only has a single element) we iterate through + * a list until its head becomes greater than the head of the other list, + * then we switch their roles. As soon as one of the two lists is empty, we + * just attach the other one to the current list and exit. + * Writes to memory are only needed to "switch" lists (as it also requires + * attaching to the output list the list which we will be iterating next) and + * to attach the last non-empty list. + */ +static struct edge * +merge_sorted_edges (struct edge *head_a, struct edge *head_b) +{ + struct edge *head, **next; + int32_t x; + + if (head_a == NULL) + return head_b; + + next = &head; + if (head_a->x.quo <= head_b->x.quo) { + head = head_a; + } else { + head = head_b; + goto start_with_b; + } + + do { + x = head_b->x.quo; + while (head_a != NULL && head_a->x.quo <= x) { + next = &head_a->next; + head_a = head_a->next; + } + + *next = head_b; + if (head_a == NULL) + return head; + +start_with_b: + x = head_a->x.quo; + while (head_b != NULL && head_b->x.quo <= x) { + next = &head_b->next; + head_b = head_b->next; + } + + *next = head_a; + if (head_b == NULL) + return head; + } while (1); +} + +/* + * Sort (part of) a list. + * Input: + * - list: The list to be sorted; list cannot be NULL. + * - limit: Recursion limit. + * Output: + * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the + * input list; if the input list has fewer elements, head_out be a sorted list + * containing all the elements of the input list. + * Returns the head of the list of unprocessed elements (NULL if the sorted list contains + * all the elements of the input list). + * + * Implementation notes: + * Special case single element list, unroll/inline the sorting of the first two elements. + * Some tail recursion is used since we iterate on the bottom-up solution of the problem + * (we start with a small sorted list and keep merging other lists of the same size to it). + */ +static struct edge * +sort_edges (struct edge *list, + unsigned int level, + struct edge **head_out) +{ + struct edge *head_other, *remaining; + unsigned int i; + + head_other = list->next; + + /* Single element list -> return */ + if (head_other == NULL) { + *head_out = list; + return NULL; + } + + /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges): + * - Initialize remaining to be the list containing the elements after the second in the input list. + * - Initialize *head_out to be the sorted list containing the first two element. + */ + remaining = head_other->next; + if (list->x.quo <= head_other->x.quo) { + *head_out = list; + /* list->next = head_other; */ /* The input list is already like this. */ + head_other->next = NULL; + } else { + *head_out = head_other; + head_other->next = list; + list->next = NULL; + } + + for (i = 0; i < level && remaining; i++) { + /* Extract a sorted list of the same size as *head_out + * (2^(i+1) elements) from the list of remaining elements. */ + remaining = sort_edges (remaining, i, &head_other); + *head_out = merge_sorted_edges (*head_out, head_other); + } + + /* *head_out now contains (at most) 2^(level+1) elements. */ + + return remaining; +} + +/* Test if the edges on the active list can be safely advanced by a + * full row without intersections or any edges ending. */ +inline static int +active_list_can_step_full_row (struct active_list *active) +{ + const struct edge *e; + int prev_x = INT_MIN; + + /* Recomputes the minimum height of all edges on the active + * list if we have been dropping edges. */ + if (active->min_height <= 0) { + int min_height = INT_MAX; + + e = active->head; + while (NULL != e) { + if (e->height_left < min_height) + min_height = e->height_left; + e = e->next; + } + + active->min_height = min_height; + } + + if (active->min_height < GRID_Y) + return 0; + + /* Check for intersections as no edges end during the next row. */ + e = active->head; + while (NULL != e) { + struct quorem x = e->x; + + if (! e->vertical) { + x.quo += e->dxdy_full.quo; + x.rem += e->dxdy_full.rem; + if (x.rem >= 0) + ++x.quo; + } + + if (x.quo <= prev_x) + return 0; + + prev_x = x.quo; + e = e->next; + } + + return 1; +} + +/* Merges edges on the given subpixel row from the polygon to the + * active_list. */ +inline static void +active_list_merge_edges_from_polygon(struct active_list *active, + struct edge **ptail, + grid_scaled_y_t y, + struct polygon *polygon) +{ + /* Split off the edges on the current subrow and merge them into + * the active list. */ + int min_height = active->min_height; + struct edge *subrow_edges = NULL; + struct edge *tail = *ptail; + + do { + struct edge *next = tail->next; + + if (y == tail->ytop) { + tail->next = subrow_edges; + subrow_edges = tail; + + if (tail->height_left < min_height) + min_height = tail->height_left; + + *ptail = next; + } else + ptail = &tail->next; + + tail = next; + } while (tail); + + if (subrow_edges) { + sort_edges (subrow_edges, UINT_MAX, &subrow_edges); + active->head = merge_sorted_edges (active->head, subrow_edges); + active->min_height = min_height; + } +} + +/* Advance the edges on the active list by one subsample row by + * updating their x positions. Drop edges from the list that end. */ +inline static void +active_list_substep_edges(struct active_list *active) +{ + struct edge **cursor = &active->head; + grid_scaled_x_t prev_x = INT_MIN; + struct edge *unsorted = NULL; + struct edge *edge = *cursor; + + do { + UNROLL3({ + struct edge *next; + + if (NULL == edge) + break; + + next = edge->next; + if (--edge->height_left) { + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } + + if (edge->x.quo < prev_x) { + *cursor = next; + edge->next = unsorted; + unsorted = edge; + } else { + prev_x = edge->x.quo; + cursor = &edge->next; + } + } else { + *cursor = next; + } + edge = next; + }) + } while (1); + + if (unsorted) { + sort_edges (unsorted, UINT_MAX, &unsorted); + active->head = merge_sorted_edges (active->head, unsorted); + } +} + +inline static void +apply_nonzero_fill_rule_for_subrow (struct active_list *active, + struct cell_list *coverages) +{ + struct edge *edge = active->head; + int winding = 0; + int xstart; + int xend; + + cell_list_rewind (coverages); + + while (NULL != edge) { + xstart = edge->x.quo; + winding = edge->dir; + while (1) { + edge = edge->next; + if (NULL == edge) { + ASSERT_NOT_REACHED; + return; + } + + winding += edge->dir; + if (0 == winding) { + if (edge->next == NULL || edge->next->x.quo != edge->x.quo) + break; + } + } + + xend = edge->x.quo; + cell_list_add_subspan (coverages, xstart, xend); + + edge = edge->next; + } +} + +static void +apply_evenodd_fill_rule_for_subrow (struct active_list *active, + struct cell_list *coverages) +{ + struct edge *edge = active->head; + int xstart; + int xend; + + cell_list_rewind (coverages); + + while (NULL != edge) { + xstart = edge->x.quo; + + while (1) { + edge = edge->next; + if (NULL == edge) { + ASSERT_NOT_REACHED; + return; + } + + if (edge->next == NULL || edge->next->x.quo != edge->x.quo) + break; + + edge = edge->next; + } + + xend = edge->x.quo; + cell_list_add_subspan (coverages, xstart, xend); + + edge = edge->next; + } +} + +static void +apply_nonzero_fill_rule_and_step_edges (struct active_list *active, + struct cell_list *coverages) +{ + struct edge **cursor = &active->head; + struct edge *left_edge; + + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; + int winding = left_edge->dir; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) + cursor = &left_edge->next; + else + *cursor = left_edge->next; + + while (1) { + right_edge = *cursor; + if (NULL == right_edge) { + cell_list_render_edge (coverages, left_edge, +1); + return; + } + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) + cursor = &right_edge->next; + else + *cursor = right_edge->next; + + winding += right_edge->dir; + if (0 == winding) { + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } + } + + if (! right_edge->vertical) { + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } + } + } + + cell_list_render_edge (coverages, left_edge, +1); + cell_list_render_edge (coverages, right_edge, -1); + + left_edge = *cursor; + } +} + +static void +apply_evenodd_fill_rule_and_step_edges (struct active_list *active, + struct cell_list *coverages) +{ + struct edge **cursor = &active->head; + struct edge *left_edge; + + left_edge = *cursor; + while (NULL != left_edge) { + struct edge *right_edge; + + left_edge->height_left -= GRID_Y; + if (left_edge->height_left) + cursor = &left_edge->next; + else + *cursor = left_edge->next; + + while (1) { + right_edge = *cursor; + if (NULL == right_edge) { + cell_list_render_edge (coverages, left_edge, +1); + return; + } + + right_edge->height_left -= GRID_Y; + if (right_edge->height_left) + cursor = &right_edge->next; + else + *cursor = right_edge->next; + + if (right_edge->next == NULL || + right_edge->next->x.quo != right_edge->x.quo) + { + break; + } + + if (! right_edge->vertical) { + right_edge->x.quo += right_edge->dxdy_full.quo; + right_edge->x.rem += right_edge->dxdy_full.rem; + if (right_edge->x.rem >= 0) { + ++right_edge->x.quo; + right_edge->x.rem -= right_edge->dy; + } + } + } + + cell_list_render_edge (coverages, left_edge, +1); + cell_list_render_edge (coverages, right_edge, -1); + + left_edge = *cursor; + } +} + +static void +_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp) +{ + polygon_init(converter->polygon, jmp); + active_list_init(converter->active); + cell_list_init(converter->coverages, jmp); + converter->ymin=0; + converter->ymax=0; +} + +static void +_glitter_scan_converter_fini(glitter_scan_converter_t *converter) +{ + polygon_fini(converter->polygon); + cell_list_fini(converter->coverages); + converter->ymin=0; + converter->ymax=0; +} + +static grid_scaled_t +int_to_grid_scaled(int i, int scale) +{ + /* Clamp to max/min representable scaled number. */ + if (i >= 0) { + if (i >= INT_MAX/scale) + i = INT_MAX/scale; + } + else { + if (i <= INT_MIN/scale) + i = INT_MIN/scale; + } + return i*scale; +} + +#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) +#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) + +static cairo_status_t +glitter_scan_converter_reset(glitter_scan_converter_t *converter, + int ymin, int ymax) +{ + cairo_status_t status; + + converter->ymin = 0; + converter->ymax = 0; + + ymin = int_to_grid_scaled_y(ymin); + ymax = int_to_grid_scaled_y(ymax); + + active_list_reset(converter->active); + cell_list_reset(converter->coverages); + status = polygon_reset(converter->polygon, ymin, ymax); + if (status) + return status; + + converter->ymin = ymin; + converter->ymax = ymax; + return CAIRO_STATUS_SUCCESS; +} + +/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) + * These macros convert an input coordinate in the client's + * device space to the rasterisation grid. + */ +/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use + * shifts if possible, and something saneish if not. + */ +#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) +#else +# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) +#endif + +#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) +#else +# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) +#endif + +#define INPUT_TO_GRID_general(in, out, grid_scale) do { \ + long long tmp__ = (long long)(grid_scale) * (in); \ + tmp__ >>= GLITTER_INPUT_BITS; \ + (out) = tmp__; \ +} while (0) + +static void +glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, + const cairo_edge_t *edge, + int clip) +{ + cairo_edge_t e; + + INPUT_TO_GRID_Y (edge->top, e.top); + INPUT_TO_GRID_Y (edge->bottom, e.bottom); + if (e.top >= e.bottom) + return; + + /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ + INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); + INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); + if (e.line.p1.y == e.line.p2.y) + return; + + INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); + INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); + + e.dir = edge->dir; + + polygon_add_edge (converter->polygon, &e, clip); +} + +static cairo_bool_t +active_list_is_vertical (struct active_list *active) +{ + struct edge *e; + + for (e = active->head; e != NULL; e = e->next) { + if (! e->vertical) + return FALSE; + } + + return TRUE; +} + +static void +step_edges (struct active_list *active, int count) +{ + struct edge **cursor = &active->head; + struct edge *edge; + + for (edge = *cursor; edge != NULL; edge = *cursor) { + edge->height_left -= GRID_Y * count; + if (edge->height_left) + cursor = &edge->next; + else + *cursor = edge->next; + } +} + +static cairo_status_t +blit_coverages (struct cell_list *cells, + cairo_span_renderer_t *renderer, + struct pool *span_pool, + int y, int height) +{ + struct cell *cell = cells->head.next; + int prev_x = -1; + int cover = 0, last_cover = 0; + int clip = 0; + cairo_half_open_span_t *spans; + unsigned num_spans; + + assert (cell != &cells->tail); + + /* Count number of cells remaining. */ + { + struct cell *next = cell; + num_spans = 2; + while (next->next) { + next = next->next; + ++num_spans; + } + num_spans = 2*num_spans; + } + + /* Allocate enough spans for the row. */ + pool_reset (span_pool); + spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); + num_spans = 0; + + /* Form the spans from the coverages and areas. */ + for (; cell->next; cell = cell->next) { + int x = cell->x; + int area; + + if (x > prev_x && cover != last_cover) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + spans[num_spans].inverse = 0; + last_cover = cover; + ++num_spans; + } + + cover += cell->covered_height*GRID_X*2; + clip += cell->covered_height*GRID_X*2; + area = cover - cell->uncovered_area; + + if (area != last_cover) { + spans[num_spans].x = x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); + spans[num_spans].inverse = 0; + last_cover = area; + ++num_spans; + } + + prev_x = x+1; + } + + /* Dump them into the renderer. */ + return renderer->render_rows (renderer, y, height, spans, num_spans); +} + +static void +glitter_scan_converter_render(glitter_scan_converter_t *converter, + int nonzero_fill, + cairo_span_renderer_t *span_renderer, + struct pool *span_pool) +{ + int i, j; + int ymax_i = converter->ymax / GRID_Y; + int ymin_i = converter->ymin / GRID_Y; + int h = ymax_i - ymin_i; + struct polygon *polygon = converter->polygon; + struct cell_list *coverages = converter->coverages; + struct active_list *active = converter->active; + + /* Render each pixel row. */ + for (i = 0; i < h; i = j) { + int do_full_step = 0; + + j = i + 1; + + /* Determine if we can ignore this row or use the full pixel + * stepper. */ + if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) { + if (! active->head) { + for (; j < h && ! polygon->y_buckets[j]; j++) + ; + continue; + } + + do_full_step = active_list_can_step_full_row (active); + } + + if (do_full_step) { + /* Step by a full pixel row's worth. */ + if (nonzero_fill) + apply_nonzero_fill_rule_and_step_edges (active, coverages); + else + apply_evenodd_fill_rule_and_step_edges (active, coverages); + + if (active_list_is_vertical (active)) { + while (j < h && + polygon->y_buckets[j] == NULL && + active->min_height >= 2*GRID_Y) + { + active->min_height -= GRID_Y; + j++; + } + if (j != i + 1) + step_edges (active, j - (i + 1)); + } + } else { + grid_scaled_y_t suby; + + /* Subsample this row. */ + for (suby = 0; suby < GRID_Y; suby++) { + grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; + + if (polygon->y_buckets[i]) { + active_list_merge_edges_from_polygon (active, + &polygon->y_buckets[i], y, + polygon); + } + + if (nonzero_fill) + apply_nonzero_fill_rule_for_subrow (active, coverages); + else + apply_evenodd_fill_rule_for_subrow (active, coverages); + + active_list_substep_edges(active); + } + } + + blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i); + cell_list_reset (coverages); + + if (! active->head) + active->min_height = INT_MAX; + else + active->min_height -= GRID_Y; + } +} + +struct _cairo_clip_tor_scan_converter { + cairo_scan_converter_t base; + + glitter_scan_converter_t converter[1]; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + + cairo_fill_rule_t clip_fill_rule; + cairo_antialias_t clip_antialias; + + jmp_buf jmp; + + struct { + struct pool base[1]; + cairo_half_open_span_t embedded[32]; + } span_pool; +}; + +typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t; + +static void +_cairo_clip_tor_scan_converter_destroy (void *converter) +{ + cairo_clip_tor_scan_converter_t *self = converter; + if (self == NULL) { + return; + } + _glitter_scan_converter_fini (self->converter); + pool_fini (self->span_pool.base); + free(self); +} + +static cairo_status_t +_cairo_clip_tor_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_clip_tor_scan_converter_t *self = converter; + cairo_status_t status; + + if ((status = setjmp (self->jmp))) + return _cairo_scan_converter_set_error (self, _cairo_error (status)); + + glitter_scan_converter_render (self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING, + renderer, + self->span_pool.base); + return CAIRO_STATUS_SUCCESS; +} + +cairo_scan_converter_t * +_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) +{ + cairo_clip_tor_scan_converter_t *self; + cairo_polygon_t clipper; + cairo_status_t status; + int i; + + self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter)); + if (unlikely (self == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail_nomem; + } + + self->base.destroy = _cairo_clip_tor_scan_converter_destroy; + self->base.generate = _cairo_clip_tor_scan_converter_generate; + + pool_init (self->span_pool.base, &self->jmp, + 250 * sizeof(self->span_pool.embedded[0]), + sizeof(self->span_pool.embedded)); + + _glitter_scan_converter_init (self->converter, &self->jmp); + status = glitter_scan_converter_reset (self->converter, + clip->extents.y, + clip->extents.y + clip->extents.height); + if (unlikely (status)) + goto bail; + + self->fill_rule = fill_rule; + self->antialias = antialias; + + for (i = 0; i < polygon->num_edges; i++) + glitter_scan_converter_add_edge (self->converter, + &polygon->edges[i], + FALSE); + + status = _cairo_clip_get_polygon (clip, + &clipper, + &self->clip_fill_rule, + &self->clip_antialias); + if (unlikely (status)) + goto bail; + + for (i = 0; i < clipper.num_edges; i++) + glitter_scan_converter_add_edge (self->converter, + &clipper.edges[i], + TRUE); + _cairo_polygon_fini (&clipper); + + return &self->base; + + bail: + self->base.destroy(&self->base); + bail_nomem: + return _cairo_scan_converter_create_in_error (status); +} + diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c index 0ebe9b207d78..d499bf0adece 100644 --- a/gfx/cairo/cairo/src/cairo-clip.c +++ b/gfx/cairo/cairo/src/cairo-clip.c @@ -40,17 +40,20 @@ */ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" #include "cairo-gstate-private.h" #include "cairo-path-fixed-private.h" +#include "cairo-pattern-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-region-private.h" -#if HAS_FREED_POOL static freed_pool_t clip_path_pool; -#endif +static freed_pool_t clip_pool; + +const cairo_clip_t __cairo_clip_all; static cairo_clip_path_t * _cairo_clip_path_create (cairo_clip_t *clip) @@ -59,24 +62,20 @@ _cairo_clip_path_create (cairo_clip_t *clip) clip_path = _freed_pool_get (&clip_path_pool); if (unlikely (clip_path == NULL)) { - clip_path = malloc (sizeof (cairo_clip_path_t)); + clip_path = _cairo_malloc (sizeof (cairo_clip_path_t)); if (unlikely (clip_path == NULL)) return NULL; } CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1); - clip_path->flags = 0; - clip_path->region = NULL; - clip_path->surface = NULL; - clip_path->prev = clip->path; clip->path = clip_path; return clip_path; } -static cairo_clip_path_t * +cairo_clip_path_t * _cairo_clip_path_reference (cairo_clip_path_t *clip_path) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); @@ -86,7 +85,7 @@ _cairo_clip_path_reference (cairo_clip_path_t *clip_path) return clip_path; } -static void +void _cairo_clip_path_destroy (cairo_clip_path_t *clip_path) { assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); @@ -95,10 +94,6 @@ _cairo_clip_path_destroy (cairo_clip_path_t *clip_path) return; _cairo_path_fixed_fini (&clip_path->path); - if (clip_path->region != NULL) - cairo_region_destroy (clip_path->region); - if (clip_path->surface != NULL) - cairo_surface_destroy (clip_path->surface); if (clip_path->prev != NULL) _cairo_clip_path_destroy (clip_path->prev); @@ -106,115 +101,134 @@ _cairo_clip_path_destroy (cairo_clip_path_t *clip_path) _freed_pool_put (&clip_path_pool, clip_path); } -void -_cairo_clip_init (cairo_clip_t *clip) -{ - clip->all_clipped = FALSE; - clip->path = NULL; -} - -static void -_cairo_clip_set_all_clipped (cairo_clip_t *clip) -{ - clip->all_clipped = TRUE; - if (clip->path != NULL) { - _cairo_clip_path_destroy (clip->path); - clip->path = NULL; - } -} - -static cairo_status_t -_cairo_clip_intersect_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rect) -{ - cairo_clip_path_t *clip_path; - cairo_status_t status; - - if (clip->path != NULL) { - if (rect->x <= clip->path->extents.x && - rect->y <= clip->path->extents.y && - rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width && - rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height) - { - return CAIRO_STATUS_SUCCESS; - } - } - - clip_path = _cairo_clip_path_create (clip); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_path_fixed_init (&clip_path->path); - - status = _cairo_path_fixed_move_to (&clip_path->path, - _cairo_fixed_from_int (rect->x), - _cairo_fixed_from_int (rect->y)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (&clip_path->path, - _cairo_fixed_from_int (rect->width), - _cairo_fixed_from_int (0)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (&clip_path->path, - _cairo_fixed_from_int (0), - _cairo_fixed_from_int (rect->height)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_rel_line_to (&clip_path->path, - _cairo_fixed_from_int (-rect->width), - _cairo_fixed_from_int (0)); - assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_path_fixed_close_path (&clip_path->path); - assert (status == CAIRO_STATUS_SUCCESS); - - clip_path->fill_rule = CAIRO_FILL_RULE_WINDING; - clip_path->tolerance = 1; - clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT; - clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; - - clip_path->extents = *rect; - if (clip_path->prev != NULL) { - if (! _cairo_rectangle_intersect (&clip_path->extents, - &clip_path->prev->extents)) - { - _cairo_clip_set_all_clipped (clip); - } - } - - /* could preallocate the region if it proves worthwhile */ - - return CAIRO_STATUS_SUCCESS; -} - cairo_clip_t * -_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) +_cairo_clip_create (void) { - if (other != NULL) { - clip->all_clipped = other->all_clipped; - if (other->path == NULL) { - clip->path = NULL; - if (! clip->all_clipped) - clip = NULL; - } else { - clip->path = _cairo_clip_path_reference (other->path); - } - } else { - _cairo_clip_init (clip); - clip = NULL; + cairo_clip_t *clip; + + clip = _freed_pool_get (&clip_pool); + if (unlikely (clip == NULL)) { + clip = _cairo_malloc (sizeof (cairo_clip_t)); + if (unlikely (clip == NULL)) + return NULL; } + clip->extents = _cairo_unbounded_rectangle; + + clip->path = NULL; + clip->boxes = NULL; + clip->num_boxes = 0; + clip->region = NULL; + clip->is_region = FALSE; + return clip; } void -_cairo_clip_reset (cairo_clip_t *clip) +_cairo_clip_destroy (cairo_clip_t *clip) { - clip->all_clipped = FALSE; - if (clip->path != NULL) { + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return; + + if (clip->path != NULL) _cairo_clip_path_destroy (clip->path); - clip->path = NULL; - } + + if (clip->boxes != &clip->embedded_box) + free (clip->boxes); + cairo_region_destroy (clip->region); + + _freed_pool_put (&clip_pool, clip); } -static cairo_status_t +cairo_clip_t * +_cairo_clip_copy (const cairo_clip_t *clip) +{ + cairo_clip_t *copy; + + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return (cairo_clip_t *) clip; + + copy = _cairo_clip_create (); + + if (clip->path) + copy->path = _cairo_clip_path_reference (clip->path); + + if (clip->num_boxes) { + if (clip->num_boxes == 1) { + copy->boxes = ©->embedded_box; + } else { + copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); + if (unlikely (copy->boxes == NULL)) + return _cairo_clip_set_all_clipped (copy); + } + + memcpy (copy->boxes, clip->boxes, + clip->num_boxes * sizeof (cairo_box_t)); + copy->num_boxes = clip->num_boxes; + } + + copy->extents = clip->extents; + copy->region = cairo_region_reference (clip->region); + copy->is_region = clip->is_region; + + return copy; +} + +cairo_clip_t * +_cairo_clip_copy_path (const cairo_clip_t *clip) +{ + cairo_clip_t *copy; + + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return (cairo_clip_t *) clip; + + assert (clip->num_boxes); + + copy = _cairo_clip_create (); + copy->extents = clip->extents; + if (clip->path) + copy->path = _cairo_clip_path_reference (clip->path); + + return copy; +} + +cairo_clip_t * +_cairo_clip_copy_region (const cairo_clip_t *clip) +{ + cairo_clip_t *copy; + int i; + + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return (cairo_clip_t *) clip; + + assert (clip->num_boxes); + + copy = _cairo_clip_create (); + copy->extents = clip->extents; + + if (clip->num_boxes == 1) { + copy->boxes = ©->embedded_box; + } else { + copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); + if (unlikely (copy->boxes == NULL)) + return _cairo_clip_set_all_clipped (copy); + } + + for (i = 0; i < clip->num_boxes; i++) { + copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x); + copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y); + copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x); + copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y); + } + copy->num_boxes = clip->num_boxes; + + copy->region = cairo_region_reference (clip->region); + copy->is_region = TRUE; + + return copy; +} + +cairo_clip_t * _cairo_clip_intersect_path (cairo_clip_t *clip, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, @@ -225,1087 +239,461 @@ _cairo_clip_intersect_path (cairo_clip_t *clip, cairo_status_t status; cairo_rectangle_int_t extents; cairo_box_t box; - cairo_bool_t is_box = FALSE; - if (clip->path != NULL) { - if (clip->path->fill_rule == fill_rule && - (path->is_rectilinear || tolerance == clip->path->tolerance) && - antialias == clip->path->antialias && - _cairo_path_fixed_equal (&clip->path->path, path)) - { - return CAIRO_STATUS_SUCCESS; + if (_cairo_clip_is_all_clipped (clip)) + return clip; + + /* catch the empty clip path */ + if (_cairo_path_fixed_fill_is_empty (path)) + return _cairo_clip_set_all_clipped (clip); + + if (_cairo_path_fixed_is_box (path, &box)) { + if (antialias == CAIRO_ANTIALIAS_NONE) { + box.p1.x = _cairo_fixed_round_down (box.p1.x); + box.p1.y = _cairo_fixed_round_down (box.p1.y); + box.p2.x = _cairo_fixed_round_down (box.p2.x); + box.p2.y = _cairo_fixed_round_down (box.p2.y); } + + return _cairo_clip_intersect_box (clip, &box); } + if (_cairo_path_fixed_fill_is_rectilinear (path)) + return _cairo_clip_intersect_rectilinear_path (clip, path, + fill_rule, antialias); _cairo_path_fixed_approximate_clip_extents (path, &extents); - if (extents.width == 0 || extents.height == 0) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } + if (extents.width == 0 || extents.height == 0) + return _cairo_clip_set_all_clipped (clip); - is_box = _cairo_path_fixed_is_box (path, &box); - if (clip->path != NULL) { - if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - /* does this clip wholly subsume the others? */ - if (is_box && - box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) && - box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) && - box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) && - box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height)) - { - return CAIRO_STATUS_SUCCESS; - } - } + clip = _cairo_clip_intersect_rectangle (clip, &extents); + if (_cairo_clip_is_all_clipped (clip)) + return clip; clip_path = _cairo_clip_path_create (clip); if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_clip_set_all_clipped (clip); status = _cairo_path_fixed_init_copy (&clip_path->path, path); - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } + if (unlikely (status)) + return _cairo_clip_set_all_clipped (clip); - clip_path->extents = extents; clip_path->fill_rule = fill_rule; clip_path->tolerance = tolerance; clip_path->antialias = antialias; - if (is_box) - clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX; - return CAIRO_STATUS_SUCCESS; + if (clip->region) { + cairo_region_destroy (clip->region); + clip->region = NULL; + } + + clip->is_region = FALSE; + return clip; +} + +static cairo_clip_t * +_cairo_clip_intersect_clip_path (cairo_clip_t *clip, + const cairo_clip_path_t *clip_path) +{ + if (clip_path->prev) + clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev); + + return _cairo_clip_intersect_path (clip, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias); +} + +cairo_clip_t * +_cairo_clip_intersect_clip (cairo_clip_t *clip, + const cairo_clip_t *other) +{ + if (_cairo_clip_is_all_clipped (clip)) + return clip; + + if (other == NULL) + return clip; + + if (clip == NULL) + return _cairo_clip_copy (other); + + if (_cairo_clip_is_all_clipped (other)) + return _cairo_clip_set_all_clipped (clip); + + if (! _cairo_rectangle_intersect (&clip->extents, &other->extents)) + return _cairo_clip_set_all_clipped (clip); + + if (other->num_boxes) { + cairo_boxes_t boxes; + + _cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes); + clip = _cairo_clip_intersect_boxes (clip, &boxes); + } + + if (! _cairo_clip_is_all_clipped (clip)) { + if (other->path) { + if (clip->path == NULL) + clip->path = _cairo_clip_path_reference (other->path); + else + clip = _cairo_clip_intersect_clip_path (clip, other->path); + } + } + + if (clip->region) { + cairo_region_destroy (clip->region); + clip->region = NULL; + } + clip->is_region = FALSE; + + return clip; } cairo_bool_t _cairo_clip_equal (const cairo_clip_t *clip_a, const cairo_clip_t *clip_b) { - const cairo_clip_path_t *clip_path_a, *clip_path_b; + const cairo_clip_path_t *cp_a, *cp_b; - clip_path_a = clip_a->path; - clip_path_b = clip_b->path; + /* are both all-clipped or no-clip? */ + if (clip_a == clip_b) + return TRUE; - while (clip_path_a && clip_path_b) { - if (clip_path_a == clip_path_b) + /* or just one of them? */ + if (clip_a == NULL || clip_b == NULL || + _cairo_clip_is_all_clipped (clip_a) || + _cairo_clip_is_all_clipped (clip_b)) + { + return FALSE; + } + + /* We have a pair of normal clips, check their contents */ + + if (clip_a->num_boxes != clip_b->num_boxes) + return FALSE; + + if (memcmp (clip_a->boxes, clip_b->boxes, + sizeof (cairo_box_t) * clip_a->num_boxes)) + return FALSE; + + cp_a = clip_a->path; + cp_b = clip_b->path; + while (cp_a && cp_b) { + if (cp_a == cp_b) return TRUE; - if (clip_path_a->fill_rule != clip_path_b->fill_rule) + /* XXX compare reduced polygons? */ + + if (cp_a->antialias != cp_b->antialias) return FALSE; - if (clip_path_a->tolerance != clip_path_b->tolerance) + if (cp_a->tolerance != cp_b->tolerance) return FALSE; - if (clip_path_a->antialias != clip_path_b->antialias) + if (cp_a->fill_rule != cp_b->fill_rule) return FALSE; - if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path)) + if (! _cairo_path_fixed_equal (&cp_a->path, + &cp_b->path)) return FALSE; - clip_path_a = clip_path_a->prev; - clip_path_b = clip_path_b->prev; + cp_a = cp_a->prev; + cp_b = cp_b->prev; } - return clip_path_a == clip_path_b; /* ie both NULL */ + return cp_a == NULL && cp_b == NULL; } -cairo_status_t -_cairo_clip_clip (cairo_clip_t *clip, - const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - /* catch the empty clip path */ - if (_cairo_path_fixed_fill_is_empty (path)) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_clip_intersect_path (clip, - path, fill_rule, tolerance, - antialias); -} - -cairo_status_t -_cairo_clip_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rectangle) -{ - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (rectangle->width == 0 || rectangle->height == 0) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - /* if a smaller clip has already been set, ignore the new path */ - if (clip->path != NULL) { - if (rectangle->x <= clip->path->extents.x && - rectangle->y <= clip->path->extents.y && - rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width && - rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height) - { - return CAIRO_STATUS_SUCCESS; - } - } - - return _cairo_clip_intersect_rectangle (clip, rectangle); -} - -static cairo_status_t -_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip, - cairo_clip_path_t *other_path, - const cairo_matrix_t *matrix) +static cairo_clip_t * +_cairo_clip_path_copy_with_translation (cairo_clip_t *clip, + cairo_clip_path_t *other_path, + int fx, int fy) { cairo_status_t status; cairo_clip_path_t *clip_path; - cairo_bool_t is_empty; - if (other_path->prev != NULL) { - status = _cairo_clip_path_reapply_clip_path_transform (clip, - other_path->prev, - matrix); - if (unlikely (status)) - return status; - } + if (other_path->prev != NULL) + clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev, + fx, fy); + if (_cairo_clip_is_all_clipped (clip)) + return clip; clip_path = _cairo_clip_path_create (clip); if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_clip_set_all_clipped (clip); status = _cairo_path_fixed_init_copy (&clip_path->path, &other_path->path); - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } + if (unlikely (status)) + return _cairo_clip_set_all_clipped (clip); - _cairo_path_fixed_transform (&clip_path->path, matrix); - _cairo_path_fixed_approximate_clip_extents (&clip_path->path, - &clip_path->extents); - if (clip_path->prev != NULL) { - is_empty = _cairo_rectangle_intersect (&clip_path->extents, - &clip_path->prev->extents); - } + _cairo_path_fixed_translate (&clip_path->path, fx, fy); clip_path->fill_rule = other_path->fill_rule; clip_path->tolerance = other_path->tolerance; clip_path->antialias = other_path->antialias; - return CAIRO_STATUS_SUCCESS; + return clip; } -static cairo_status_t -_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip, - cairo_clip_path_t *other_path, - int tx, int ty) +cairo_clip_t * +_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty) { - cairo_status_t status; + int fx, fy, i; cairo_clip_path_t *clip_path; - if (other_path->prev != NULL) { - status = _cairo_clip_path_reapply_clip_path_translate (clip, - other_path->prev, - tx, ty); - if (unlikely (status)) - return status; + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return clip; + + if (tx == 0 && ty == 0) + return clip; + + fx = _cairo_fixed_from_int (tx); + fy = _cairo_fixed_from_int (ty); + + for (i = 0; i < clip->num_boxes; i++) { + clip->boxes[i].p1.x += fx; + clip->boxes[i].p2.x += fx; + clip->boxes[i].p1.y += fy; + clip->boxes[i].p2.y += fy; } - clip_path = _cairo_clip_path_create (clip); - if (unlikely (clip_path == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + clip->extents.x += tx; + clip->extents.y += ty; - status = _cairo_path_fixed_init_copy (&clip_path->path, - &other_path->path); - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } - - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (tx), - _cairo_fixed_from_int (ty)); - - clip_path->fill_rule = other_path->fill_rule; - clip_path->tolerance = other_path->tolerance; - clip_path->antialias = other_path->antialias; - - clip_path->flags = other_path->flags; - if (other_path->region != NULL) { - clip_path->region = cairo_region_copy (other_path->region); - status = clip_path->region->status; - if (unlikely (status)) { - clip->path = clip->path->prev; - _cairo_clip_path_destroy (clip_path); - return status; - } - - cairo_region_translate (clip_path->region, tx, ty); - } - clip_path->surface = cairo_surface_reference (other_path->surface); - - clip_path->extents = other_path->extents; - clip_path->extents.x += tx; - clip_path->extents.y += ty; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_clip_init_copy_transformed (cairo_clip_t *clip, - cairo_clip_t *other, - const cairo_matrix_t *matrix) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - int tx, ty; - - if (other == NULL) { - _cairo_clip_init (clip); - return CAIRO_STATUS_SUCCESS; - } - - if (other->all_clipped) { - _cairo_clip_init (clip); - clip->all_clipped = TRUE; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_matrix_is_identity (matrix)) { - _cairo_clip_init_copy (clip, other); - return CAIRO_STATUS_SUCCESS; - } - - if (other->path != NULL) { - _cairo_clip_init (clip); - - /* if we only need to translate, so we can reuse the caches... */ - /* XXX we still loose the benefit of constructs when the copy is - * deleted though. Indirect clip_paths? - */ - if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) { - status = _cairo_clip_path_reapply_clip_path_translate (clip, - other->path, - tx, ty); - } else { - status = _cairo_clip_path_reapply_clip_path_transform (clip, - other->path, - matrix); - if (clip->path->extents.width == 0 && - clip->path->extents.height == 0) - { - _cairo_clip_set_all_clipped (clip); - } - } - } - - return status; -} - -static cairo_status_t -_cairo_clip_apply_clip_path (cairo_clip_t *clip, - const cairo_clip_path_t *path) -{ - cairo_status_t status; - - if (path->prev != NULL) - status = _cairo_clip_apply_clip_path (clip, path->prev); - - return _cairo_clip_intersect_path (clip, - &path->path, - path->fill_rule, - path->tolerance, - path->antialias); -} - -cairo_status_t -_cairo_clip_apply_clip (cairo_clip_t *clip, - const cairo_clip_t *other) -{ - cairo_status_t status; - - if (clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (other->all_clipped) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_STATUS_SUCCESS; - } - - status = CAIRO_STATUS_SUCCESS; - if (other->path != NULL) - status = _cairo_clip_apply_clip_path (clip, other->path); - - return status; -} - -static inline cairo_bool_t -_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path) -{ - while (clip_path != NULL) { - if (! clip_path->path.is_rectilinear) - return FALSE; - - clip_path = clip_path->prev; - } - - return TRUE; -} - -static cairo_int_status_t -_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path) -{ - cairo_traps_t traps; - cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)]; - cairo_box_t *boxes = stack_boxes; - cairo_status_t status; - int n; - - /* If we have nothing to intersect with this path, then it cannot - * magically be reduced into a region. - */ - if (clip_path->prev == NULL) - goto UNSUPPORTED; - - /* Start simple... Intersect some boxes with an arbitrary path. */ - if (! clip_path->path.is_rectilinear) - goto UNSUPPORTED; - if (clip_path->prev->prev != NULL) - goto UNSUPPORTED; - - _cairo_traps_init (&traps); - _cairo_box_from_rectangle (&boxes[0], &clip_path->extents); - _cairo_traps_limit (&traps, boxes, 1); - - status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path, - clip_path->fill_rule, - &traps); - if (unlikely (_cairo_status_is_error (status))) - return status; - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - goto UNSUPPORTED; - - if (unlikely (traps.num_traps == 0)) { - clip_path->region = cairo_region_create (); - clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; - return CAIRO_STATUS_SUCCESS; - } - - if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) { - boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); - if (unlikely (boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (n = 0; n < traps.num_traps; n++) { - boxes[n].p1.x = traps.traps[n].left.p1.x; - boxes[n].p1.y = traps.traps[n].top; - boxes[n].p2.x = traps.traps[n].right.p1.x; - boxes[n].p2.y = traps.traps[n].bottom; - } - - _cairo_traps_clear (&traps); - _cairo_traps_limit (&traps, boxes, n); - status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path, - clip_path->prev->fill_rule, - clip_path->prev->tolerance, - &traps); - if (boxes != stack_boxes) - free (boxes); - - if (unlikely (status)) - return status; - - status = _cairo_traps_extract_region (&traps, &clip_path->region); - _cairo_traps_fini (&traps); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - goto UNSUPPORTED; - if (unlikely (status)) - return status; - - clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; - return CAIRO_STATUS_SUCCESS; - -UNSUPPORTED: - clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED; - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_clip_path_to_region (cairo_clip_path_t *clip_path) -{ - cairo_int_status_t status; - cairo_region_t *prev = NULL; - - if (clip_path->flags & - (CAIRO_CLIP_PATH_HAS_REGION | - CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED)) - { - return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ? - CAIRO_INT_STATUS_UNSUPPORTED : - CAIRO_STATUS_SUCCESS; - } - - if (! clip_path->path.maybe_fill_region) - return _cairo_clip_path_to_region_geometric (clip_path); - - /* first retrieve the region for our antecedents */ - if (clip_path->prev != NULL) { - status = _cairo_clip_path_to_region (clip_path->prev); - if (status) { - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_clip_path_to_region_geometric (clip_path); - - return status; - } - - prev = clip_path->prev->region; - } - - /* now extract the region for ourselves */ - clip_path->region = - _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path, - clip_path->fill_rule, - &clip_path->extents); - assert (clip_path->region != NULL); - - status = clip_path->region->status; - if (unlikely (status)) - return status; - - if (prev != NULL) { - status = cairo_region_intersect (clip_path->region, prev); - if (unlikely (status)) - return status; - } - - clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; - return CAIRO_STATUS_SUCCESS; -} - -static inline int -pot (int v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - -/* XXX there is likely a faster method! ;-) */ -static cairo_status_t -_region_clip_to_boxes (const cairo_region_t *region, - cairo_box_t **boxes, - int *num_boxes, - int *size_boxes) -{ - cairo_traps_t traps; - cairo_status_t status; - int n, num_rects; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, *boxes, *num_boxes); - traps.is_rectilinear = TRUE; - traps.is_rectangular = TRUE; - - num_rects = cairo_region_num_rectangles (region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - cairo_point_t p1, p2; - - cairo_region_get_rectangle (region, n, &rect); - - p1.x = _cairo_fixed_from_int (rect.x); - p1.y = _cairo_fixed_from_int (rect.y); - p2.x = _cairo_fixed_from_int (rect.x + rect.width); - p2.y = _cairo_fixed_from_int (rect.y + rect.height); - - status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2); - if (unlikely (status)) - goto CLEANUP; - } - - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - - n = *size_boxes; - if (n < 0) - n = -n; - - if (traps.num_traps > n) { - cairo_box_t *new_boxes; - - new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); - if (unlikely (new_boxes == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - if (*size_boxes > 0) - free (*boxes); - - *boxes = new_boxes; - *size_boxes = traps.num_traps; - } - - for (n = 0; n < traps.num_traps; n++) { - (*boxes)[n].p1.x = traps.traps[n].left.p1.x; - (*boxes)[n].p1.y = traps.traps[n].top; - (*boxes)[n].p2.x = traps.traps[n].right.p1.x; - (*boxes)[n].p2.y = traps.traps[n].bottom; - } - *num_boxes = n; - - CLEANUP: - _cairo_traps_fini (&traps); - - return status; -} - -static cairo_status_t -_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_box_t **boxes, - int *num_boxes, - int *size_boxes) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_status_t status; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, *boxes, *num_boxes); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, *boxes, *num_boxes); - - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (unlikely (_cairo_status_is_error (status))) - goto CLEANUP; - if (status == CAIRO_STATUS_SUCCESS) - goto BOXES; - - /* tolerance will be ignored as the path is rectilinear */ - status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) { - *num_boxes = 0; - } else { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - int i; - - BOXES: - i = *size_boxes; - if (i < 0) - i = -i; - - if (traps.num_traps > i) { - cairo_box_t *new_boxes; - int new_size; - - new_size = pot (traps.num_traps); - new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t)); - if (unlikely (new_boxes == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - if (*size_boxes > 0) - free (*boxes); - - *boxes = new_boxes; - *size_boxes = new_size; - } - - for (i = 0; i < traps.num_traps; i++) { - (*boxes)[i].p1.x = traps.traps[i].left.p1.x; - (*boxes)[i].p1.y = traps.traps[i].top; - (*boxes)[i].p2.x = traps.traps[i].right.p1.x; - (*boxes)[i].p2.y = traps.traps[i].bottom; - } - *num_boxes = i; - } - } - - CLEANUP: - _cairo_polygon_fini (&polygon); - _cairo_traps_fini (&traps); - - return status; -} - -static cairo_int_status_t -_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path, - cairo_box_t **boxes, - int *count) -{ - int size = -*count; - int num_boxes = 0; - cairo_status_t status; - - if (clip_path->region != NULL) { - int num_rects, n; - - num_rects = cairo_region_num_rectangles (clip_path->region); - if (num_rects > -size) { - cairo_box_t *new_boxes; - - new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t)); - if (unlikely (new_boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *boxes = new_boxes; - } - - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_path->region, n, &rect); - (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x); - (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y); - (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width); - (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height); - } - - *count = num_rects; - return CAIRO_STATUS_SUCCESS; - } - - /* keep it simple at first */ - if (! _clip_paths_are_rectilinear (clip_path)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - assert (-size >= 1); - if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) { - num_boxes = 1; - } else { - status = _rectilinear_clip_to_boxes (&clip_path->path, - clip_path->fill_rule, - boxes, &num_boxes, &size); - if (unlikely (status)) - return status; - } - - while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) { - cairo_box_t box; - - if (clip_path->region != NULL) { - status = _region_clip_to_boxes (clip_path->region, - boxes, &num_boxes, &size); - if (unlikely (status)) - return status; - - break; - } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) { - int i, j; - - for (i = j = 0; i < num_boxes; i++) { - if (j != i) - (*boxes)[j] = (*boxes)[i]; - - if (box.p1.x > (*boxes)[j].p1.x) - (*boxes)[j].p1.x = box.p1.x; - if (box.p2.x < (*boxes)[j].p2.x) - (*boxes)[j].p2.x = box.p2.x; - - if (box.p1.y > (*boxes)[j].p1.y) - (*boxes)[j].p1.y = box.p1.y; - if (box.p2.y < (*boxes)[j].p2.y) - (*boxes)[j].p2.y = box.p2.y; - - j += (*boxes)[j].p2.x > (*boxes)[j].p1.x && - (*boxes)[j].p2.y > (*boxes)[j].p1.y; - } - - num_boxes = j; - } else { - status = _rectilinear_clip_to_boxes (&clip_path->path, - clip_path->fill_rule, - boxes, &num_boxes, &size); - if (unlikely (status)) - return status; - } - } - - *count = num_boxes; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t * -_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path, - cairo_surface_t *target, - int *tx, int *ty) -{ - const cairo_rectangle_int_t *clip_extents = &clip_path->extents; - cairo_bool_t need_translate; - cairo_surface_t *surface; - cairo_clip_path_t *prev; - cairo_status_t status; - - while (clip_path->prev != NULL && - clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && - clip_path->path.maybe_fill_region) - { - clip_path = clip_path->prev; - } - - clip_extents = &clip_path->extents; - if (clip_path->surface != NULL && - clip_path->surface->backend == target->backend) - { - *tx = clip_extents->x; - *ty = clip_extents->y; - return clip_path->surface; - } - - surface = _cairo_surface_create_similar_scratch (target, - CAIRO_CONTENT_ALPHA, - clip_extents->width, - clip_extents->height); - if (surface == NULL) { - surface = cairo_image_surface_create (CAIRO_FORMAT_A8, - clip_extents->width, - clip_extents->height); - } - if (unlikely (surface->status)) - return surface; - - need_translate = clip_extents->x | clip_extents->y; - if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && - clip_path->path.maybe_fill_region) - { - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_SOURCE, - &_cairo_pattern_white.base, - NULL); - if (unlikely (status)) - goto BAIL; - } - else - { - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_CLEAR, - &_cairo_pattern_clear.base, - NULL); - if (unlikely (status)) - goto BAIL; - - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (-clip_extents->x), - _cairo_fixed_from_int (-clip_extents->y)); - } - status = _cairo_surface_fill (surface, - CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (clip_extents->x), - _cairo_fixed_from_int (clip_extents->y)); - } - - if (unlikely (status)) - goto BAIL; - } - - prev = clip_path->prev; - while (prev != NULL) { - if (prev->flags & CAIRO_CLIP_PATH_IS_BOX && - prev->path.maybe_fill_region) - { - /* a simple box only affects the extents */ - } - else if (prev->path.is_rectilinear || - prev->surface == NULL || - prev->surface->backend != target->backend) - { - if (need_translate) { - _cairo_path_fixed_translate (&prev->path, - _cairo_fixed_from_int (-clip_extents->x), - _cairo_fixed_from_int (-clip_extents->y)); - } - status = _cairo_surface_fill (surface, - CAIRO_OPERATOR_IN, - &_cairo_pattern_white.base, - &prev->path, - prev->fill_rule, - prev->tolerance, - prev->antialias, - NULL); - if (need_translate) { - _cairo_path_fixed_translate (&prev->path, - _cairo_fixed_from_int (clip_extents->x), - _cairo_fixed_from_int (clip_extents->y)); - } - - if (unlikely (status)) - goto BAIL; - } - else - { - cairo_surface_pattern_t pattern; - cairo_surface_t *prev_surface; - int prev_tx, prev_ty; - - prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty); - status = prev_surface->status; - if (unlikely (status)) - goto BAIL; - - _cairo_pattern_init_for_surface (&pattern, prev_surface); - pattern.base.filter = CAIRO_FILTER_NEAREST; - cairo_matrix_init_translate (&pattern.base.matrix, - clip_extents->x - prev_tx, - clip_extents->y - prev_ty); - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_IN, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) - goto BAIL; - - break; - } - - prev = prev->prev; - } - - *tx = clip_extents->x; - *ty = clip_extents->y; - cairo_surface_destroy (clip_path->surface); - return clip_path->surface = surface; - - BAIL: - cairo_surface_destroy (surface); - return _cairo_surface_create_in_error (status); -} - -cairo_bool_t -_cairo_clip_contains_rectangle (cairo_clip_t *clip, - const cairo_rectangle_int_t *rect) -{ - cairo_clip_path_t *clip_path; - - if (clip == NULL) - return FALSE; + if (clip->path == NULL) + return clip; clip_path = clip->path; - if (clip_path->extents.x > rect->x || - clip_path->extents.y > rect->y || - clip_path->extents.x + clip_path->extents.width < rect->x + rect->width || - clip_path->extents.y + clip_path->extents.height < rect->y + rect->height) - { - return FALSE; + clip->path = NULL; + clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy); + _cairo_clip_path_destroy (clip_path); + + return clip; +} + +static cairo_status_t +_cairo_path_fixed_add_box (cairo_path_fixed_t *path, + const cairo_box_t *box) +{ + cairo_status_t status; + + status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_close_path (path); +} + +static cairo_status_t +_cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path, + const cairo_boxes_t *boxes) +{ + cairo_status_t status; + const struct _cairo_boxes_chunk *chunk; + int i; + + _cairo_path_fixed_init (path); + if (boxes->num_boxes == 0) + return CAIRO_STATUS_SUCCESS; + + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_path_fixed_add_box (path, &chunk->base[i]); + if (unlikely (status)) { + _cairo_path_fixed_fini (path); + return status; + } + } } - do { - cairo_box_t box; + return CAIRO_STATUS_SUCCESS; +} - if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) - return FALSE; +static cairo_clip_t * +_cairo_clip_intersect_clip_path_transformed (cairo_clip_t *clip, + const cairo_clip_path_t *clip_path, + const cairo_matrix_t *m) +{ + cairo_path_fixed_t path; - if (! _cairo_path_fixed_is_box (&clip_path->path, &box)) - return FALSE; + if (clip_path->prev) + clip = _cairo_clip_intersect_clip_path_transformed (clip, + clip_path->prev, + m); - if (box.p1.x > _cairo_fixed_from_int (rect->x) || - box.p1.y > _cairo_fixed_from_int (rect->y) || - box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) || - box.p2.y < _cairo_fixed_from_int (rect->y + rect->height)) - { - return FALSE; + if (_cairo_path_fixed_init_copy (&path, &clip_path->path)) + return _cairo_clip_set_all_clipped (clip); + + _cairo_path_fixed_transform (&path, m); + + clip = _cairo_clip_intersect_path (clip, + &path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias); + _cairo_path_fixed_fini (&path); + + return clip; +} + +cairo_clip_t * +_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m) +{ + cairo_clip_t *copy; + + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return clip; + + if (_cairo_matrix_is_translation (m)) + return _cairo_clip_translate (clip, m->x0, m->y0); + + copy = _cairo_clip_create (); + + if (clip->num_boxes) { + cairo_path_fixed_t path; + cairo_boxes_t boxes; + + _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes); + _cairo_path_fixed_init_from_boxes (&path, &boxes); + _cairo_path_fixed_transform (&path, m); + + copy = _cairo_clip_intersect_path (copy, &path, + CAIRO_FILL_RULE_WINDING, + 0.1, + CAIRO_ANTIALIAS_DEFAULT); + + _cairo_path_fixed_fini (&path); + } + + if (clip->path) + copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m); + + _cairo_clip_destroy (clip); + return copy; +} + +cairo_clip_t * +_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty) +{ + cairo_clip_t *copy; + int fx, fy, i; + + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return (cairo_clip_t *)clip; + + if (tx == 0 && ty == 0) + return _cairo_clip_copy (clip); + + copy = _cairo_clip_create (); + if (copy == NULL) + return _cairo_clip_set_all_clipped (copy); + + fx = _cairo_fixed_from_int (tx); + fy = _cairo_fixed_from_int (ty); + + if (clip->num_boxes) { + if (clip->num_boxes == 1) { + copy->boxes = ©->embedded_box; + } else { + copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); + if (unlikely (copy->boxes == NULL)) + return _cairo_clip_set_all_clipped (copy); } - } while ((clip_path = clip_path->prev) != NULL); - return TRUE; + for (i = 0; i < clip->num_boxes; i++) { + copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx; + copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx; + copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy; + copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy; + } + copy->num_boxes = clip->num_boxes; + } + + copy->extents = clip->extents; + copy->extents.x += tx; + copy->extents.y += ty; + + if (clip->path == NULL) + return copy; + + return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy); } cairo_bool_t -_cairo_clip_contains_extents (cairo_clip_t *clip, +_cairo_clip_contains_extents (const cairo_clip_t *clip, const cairo_composite_rectangles_t *extents) { const cairo_rectangle_int_t *rect; - if (clip == NULL) - return FALSE; - rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; return _cairo_clip_contains_rectangle (clip, rect); } void -_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip) +_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip) { - cairo_clip_path_t *clip_path; + int i; if (clip == NULL) { fprintf (stream, "no clip\n"); return; } - if (clip->all_clipped) { + if (_cairo_clip_is_all_clipped (clip)) { fprintf (stream, "clip: all-clipped\n"); return; } - if (clip->path == NULL) { - fprintf (stream, "clip: empty\n"); - return; + fprintf (stream, "clip:\n"); + fprintf (stream, " extents: (%d, %d) x (%d, %d), is-region? %d", + clip->extents.x, clip->extents.y, + clip->extents.width, clip->extents.height, + clip->is_region); + + fprintf (stream, " num_boxes = %d\n", clip->num_boxes); + for (i = 0; i < clip->num_boxes; i++) { + fprintf (stream, " [%d] = (%f, %f), (%f, %f)\n", i, + _cairo_fixed_to_double (clip->boxes[i].p1.x), + _cairo_fixed_to_double (clip->boxes[i].p1.y), + _cairo_fixed_to_double (clip->boxes[i].p2.x), + _cairo_fixed_to_double (clip->boxes[i].p2.y)); } - fprintf (stream, "clip:\n"); - - clip_path = clip->path; - do { - fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ", - clip_path->region == NULL ? "no" : "yes", - clip_path->surface == NULL ? "no" : "yes", - clip_path->antialias, - clip_path->tolerance, - clip_path->fill_rule); - _cairo_debug_print_path (stream, &clip_path->path); - fprintf (stream, "\n"); - } while ((clip_path = clip_path->prev) != NULL); + if (clip->path) { + cairo_clip_path_t *clip_path = clip->path; + do { + fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ", + clip_path->antialias, + clip_path->tolerance, + clip_path->fill_rule); + _cairo_debug_print_path (stream, &clip_path->path); + fprintf (stream, "\n"); + } while ((clip_path = clip_path->prev) != NULL); + } } -cairo_surface_t * -_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty) -{ - /* XXX is_clear -> all_clipped */ - assert (clip->path != NULL); - return _cairo_clip_path_get_surface (clip->path, target, tx, ty); -} - -cairo_status_t -_cairo_clip_combine_with_surface (cairo_clip_t *clip, - cairo_surface_t *dst, - int dst_x, int dst_y) -{ - cairo_clip_path_t *clip_path = clip->path; - cairo_bool_t need_translate; - cairo_status_t status; - - assert (clip_path != NULL); - - need_translate = dst_x | dst_y; - do { - if (clip_path->surface != NULL && - clip_path->surface->backend == dst->backend) - { - cairo_surface_pattern_t pattern; - - _cairo_pattern_init_for_surface (&pattern, clip_path->surface); - cairo_matrix_init_translate (&pattern.base.matrix, - dst_x - clip_path->extents.x, - dst_y - clip_path->extents.y); - pattern.base.filter = CAIRO_FILTER_NEAREST; - status = _cairo_surface_paint (dst, - CAIRO_OPERATOR_IN, - &pattern.base, - NULL); - - _cairo_pattern_fini (&pattern.base); - - return status; - } - - if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX && - clip_path->path.maybe_fill_region) - { - continue; - } - - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (-dst_x), - _cairo_fixed_from_int (-dst_y)); - } - status = _cairo_surface_fill (dst, - CAIRO_OPERATOR_IN, - &_cairo_pattern_white.base, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - if (need_translate) { - _cairo_path_fixed_translate (&clip_path->path, - _cairo_fixed_from_int (dst_x), - _cairo_fixed_from_int (dst_y)); - } - - if (unlikely (status)) - return status; - } while ((clip_path = clip_path->prev) != NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 }; - const cairo_rectangle_int_t * _cairo_clip_get_extents (const cairo_clip_t *clip) { - if (clip->all_clipped) - return &_cairo_empty_rectangle_int; + if (clip == NULL) + return &_cairo_unbounded_rectangle; - if (clip->path == NULL) - return NULL; + if (_cairo_clip_is_all_clipped (clip)) + return &_cairo_empty_rectangle; - return &clip->path->extents; -} - -void -_cairo_clip_drop_cache (cairo_clip_t *clip) -{ - cairo_clip_path_t *clip_path; - - if (clip->path == NULL) - return; - - clip_path = clip->path; - do { - if (clip_path->region != NULL) { - cairo_region_destroy (clip_path->region); - clip_path->region = NULL; - } - - if (clip_path->surface != NULL) { - cairo_surface_destroy (clip_path->surface); - clip_path->surface = NULL; - } - - clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION; - } while ((clip_path = clip_path->prev) != NULL); + return &clip->extents; } const cairo_rectangle_list_t _cairo_rectangles_nil = @@ -1337,145 +725,7 @@ _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate, return is_tight; } -cairo_int_status_t -_cairo_clip_get_region (cairo_clip_t *clip, - cairo_region_t **region) -{ - cairo_int_status_t status; - - if (clip->all_clipped) - goto CLIPPED; - - assert (clip->path != NULL); - - status = _cairo_clip_path_to_region (clip->path); - if (status) - return status; - - if (cairo_region_is_empty (clip->path->region)) { - _cairo_clip_set_all_clipped (clip); - goto CLIPPED; - } - - if (region) - *region = clip->path->region; - return CAIRO_STATUS_SUCCESS; - - CLIPPED: - if (region) - *region = NULL; - return CAIRO_INT_STATUS_NOTHING_TO_DO; -} - -cairo_int_status_t -_cairo_clip_get_boxes (cairo_clip_t *clip, - cairo_box_t **boxes, - int *count) -{ - cairo_int_status_t status; - - if (clip->all_clipped) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - assert (clip->path != NULL); - - status = _cairo_clip_path_to_boxes (clip->path, boxes, count); - if (status) - return status; - - if (*count == 0) { - _cairo_clip_set_all_clipped (clip); - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -box_is_aligned (const cairo_box_t *box) -{ - return - _cairo_fixed_is_integer (box->p1.x) && - _cairo_fixed_is_integer (box->p1.y) && - _cairo_fixed_is_integer (box->p2.x) && - _cairo_fixed_is_integer (box->p2.y); -} - -static void -intersect_with_boxes (cairo_composite_rectangles_t *extents, - cairo_box_t *boxes, - int num_boxes) -{ - cairo_rectangle_int_t rect; - cairo_box_t box; - cairo_bool_t is_empty; - - box.p1.x = box.p1.y = INT_MIN; - box.p2.x = box.p2.y = INT_MAX; - while (num_boxes--) { - if (boxes->p1.x < box.p1.x) - box.p1.x = boxes->p1.x; - if (boxes->p1.y < box.p1.y) - box.p1.y = boxes->p1.y; - - if (boxes->p2.x > box.p2.x) - box.p2.x = boxes->p2.x; - if (boxes->p2.y > box.p2.y) - box.p2.y = boxes->p2.y; - } - - _cairo_box_round_to_rectangle (&box, &rect); - is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect); - is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect); -} - -cairo_status_t -_cairo_clip_to_boxes (cairo_clip_t **clip, - cairo_composite_rectangles_t *extents, - cairo_box_t **boxes, - int *num_boxes) -{ - cairo_status_t status; - const cairo_rectangle_int_t *rect; - - rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; - - if (*clip == NULL) - goto EXTENTS; - - status = _cairo_clip_rectangle (*clip, rect); - if (unlikely (status)) - return status; - - status = _cairo_clip_get_boxes (*clip, boxes, num_boxes); - switch ((int) status) { - case CAIRO_STATUS_SUCCESS: - intersect_with_boxes (extents, *boxes, *num_boxes); - if (rect->width == 0 || rect->height == 0 || - extents->is_bounded || - (*num_boxes == 1 && box_is_aligned (*boxes))) - { - *clip = NULL; - } - goto DONE; - - case CAIRO_INT_STATUS_UNSUPPORTED: - goto EXTENTS; - - default: - return status; - } - - EXTENTS: - status = CAIRO_STATUS_SUCCESS; - _cairo_box_from_rectangle (&(*boxes)[0], rect); - *num_boxes = 1; - DONE: - return status; -} - - -static cairo_rectangle_list_t * +cairo_rectangle_list_t * _cairo_rectangle_list_create_in_error (cairo_status_t status) { cairo_rectangle_list_t *list; @@ -1485,9 +735,9 @@ _cairo_rectangle_list_create_in_error (cairo_status_t status) if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; - list = malloc (sizeof (*list)); + list = _cairo_malloc (sizeof (*list)); if (unlikely (list == NULL)) { - _cairo_error_throw (status); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; } @@ -1506,24 +756,21 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) cairo_rectangle_list_t *list; cairo_rectangle_t *rectangles = NULL; cairo_region_t *region = NULL; - cairo_int_status_t status; int n_rects = 0; int i; - if (clip->all_clipped) - goto DONE; - - if (!clip->path) + if (clip == NULL) return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - status = _cairo_clip_get_region (clip, ®ion); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + if (_cairo_clip_is_all_clipped (clip)) goto DONE; - } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + + if (! _cairo_clip_is_region (clip)) return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); - } else if (unlikely (status)) { - return ERROR_LIST (status); - } + + region = _cairo_clip_get_region (clip); + if (region == NULL) + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); n_rects = cairo_region_num_rectangles (region); if (n_rects) { @@ -1548,7 +795,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) } DONE: - list = malloc (sizeof (cairo_rectangle_list_t)); + list = _cairo_malloc (sizeof (cairo_rectangle_list_t)); if (unlikely (list == NULL)) { free (rectangles); return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); @@ -1564,7 +811,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) /** * cairo_rectangle_list_destroy: - * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles() + * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list() * * Unconditionally frees @rectangle_list and all associated * references. After this call, the @rectangle_list pointer must not @@ -1587,4 +834,5 @@ void _cairo_clip_reset_static_data (void) { _freed_pool_reset (&clip_path_pool); + _freed_pool_reset (&clip_pool); } diff --git a/gfx/cairo/cairo/src/cairo-cogl-gradient-private.h b/gfx/cairo/cairo/src/cairo-cogl-gradient-private.h new file mode 100644 index 000000000000..9367044f3f25 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cogl-gradient-private.h @@ -0,0 +1,90 @@ +/* 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.og/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): + * Robert Bragg + */ + +#ifndef CAIRO_COGL_GRADIENT_PRIVATE_H +#define CAIRO_COGL_GRADIENT_PRIVATE_H + +#include "cairoint.h" +#include "cairo-pattern-private.h" + +#include + +#define CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE (1024 * 1024) + +typedef enum _cairo_cogl_gradient_compatibility { + CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD = 1<<0, + CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT = 1<<1, + CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT = 1<<2, + CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE = 1<<3 +} cairo_cogl_gradient_compatibility_t; +#define CAIRO_COGL_GRADIENT_CAN_EXTEND_ALL (CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD |\ + CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT|\ + CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT|\ + CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE) + +typedef struct _cairo_cogl_linear_texture_entry { + cairo_cogl_gradient_compatibility_t compatibility; + CoglTexture *texture; + float translate_x; + float scale_x; +} cairo_cogl_linear_texture_entry_t; + +typedef struct _cairo_cogl_linear_gradient { + cairo_cache_entry_t cache_entry; + cairo_reference_count_t ref_count; + GList *textures; + int n_stops; + const cairo_gradient_stop_t *stops; + cairo_gradient_stop_t stops_embedded[1]; +} cairo_cogl_linear_gradient_t; + +cairo_int_status_t +_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *context, + cairo_extend_t extend_mode, + int n_stops, + const cairo_gradient_stop_t *stops, + const cairo_bool_t need_mirrored_gradient, + cairo_cogl_linear_gradient_t **gradient_out); + +cairo_cogl_linear_texture_entry_t * +_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient, + cairo_extend_t extend_mode); + +cairo_cogl_linear_gradient_t * +_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient); + +void +_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient); + +cairo_bool_t +_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b); + +#endif /* CAIRO_COGL_GRADIENT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-cogl-gradient.c b/gfx/cairo/cairo/src/cairo-cogl-gradient.c new file mode 100644 index 000000000000..cdb4cae82dd4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cogl-gradient.c @@ -0,0 +1,678 @@ +/* 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.og/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): + * Robert Bragg + */ +//#include "cairoint.h" + +#include "cairo-cogl-private.h" +#include "cairo-cogl-gradient-private.h" +#include "cairo-image-surface-private.h" + +#include +#include + +//#define DUMP_GRADIENTS_TO_PNG + +static unsigned long +_cairo_cogl_linear_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_cogl_linear_gradient_t * +_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t *ctx, + unsigned long hash, + unsigned int n_stops, + const cairo_gradient_stop_t *stops) +{ + cairo_cogl_linear_gradient_t lookup; + + lookup.cache_entry.hash = hash, + lookup.n_stops = n_stops; + lookup.stops = stops; + + return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry); +} + +cairo_bool_t +_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b) +{ + const cairo_cogl_linear_gradient_t *a = key_a; + const cairo_cogl_linear_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_cogl_linear_gradient_t * +_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); + + _cairo_reference_count_inc (&gradient->ref_count); + + return gradient; +} + +void +_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient) +{ + GList *l; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&gradient->ref_count)) + return; + + for (l = gradient->textures; l; l = l->next) { + cairo_cogl_linear_texture_entry_t *entry = l->data; + cogl_object_unref (entry->texture); + free (entry); + } + g_list_free (gradient->textures); + + free (gradient); +} + +static int +_cairo_cogl_util_next_p2 (int a) +{ + int rval = 1; + + while (rval < a) + rval <<= 1; + + return rval; +} + +static float +get_max_color_component_range (const cairo_color_stop_t *color0, + const cairo_color_stop_t *color1) +{ + float range; + float max = 0; + + range = fabs (color0->red - color1->red); + max = MAX (range, max); + range = fabs (color0->green - color1->green); + max = MAX (range, max); + range = fabs (color0->blue - color1->blue); + max = MAX (range, max); + range = fabs (color0->alpha - color1->alpha); + max = MAX (range, max); + + return max; +} + +static int +_cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t extend, + unsigned int n_stops, + const cairo_gradient_stop_t *stops) +{ + unsigned int n; + float max_texels_per_unit_offset = 0; + float total_offset_range; + + /* Find the stop pair demanding the most precision because we are + * interpolating the largest color-component range. + * + * From that we can define the relative sizes of all the other + * stop pairs within our texture and thus the overall size. + * + * To determine the maximum number of texels for a given gap we + * look at the range of colors we are expected to interpolate (so + * long as the stop offsets are not degenerate) and we simply + * assume we want one texel for each unique color value possible + * for a one byte-per-component representation. + * XXX: maybe this is overkill and just allowing 128 levels + * instead of 256 would be enough and then we'd rely on the + * bilinear filtering to give the full range. + * + * XXX: potentially we could try and map offsets to pixels to come + * up with a more precise mapping, but we are aiming to cache + * the gradients so we can't make assumptions about how it will be + * scaled in the future. + */ + for (n = 1; n < n_stops; n++) { + float color_range; + float offset_range; + float texels; + float texels_per_unit_offset; + + /* note: degenerate stops don't need to be represented in the + * texture but we want to be sure that solid gaps get at least + * one texel and all other gaps get at least 2 texels. + */ + + if (stops[n].offset == stops[n-1].offset) + continue; + + color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color); + if (color_range == 0) + texels = 1; + else + texels = MAX (2, 256.0f * color_range); + + /* So how many texels would we need to map over the full [0,1] + * gradient range so this gap would have enough texels? ... */ + offset_range = stops[n].offset - stops[n - 1].offset; + texels_per_unit_offset = texels / offset_range; + + if (texels_per_unit_offset > max_texels_per_unit_offset) + max_texels_per_unit_offset = texels_per_unit_offset; + } + + total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset); + return max_texels_per_unit_offset * total_offset_range; +} + +/* Aim to create gradient textures without an alpha component so we can avoid + * needing to use blending... */ +static CoglTextureComponents +_cairo_cogl_linear_gradient_components_for_stops (cairo_extend_t extend, + unsigned int n_stops, + const cairo_gradient_stop_t *stops) +{ + unsigned int n; + + /* We have to add extra transparent texels to the end of the gradient to + * handle CAIRO_EXTEND_NONE... */ + if (extend == CAIRO_EXTEND_NONE) + return COGL_TEXTURE_COMPONENTS_RGBA; + + for (n = 1; n < n_stops; n++) { + if (stops[n].color.alpha != 1.0) + return COGL_TEXTURE_COMPONENTS_RGBA; + } + + return COGL_TEXTURE_COMPONENTS_RGBA; +} + +static cairo_cogl_gradient_compatibility_t +_cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode) +{ + switch (extend_mode) + { + case CAIRO_EXTEND_NONE: + return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE; + case CAIRO_EXTEND_PAD: + return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD; + case CAIRO_EXTEND_REPEAT: + return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT; + case CAIRO_EXTEND_REFLECT: + return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT; + } + + assert (0); /* not reached */ + return CAIRO_EXTEND_NONE; +} + +cairo_cogl_linear_texture_entry_t * +_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient, + cairo_extend_t extend_mode) +{ + GList *l; + cairo_cogl_gradient_compatibility_t compatibility = + _cairo_cogl_compatibility_from_extend_mode (extend_mode); + for (l = gradient->textures; l; l = l->next) { + cairo_cogl_linear_texture_entry_t *entry = l->data; + if (entry->compatibility & compatibility) + return entry; + } + return NULL; +} + +static void +color_stop_lerp (const cairo_color_stop_t *c0, + const cairo_color_stop_t *c1, + float factor, + cairo_color_stop_t *dest) +{ + /* NB: we always ignore the short members in this file so we don't need to + * worry about initializing them here. */ + dest->red = c0->red * (1.0f-factor) + c1->red * factor; + dest->green = c0->green * (1.0f-factor) + c1->green * factor; + dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor; + dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor; +} + +static size_t +_cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient) +{ + GList *l; + size_t size = 0; + for (l = gradient->textures; l; l = l->next) { + cairo_cogl_linear_texture_entry_t *entry = l->data; + size += cogl_texture_get_width (entry->texture) * 4; + } + return size; +} + +static void +emit_stop (CoglVertexP2C4 **position, + float left, + float right, + const cairo_color_stop_t *left_color, + const cairo_color_stop_t *right_color) +{ + CoglVertexP2C4 *p = *position; + + guint8 lr = left_color->red * 255; + guint8 lg = left_color->green * 255; + guint8 lb = left_color->blue * 255; + guint8 la = left_color->alpha * 255; + + guint8 rr = right_color->red * 255; + guint8 rg = right_color->green * 255; + guint8 rb = right_color->blue * 255; + guint8 ra = right_color->alpha * 255; + + p[0].x = left; + p[0].y = 0; + p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la; + p[1].x = left; + p[1].y = 1; + p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la; + p[2].x = right; + p[2].y = 1; + p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra; + + p[3].x = left; + p[3].y = 0; + p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la; + p[4].x = right; + p[4].y = 1; + p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra; + p[5].x = right; + p[5].y = 0; + p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra; + + *position = &p[6]; +} + +#ifdef DUMP_GRADIENTS_TO_PNG +static void +dump_gradient_to_png (CoglTexture *texture) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *) + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + cogl_texture_get_width (texture), + cogl_texture_get_height (texture)); + CoglPixelFormat format; + static int gradient_id = 0; + char *gradient_name; + + if (image->base.status) + return; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + format = COGL_PIXEL_FORMAT_BGRA_8888_PRE; +#else + format = COGL_PIXEL_FORMAT_ARGB_8888_PRE; +#endif + cogl_texture_get_data (texture, + format, + 0, + image->data); + gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++); + g_print ("writing gradient: %s\n", gradient_name); + cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name); + g_free (gradient_name); +} +#endif + +cairo_int_status_t +_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device, + cairo_extend_t extend_mode, + int n_stops, + const cairo_gradient_stop_t *stops, + const cairo_bool_t need_mirrored_gradient, + cairo_cogl_linear_gradient_t **gradient_out) +{ + unsigned long hash; + cairo_cogl_linear_gradient_t *gradient; + cairo_cogl_linear_texture_entry_t *entry; + cairo_gradient_stop_t *internal_stops; + int stop_offset; + int n_internal_stops; + int n; + cairo_cogl_gradient_compatibility_t compatibilities; + int width; + int tex_width; + int left_padding = 0; + cairo_color_stop_t left_padding_color; + int right_padding = 0; + cairo_color_stop_t right_padding_color; + CoglTextureComponents components; + CoglTexture2D *tex; + int un_padded_width; + CoglFramebuffer *offscreen = NULL; + cairo_int_status_t status; + int n_quads; + int n_vertices; + float prev; + float right; + CoglVertexP2C4 *vertices; + CoglVertexP2C4 *p; + CoglPrimitive *prim; + CoglPipeline *pipeline; + + hash = _cairo_cogl_linear_gradient_hash (n_stops, stops); + + gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops); + if (gradient) { + cairo_cogl_linear_texture_entry_t *entry = + _cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode); + if (entry) { + *gradient_out = _cairo_cogl_linear_gradient_reference (gradient); + return CAIRO_INT_STATUS_SUCCESS; + } + } + + if (!gradient) { + gradient = _cairo_malloc (sizeof (cairo_cogl_linear_gradient_t) + + sizeof (cairo_gradient_stop_t) * (n_stops - 1)); + if (!gradient) + return CAIRO_INT_STATUS_NO_MEMORY; + + CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1); + /* NB: we update the cache_entry size at the end before + * [re]adding it to the cache. */ + gradient->cache_entry.hash = hash; + gradient->textures = NULL; + gradient->n_stops = n_stops; + gradient->stops = gradient->stops_embedded; + memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops); + } else { + _cairo_cogl_linear_gradient_reference (gradient); + } + + entry = _cairo_malloc (sizeof (cairo_cogl_linear_texture_entry_t)); + if (unlikely (!entry)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + compatibilities = _cairo_cogl_compatibility_from_extend_mode (extend_mode); + + n_internal_stops = n_stops; + stop_offset = 0; + + /* We really need stops covering the full [0,1] range for repeat/reflect + * if we want to use sampler REPEAT/MIRROR wrap modes so we may need + * to add some extra stops... */ + if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT) + { + /* If we don't need any extra stops then actually the texture + * will be shareable for repeat and reflect... */ + compatibilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT | + CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT); + + if (stops[0].offset != 0) { + n_internal_stops++; + stop_offset++; + } + + if (stops[n_stops - 1].offset != 1) + n_internal_stops++; + } + + internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t)); + memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops); + + /* cairo_color_stop_t values are all unpremultiplied but we need to + * interpolate premultiplied colors so we premultiply all the double + * components now. (skipping any extra stops added for repeat/reflect) + * + * Another thing to note is that by premultiplying the colors + * early we'll also reduce the range of colors to interpolate + * which can result in smaller gradient textures. + */ + for (n = stop_offset; n < n_stops; n++) { + cairo_color_stop_t *color = &internal_stops[n].color; + color->red *= color->alpha; + color->green *= color->alpha; + color->blue *= color->alpha; + } + + if (n_internal_stops != n_stops) + { + if (extend_mode == CAIRO_EXTEND_REPEAT) { + compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT; + if (stops[0].offset != 0) { + /* what's the wrap-around distance between the user's end-stops? */ + double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset; + internal_stops[0].offset = 0; + color_stop_lerp (&stops[0].color, + &stops[n_stops - 1].color, + stops[0].offset / dx, + &internal_stops[0].color); + } + if (stops[n_stops - 1].offset != 1) { + internal_stops[n_internal_stops - 1].offset = 1; + internal_stops[n_internal_stops - 1].color = internal_stops[0].color; + } + } else if (extend_mode == CAIRO_EXTEND_REFLECT) { + compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT; + if (stops[0].offset != 0) { + internal_stops[0].offset = 0; + internal_stops[0].color = stops[n_stops - 1].color; + } + if (stops[n_stops - 1].offset != 1) { + internal_stops[n_internal_stops - 1].offset = 1; + internal_stops[n_internal_stops - 1].color = stops[0].color; + } + } + } + + stops = internal_stops; + n_stops = n_internal_stops; + + width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops); + + if (extend_mode == CAIRO_EXTEND_PAD) { + + /* Here we need to guarantee that the edge texels of our + * texture correspond to the desired padding color so we + * can use CLAMP_TO_EDGE. + * + * For short stop-gaps and especially for degenerate stops + * it's possible that without special consideration the + * user's end stop colors would not be present in our final + * texture. + * + * To handle this we forcibly add two extra padding texels + * at the edges which extend beyond the [0,1] range of the + * gradient itself and we will later report a translate and + * scale transform to compensate for this. + */ + + /* XXX: If we consider generating a mipmap for our 1d texture + * at some point then we also need to consider how much + * padding to add to be sure lower mipmap levels still have + * the desired edge color (as opposed to a linear blend with + * other colors of the gradient). + */ + + left_padding = 1; + left_padding_color = stops[0].color; + right_padding = 1; + right_padding_color = stops[n_stops - 1].color; + } else if (extend_mode == CAIRO_EXTEND_NONE) { + /* We handle EXTEND_NONE by adding two extra, transparent, texels at + * the ends of the texture and use CLAMP_TO_EDGE. + * + * We add a scale and translate transform so to account for our texels + * extending beyond the [0,1] range. */ + + left_padding = 1; + left_padding_color.red = 0; + left_padding_color.green = 0; + left_padding_color.blue = 0; + left_padding_color.alpha = 0; + right_padding = 1; + right_padding_color = left_padding_color; + } + + /* If we still have stops that don't cover the full [0,1] range + * then we need to define a texture-coordinate scale + translate + * transform to account for that... */ + if (stops[n_stops - 1].offset - stops[0].offset < 1) { + float range = stops[n_stops - 1].offset - stops[0].offset; + entry->scale_x = 1.0 / range; + entry->translate_x = -(stops[0].offset * entry->scale_x); + } + + width += left_padding + right_padding; + + width = _cairo_cogl_util_next_p2 (width); + width = MIN (4096, width); /* lets not go too stupidly big! */ + + if (!device->has_npots) + width = pow (2, ceil (log2 (width))); + + if (need_mirrored_gradient) + tex_width = width * 2; + else + tex_width = width; + + components = _cairo_cogl_linear_gradient_components_for_stops (extend_mode, n_stops, stops); + + do { + tex = cogl_texture_2d_new_with_size (device->cogl_context, + tex_width, 1); + } while (tex == NULL && width >> 1 && tex_width >> 1); + + if (unlikely (!tex)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + cogl_texture_set_components (tex, components); + + entry->texture = tex; + entry->compatibility = compatibilities; + + un_padded_width = width - left_padding - right_padding; + + /* XXX: only when we know the final texture width can we calculate the + * scale and translate factors needed to account for padding... */ + if (un_padded_width != width) + entry->scale_x *= (float)un_padded_width / (float)width; + if (left_padding) + entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding; + + offscreen = cogl_offscreen_new_with_texture (tex); + cogl_framebuffer_orthographic (offscreen, 0, 0, + tex_width, 1, + -1, 100); + cogl_framebuffer_clear4f (offscreen, + COGL_BUFFER_BIT_COLOR, + 0, 0, 0, 0); + + n_quads = n_stops - 1 + !!left_padding + !!right_padding; + n_vertices = 6 * n_quads; + vertices = _cairo_malloc_ab (n_vertices, sizeof (CoglVertexP2C4)); + if (unlikely (!vertices)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + p = vertices; + if (left_padding) + emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color); + prev = (float)left_padding; + for (n = 1; n < n_stops; n++) { + right = (float)left_padding + (float)un_padded_width * stops[n].offset; + emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color); + prev = right; + } + if (right_padding) + emit_stop (&p, prev, width, &right_padding_color, &right_padding_color); + + prim = cogl_primitive_new_p2c4 (device->cogl_context, + COGL_VERTICES_MODE_TRIANGLES, + n_vertices, + vertices); + free (vertices); + pipeline = cogl_pipeline_new (device->cogl_context); + cogl_primitive_draw (prim, offscreen, pipeline); + + if (need_mirrored_gradient) { + /* In order to use a reflected gradient on hardware that + * doesn't have a mirrored repeating texture wrap mode, we + * render two reflected images to a double-length linear + * texture and reflect that */ + CoglMatrix transform; + + cogl_matrix_init_identity (&transform); + cogl_matrix_translate (&transform, tex_width, 0.0f, 0.0f); + cogl_matrix_scale (&transform, -1.0f, 1.0f, 1.0f); + + cogl_framebuffer_transform (offscreen, &transform); + cogl_primitive_draw (prim, offscreen, pipeline); + } + + cogl_object_unref (prim); + cogl_object_unref (pipeline); + + cogl_object_unref (offscreen); + offscreen = NULL; + + gradient->textures = g_list_prepend (gradient->textures, entry); + gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient); + +#ifdef DUMP_GRADIENTS_TO_PNG + dump_gradient_to_png (tex); +#endif + +#warning "FIXME:" + /* XXX: it seems the documentation of _cairo_cache_insert isn't true - it + * doesn't handle re-adding the same entry gracefully - the cache will + * just keep on growing and then it will start randomly evicting things + * pointlessly */ + /* we ignore errors here and just return an uncached gradient */ + if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry))) + _cairo_cogl_linear_gradient_reference (gradient); + + *gradient_out = gradient; + return CAIRO_INT_STATUS_SUCCESS; + +BAIL: + free (entry); + if (gradient) + _cairo_cogl_linear_gradient_destroy (gradient); + if (offscreen) + cogl_object_unref (offscreen); + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-cogl-private.h b/gfx/cairo/cairo/src/cairo-cogl-private.h new file mode 100644 index 000000000000..b0be2661eb79 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cogl-private.h @@ -0,0 +1,164 @@ +/* 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.og/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): + * Robert Bragg + */ + +#ifndef CAIRO_COGL_PRIVATE_H +#define CAIRO_COGL_PRIVATE_H + +#include "cairo-device-private.h" +#include "cairo-cache-private.h" +#include "cairo-backend-private.h" +#include "cairo-default-context-private.h" +#include "cairo-surface-private.h" +#include "cairo-freelist-private.h" + +#include + +typedef enum _cairo_cogl_template_type { + /* solid source */ + CAIRO_COGL_TEMPLATE_TYPE_SOLID, + /* solid source with solid mask */ + CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID, + /* solid source with texture mask */ + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID, + /* texture source */ + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE, + /* texture source with solid mask */ + CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE, + /* texture source with texture mask */ + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE, + /* texture source with source alpha ignored */ + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA, + /* texture source with solid mask with source alpha ignored */ + CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA, + /* texture source with texture mask with source alpha ignored */ + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA, + CAIRO_COGL_TEMPLATE_TYPE_COUNT +} cairo_cogl_template_type; + +typedef struct _cairo_cogl_device { + cairo_device_t base; + + CoglContext *cogl_context; + + cairo_bool_t has_npots; + cairo_bool_t has_mirrored_repeat; + + CoglAttributeBuffer *buffer_stack; + size_t buffer_stack_size; + size_t buffer_stack_offset; + guint8 *buffer_stack_pointer; + + /* This is a limited set of templates because we don't support the + * full range of operators that cairo has. The CAIRO_OPERATOR_ADD + * is the operator enum with the highest value that we support so + * we cap the size of the array by that. + * + * For each operator, we have a template for when we have a solid + * source and a non-solid source. For each of those, we also have + * additional templates for when we have a solid mask or a + * non-solid mask, for a total of six templates per operator. + * + * The templates are set to null at device creation time and only + * actually created on their first use. + */ + CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT]; + + /* Caches 1d linear gradient textures */ + cairo_cache_t linear_cache; + + cairo_cache_t path_fill_prim_cache; + cairo_cache_t path_stroke_prim_cache; + + cairo_freelist_t path_fill_meta_freelist; + cairo_freelist_t path_stroke_meta_freelist; +} cairo_cogl_device_t; + +typedef struct _cairo_cogl_clip_primitives { + cairo_t *clip; + CoglPrimitive **primitives; +} cairo_cogl_clip_primitives_t; + +typedef struct _cairo_cogl_surface { + cairo_surface_t base; + + /* We currently have 3 basic kinds of Cogl surfaces: + * 1) A light surface simply wrapping a CoglTexture + * 2) A CoglOffscreen framebuffer that implicitly also wraps a CoglTexture + * 3) A CoglOnscreen framebuffer which could potentially be mapped to + * a CoglTexture (e.g. via tfp on X11) but we don't currently do + * that. + */ + + CoglTexture *texture; + CoglFramebuffer *framebuffer; + + int width; + int height; + + /* Is this a snapshot used for mirrored repeating on hardware which + * doesn't have it, consisting of four reflected images? */ + cairo_bool_t is_mirrored_snapshot; + + GQueue *journal; + + cairo_clip_t *last_clip; + + /* A small fifo of recently used cairo_clip_ts paired with CoglPrimitives + * that can be used to mask the stencil buffer. */ + GList *clips_fifo; + + int n_clip_updates_per_frame; + + /* Since the surface backend drawing operator functions don't get + * passed the current cairo_t context we don't have a good way + * to get our user-coordinates path into our surface_fill function. + * + * For now we use our _cairo_cogl_context_fill() wrapper to set this + * side band data on the surface... + */ + cairo_path_fixed_t *user_path; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + cairo_bool_t path_is_rectangle; + double path_rectangle_x; + double path_rectangle_y; + double path_rectangle_width; + double path_rectangle_height; +} cairo_cogl_surface_t; + +cairo_status_t +_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y, + cairo_fixed_t width, + cairo_fixed_t height); + +#endif /* CAIRO_COGL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-cogl-surface.c b/gfx/cairo/cairo/src/cairo-cogl-surface.c new file mode 100644 index 000000000000..36e5f77d8b9a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cogl-surface.c @@ -0,0 +1,4106 @@ +/* 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.og/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): + * Robert Bragg + */ +#include "cairoint.h" + +#include "cairo-cache-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-recording-surface-inline.h" +#include "cairo-fixed-private.h" +#include "cairo-device-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-cogl-private.h" +#include "cairo-cogl-gradient-private.h" +#include "cairo-arc-private.h" +#include "cairo-traps-private.h" +#include "cairo-surface-subsurface-inline.h" +#include "cairo-surface-fallback-private.h" +#include "cairo-surface-offset-private.h" + +#include "cairo-cogl.h" + +#include +#include + +#define CAIRO_COGL_DEBUG 0 +//#define DISABLE_BATCHING +#define MAX_JOURNAL_SIZE 100 + +#if CAIRO_COGL_DEBUG && __GNUC__ +#define UNSUPPORTED(reason) ({ \ + g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \ + CAIRO_INT_STATUS_UNSUPPORTED; \ +}) +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +#define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024) + +typedef struct _cairo_cogl_texture_attributes { + /* nabbed from cairo_surface_attributes_t... */ + cairo_matrix_t matrix; + cairo_extend_t extend; + cairo_bool_t has_component_alpha; + + CoglPipelineFilter filter; + CoglPipelineWrapMode s_wrap; + CoglPipelineWrapMode t_wrap; +} cairo_cogl_texture_attributes_t; + +typedef struct _cairo_cogl_pipeline { + int n_layers; + cairo_operator_t op; + + /* These are not always the same as the operator's, as sometimes + * this is a pre-render for a different operator */ + cairo_bool_t src_bounded; + cairo_bool_t mask_bounded; + + cairo_bool_t has_src_tex_clip; + cairo_path_fixed_t src_tex_clip; + cairo_bool_t has_mask_tex_clip; + cairo_path_fixed_t mask_tex_clip; + cairo_rectangle_int_t unbounded_extents; + + CoglPipeline *pipeline; +} cairo_cogl_pipeline_t; + +typedef enum _cairo_cogl_journal_entry_type { + CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE, + CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE, + CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP +} cairo_cogl_journal_entry_type_t; + +typedef struct _cairo_cogl_journal_entry { + cairo_cogl_journal_entry_type_t type; +} cairo_cogl_journal_entry_t; + +typedef struct _cairo_cogl_journal_clip_entry { + cairo_cogl_journal_entry_t base; + cairo_clip_t *clip; +} cairo_cogl_journal_clip_entry_t; + +typedef struct _cairo_cogl_journal_rect_entry { + cairo_cogl_journal_entry_t base; + cairo_cogl_pipeline_t *pipeline; + + float x; + float y; + float width; + float height; + cairo_matrix_t ctm; +} cairo_cogl_journal_rect_entry_t; + +typedef struct _cairo_cogl_journal_prim_entry { + cairo_cogl_journal_entry_t base; + cairo_cogl_pipeline_t *pipeline; + + CoglPrimitive *primitive; + cairo_matrix_t transform; +} cairo_cogl_journal_prim_entry_t; + +typedef struct _cairo_cogl_path_fill_meta { + cairo_cache_entry_t base; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + + CoglPrimitive *prim; + + cairo_freelist_t *freelist; +} cairo_cogl_path_fill_meta_t; + +typedef struct _cairo_cogl_path_stroke_meta { + cairo_cache_entry_t base; + cairo_path_fixed_t path; + cairo_stroke_style_t style; + double tolerance; + + CoglPrimitive *prim; + + cairo_freelist_t *freelist; +} cairo_cogl_path_stroke_meta_t; + +static cairo_surface_t * +_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev, + cairo_content_t content, + CoglFramebuffer *framebuffer, + CoglTexture *texture); + +static void +_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface); + +cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend; + +slim_hidden_proto (cairo_cogl_device_create); +slim_hidden_proto (cairo_cogl_surface_create_for_fb); +slim_hidden_proto (cairo_cogl_onscreen_surface_create); +slim_hidden_proto (cairo_cogl_offscreen_surface_create); +slim_hidden_proto (cairo_cogl_surface_get_framebuffer); +slim_hidden_proto (cairo_cogl_surface_get_texture); +slim_hidden_proto (cairo_cogl_surface_end_frame); +slim_hidden_proto (cairo_cogl_surface_synchronize); + +static cairo_cogl_device_t * +to_device (cairo_device_t *device) +{ + return (cairo_cogl_device_t *)device; +} + +/* moves trap points such that they become the actual corners of the trapezoid */ +static void +_sanitize_trap (cairo_trapezoid_t *t) +{ + cairo_trapezoid_t s = *t; + +#define FIX(lr, tb, p) \ + if (t->lr.p.y != t->tb) { \ + t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \ + t->lr.p.y = s.tb; \ + } + FIX (left, top, p1); + FIX (left, bottom, p2); + FIX (right, top, p1); + FIX (right, bottom, p2); +} + +static cairo_status_t +_cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface) +{ + CoglError *error = NULL; + + if (surface->framebuffer) + return CAIRO_STATUS_SUCCESS; + + surface->framebuffer = + cogl_offscreen_new_with_texture (surface->texture); + if (unlikely (!cogl_framebuffer_allocate (surface->framebuffer, + &error))) + { + g_warning ("Could not create framebuffer for surface: %s", + error->message); + cogl_error_free (error); + cogl_object_unref (surface->framebuffer); + surface->framebuffer = NULL; + return CAIRO_STATUS_NO_MEMORY; + } + + cogl_framebuffer_orthographic (surface->framebuffer, 0, 0, + cogl_texture_get_width (surface->texture), + cogl_texture_get_height (surface->texture), + -1, 100); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_cogl_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_cogl_surface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + + return TRUE; +} + +/* Taken from cairo-surface-clipper.c */ +static cairo_status_t +_cairo_path_fixed_add_box (cairo_path_fixed_t *path, + const cairo_box_t *box) +{ + cairo_status_t status; + + status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_close_path (path); +} + +static void +_cairo_cogl_journal_discard (cairo_cogl_surface_t *surface) +{ + GList *l; + + if (!surface->journal) { + assert (surface->last_clip == NULL); + return; + } + + for (l = surface->journal->head; l; l = l->next) { + cairo_cogl_journal_entry_t *entry = l->data; + gsize entry_size; + + switch (entry->type) + { + case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: { + cairo_cogl_journal_clip_entry_t *clip_entry = + (cairo_cogl_journal_clip_entry_t *)entry; + _cairo_clip_destroy (clip_entry->clip); + entry_size = sizeof (cairo_cogl_journal_clip_entry_t); + break; + } + case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: { + cairo_cogl_journal_rect_entry_t *rect_entry = + (cairo_cogl_journal_rect_entry_t *)entry; + cogl_object_unref (rect_entry->pipeline->pipeline); + if (rect_entry->pipeline->has_src_tex_clip) + _cairo_path_fixed_fini (&rect_entry->pipeline->src_tex_clip); + if (rect_entry->pipeline->has_mask_tex_clip) + _cairo_path_fixed_fini (&rect_entry->pipeline->mask_tex_clip); + g_free (rect_entry->pipeline); + entry_size = sizeof (cairo_cogl_journal_rect_entry_t); + break; + } + case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: { + cairo_cogl_journal_prim_entry_t *prim_entry = + (cairo_cogl_journal_prim_entry_t *)entry; + cogl_object_unref (prim_entry->pipeline->pipeline); + if (prim_entry->primitive) + cogl_object_unref (prim_entry->primitive); + if (prim_entry->pipeline->has_src_tex_clip) + _cairo_path_fixed_fini (&prim_entry->pipeline->src_tex_clip); + if (prim_entry->pipeline->has_mask_tex_clip) + _cairo_path_fixed_fini (&prim_entry->pipeline->mask_tex_clip); + g_free (prim_entry->pipeline); + entry_size = sizeof (cairo_cogl_journal_prim_entry_t); + break; + } + default: + assert (0); /* not reached! */ + entry_size = 0; /* avoid compiler warning */ + } + g_slice_free1 (entry_size, entry); + } + + g_queue_clear (surface->journal); + + if (surface->last_clip) { + _cairo_clip_destroy (surface->last_clip); + surface->last_clip = NULL; + } +} + +static void +_cairo_cogl_journal_free (cairo_cogl_surface_t *surface) +{ + _cairo_cogl_journal_discard (surface); + g_queue_free (surface->journal); + surface->journal = NULL; +} + +static void +_cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface, + cairo_cogl_pipeline_t *pipeline, + CoglPrimitive *primitive, + cairo_matrix_t *transform) +{ + cairo_cogl_journal_prim_entry_t *entry; + + if (unlikely (surface->journal == NULL)) + surface->journal = g_queue_new (); + + /* FIXME: Instead of a GList here we should stack allocate the journal + * entries so it would be cheaper to allocate and they can all be freed in + * one go after flushing! */ + entry = g_slice_new (cairo_cogl_journal_prim_entry_t); + entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE; + + entry->pipeline = pipeline; + + entry->primitive = primitive; + if (primitive) + cogl_object_ref (primitive); + + entry->transform = *transform; + + g_queue_push_tail (surface->journal, entry); + +#ifdef DISABLE_BATCHING + _cairo_cogl_journal_flush (surface); +#else + /* To avoid consuming too much memory, flush the journal if it gets + * to a certain size */ + if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE) + _cairo_cogl_journal_flush (surface); +#endif +} + +static void +_cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface, + cairo_cogl_pipeline_t *pipeline, + float x, + float y, + float width, + float height, + cairo_matrix_t *ctm) +{ + cairo_cogl_journal_rect_entry_t *entry; + + if (unlikely (surface->journal == NULL)) + surface->journal = g_queue_new (); + + /* FIXME: Instead of a GList here we should stack allocate the journal + * entries so it would be cheaper to allocate and they can all be freed in + * one go after flushing! */ + entry = g_slice_new (cairo_cogl_journal_rect_entry_t); + entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE; + + entry->pipeline = pipeline; + + entry->x = x; + entry->y = y; + entry->width = width; + entry->height = height; + entry->ctm = *ctm; + + g_queue_push_tail (surface->journal, entry); + +#ifdef DISABLE_BATCHING + _cairo_cogl_journal_flush (surface); +#else + /* To avoid consuming too much memory, flush the journal if it gets + * to a certain size */ + if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE) + _cairo_cogl_journal_flush (surface); +#endif +} + +static void +_cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface, + const cairo_clip_t *clip) +{ + cairo_cogl_journal_clip_entry_t *entry; + + if (unlikely (surface->journal == NULL)) + surface->journal = g_queue_new (); + + /* FIXME: Instead of a GList here we should stack allocate the journal + * entries so it would be cheaper to allocate and they can all be freed in + * one go after flushing! */ + entry = g_slice_new (cairo_cogl_journal_clip_entry_t); + entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP; + entry->clip = _cairo_clip_copy (clip); + + g_queue_push_tail (surface->journal, entry); +} + +static CoglAttributeBuffer * +_cairo_cogl_device_allocate_buffer_space (cairo_cogl_device_t *dev, + size_t size, + size_t *offset, + void **pointer) +{ + /* XXX: In the Cogl journal we found it more efficient to have a pool of + * buffers that we re-cycle but for now we simply throw away our stack + * buffer each time we flush. */ + if (unlikely (dev->buffer_stack && + (dev->buffer_stack_size - dev->buffer_stack_offset) < size)) { + cogl_buffer_unmap (dev->buffer_stack); + cogl_object_unref (dev->buffer_stack); + dev->buffer_stack = NULL; + dev->buffer_stack_size *= 2; + } + + if (unlikely (dev->buffer_stack_size < size)) + dev->buffer_stack_size = size * 2; + + if (unlikely (dev->buffer_stack == NULL)) { + dev->buffer_stack = + cogl_attribute_buffer_new (dev->cogl_context, + dev->buffer_stack_size, + NULL); + dev->buffer_stack_pointer = + cogl_buffer_map (dev->buffer_stack, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD); + dev->buffer_stack_offset = 0; + } + + *pointer = dev->buffer_stack_pointer + dev->buffer_stack_offset; + *offset = dev->buffer_stack_offset; + + dev->buffer_stack_offset += size; + return cogl_object_ref (dev->buffer_stack); +} + + +static CoglAttributeBuffer * +_cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface, + cairo_traps_t *traps, + size_t *offset, + cairo_bool_t one_shot) +{ + CoglAttributeBuffer *buffer; + int n_traps = traps->num_traps; + int i; + CoglVertexP2 *triangles; + cairo_cogl_device_t *dev = to_device(surface->base.device); + + if (one_shot) { + buffer = + _cairo_cogl_device_allocate_buffer_space (dev, + n_traps * sizeof (CoglVertexP2) * 6, + offset, + (void **)&triangles); + if (!buffer) + return NULL; + } else { + buffer = + cogl_attribute_buffer_new (dev->cogl_context, + n_traps * sizeof (CoglVertexP2) * 6, + NULL); + if (!buffer) + return NULL; + triangles = cogl_buffer_map (buffer, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD); + if (!triangles) + return NULL; + *offset = 0; + } + + /* XXX: This is can be very expensive. I'm not sure a.t.m if it's + * predominantly the bandwidth required or the cost of the fixed_to_float + * conversions but either way we should try using an index buffer to + * reduce the amount we upload by 1/3 (offset by allocating and uploading + * indices though) sadly though my experience with the intel mesa drivers + * is that slow paths can easily be hit when starting to use indices. + */ + for (i = 0; i < n_traps; i++) + { + CoglVertexP2 *p = &triangles[i * 6]; + cairo_trapezoid_t *trap = &traps->traps[i]; + + p[0].x = _cairo_fixed_to_double (trap->left.p1.x); + p[0].y = _cairo_fixed_to_double (trap->left.p1.y); + + p[1].x = _cairo_fixed_to_double (trap->left.p2.x); + p[1].y = _cairo_fixed_to_double (trap->left.p2.y); + + p[2].x = _cairo_fixed_to_double (trap->right.p2.x); + p[2].y = _cairo_fixed_to_double (trap->right.p2.y); + + p[3].x = p[0].x; + p[3].y = p[0].y; + + p[4].x = p[2].x; + p[4].y = p[2].y; + + p[5].x = _cairo_fixed_to_double (trap->right.p1.x); + p[5].y = _cairo_fixed_to_double (trap->right.p1.y); + } + + if (!one_shot) + cogl_buffer_unmap (buffer); + + return buffer; +} + +static CoglPrimitive * +_cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface, + cairo_traps_t *traps, + cairo_bool_t one_shot) +{ + int n_traps = traps->num_traps; + size_t offset; + CoglAttributeBuffer *buffer; + CoglPrimitive *prim; + CoglAttribute *attributes[3]; + const char *attrib_names[3] = {"cogl_position_in", + "cogl_tex_coord0_in", + "cogl_tex_coord1_in"}; + int i; + + /* XXX: Ideally we would skip tessellating to traps entirely since + * given their representation, conversion to triangles is quite expensive. + * + * This simplifies the conversion to triangles by making the end points of + * the two side lines actually just correspond to the corners of the + * traps. + */ + for (i = 0; i < n_traps; i++) + _sanitize_trap (&traps->traps[i]); + + buffer = _cairo_cogl_traps_to_triangles_buffer (surface, + traps, + &offset, + one_shot); + + /* We create the largest used number of texture coordinate + * attributes here to ensure that when cached, they can be reused + * with any pipeline that we may associate with a given path. If + * the corresponding layer for a texture coordinates attribute + * doesn't exist, cogl simply ignores it. Since all of the + * attributes are aliasing the same attribute buffer, the overhead + * of specifying them is minimal. */ + for (i = 0; i < 3; i++) { + attributes[i] = cogl_attribute_new (buffer, + attrib_names[i], + sizeof (CoglVertexP2), + offset, + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + } + + /* The attributes will have taken references on the buffer */ + cogl_object_unref (buffer); + + prim = + cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, + n_traps * 6, attributes, 3); + + /* The primitive will now keep the attributes alive... */ + for (i = 0; i < 3; i++) + cogl_object_unref (attributes[i]); + + return prim; +} + +/* In order to facilitate path caching, we transform the input path + * into a form that will make all translations and rotations of a given + * path identical, thereby allowing them to be identified with + * conventional path hashing and equivalence functions. A + * transformation matrix is also output so that the path can be + * transformed back into its original form during rendering. */ +static cairo_int_status_t +_cairo_cogl_get_untransformed_path (cairo_path_fixed_t *copy, + const cairo_path_fixed_t *orig, + cairo_matrix_t *transform_out) +{ + cairo_matrix_t transform; + cairo_int_status_t status; + + if (orig->buf.base.num_points < 1) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + status = _cairo_path_fixed_init_copy (copy, orig); + if (unlikely (status)) + return status; + + /* First the path is translated so that its first point lies on the + * origin. */ + cairo_matrix_init_translate (&transform, + -_cairo_fixed_to_double(orig->buf.points[0].x), + -_cairo_fixed_to_double(orig->buf.points[0].y)); + + /* Then the path is rotated so that its second point lies on the + * x axis. */ + if (orig->buf.base.num_points > 1) { + double x = _cairo_fixed_to_double(orig->buf.points[1].x) - + _cairo_fixed_to_double(orig->buf.points[0].x); + double y = _cairo_fixed_to_double(orig->buf.points[1].y) - + _cairo_fixed_to_double(orig->buf.points[0].y); + double hyp = sqrt (x * x + y * y); + + transform.xx = x / hyp; + transform.yy = x / hyp; + transform.xy = -y / hyp; + transform.yx = y / hyp; + } + + _cairo_path_fixed_transform (copy, &transform); + + *transform_out = transform; + status = cairo_matrix_invert (transform_out); + if (unlikely (status)) { + _cairo_path_fixed_fini (copy); + return status; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static void +_cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta) +{ + _cairo_path_fixed_fini (&meta->path); + cogl_object_unref (meta->prim); + + _cairo_freelist_free (meta->freelist, meta); +} + +static cairo_bool_t +_cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b) +{ + const cairo_cogl_path_fill_meta_t *meta0 = key_a; + const cairo_cogl_path_fill_meta_t *meta1 = key_b; + + if (meta0->fill_rule != meta1->fill_rule) + return FALSE; + + if (meta0->tolerance != meta1->tolerance) + return FALSE; + + if (!_cairo_path_fixed_equal (&meta0->path, &meta1->path)) + return FALSE; + + return TRUE; +} + +static cairo_int_status_t +_cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_bool_t one_shot, + CoglPrimitive **primitive, + cairo_matrix_t *transform) +{ + cairo_traps_t traps; + cairo_int_status_t status; + cairo_cogl_path_fill_meta_t meta; + cairo_cogl_path_fill_meta_t *acquired_meta; + cairo_cogl_path_fill_meta_t *insert_meta = NULL; + cairo_cogl_device_t *dev = to_device (surface->base.device); + unsigned long hash; + + *primitive = NULL; + + status = _cairo_cogl_get_untransformed_path (&meta.path, + path, + transform); + if (unlikely (status)) + return status; + + hash = _cairo_path_fixed_hash (&meta.path); + hash = _cairo_hash_bytes (hash, &fill_rule, sizeof (fill_rule)); + hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance)); + meta.base.hash = hash; + meta.tolerance = tolerance; + meta.fill_rule = fill_rule; + + acquired_meta = _cairo_cache_lookup (&dev->path_fill_prim_cache, + &meta.base); + + if (acquired_meta) { + // g_print ("fill cache hit"); + *primitive = cogl_object_ref (acquired_meta->prim); + _cairo_path_fixed_fini (&meta.path); + return CAIRO_STATUS_SUCCESS; + } + + _cairo_traps_init (&traps); + status = _cairo_path_fixed_fill_to_traps (&meta.path, + fill_rule, + tolerance, + &traps); + if (unlikely (status)) + goto BAIL; + + if (traps.num_traps == 0) { + status = CAIRO_INT_STATUS_NOTHING_TO_DO; + goto BAIL; + } + + *primitive = _cairo_cogl_traps_to_composite_prim (surface, + &traps, + one_shot); + if (unlikely (!*primitive)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + insert_meta = + _cairo_freelist_alloc (&dev->path_fill_meta_freelist); + if (unlikely (!insert_meta)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + insert_meta->base.hash = meta.base.hash; + insert_meta->base.size = + traps.num_traps * sizeof (CoglVertexP2) * 6; + insert_meta->tolerance = tolerance; + insert_meta->fill_rule = fill_rule; + insert_meta->prim = cogl_object_ref (*primitive); + insert_meta->freelist = &dev->path_fill_meta_freelist; + + status = _cairo_path_fixed_init_copy (&insert_meta->path, + &meta.path); + if (unlikely (status)) + goto BAIL; + + if (unlikely (_cairo_cache_insert (&dev->path_fill_prim_cache, + &insert_meta->base))) + { + g_warning ("Fill primitive cache insertion unsuccessful"); + goto BAIL; + } + + _cairo_path_fixed_fini (&meta.path); + _cairo_traps_fini (&traps); + + return status; + +BAIL: + if (*primitive) { + cogl_object_unref (*primitive); + *primitive = NULL; + } + if (insert_meta) + _cairo_cogl_path_fill_meta_destroy (insert_meta); + _cairo_path_fixed_fini (&meta.path); + _cairo_traps_fini (&traps); + + return status; +} + +static cairo_bool_t +_cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a, + const cairo_stroke_style_t *b) +{ + if (a->line_width == b->line_width && + a->line_cap == b->line_cap && + a->line_join == b->line_join && + a->miter_limit == b->miter_limit && + a->num_dashes == b->num_dashes && + a->dash_offset == b->dash_offset) + { + unsigned int i; + for (i = 0; i < a->num_dashes; i++) { + if (a->dash[i] != b->dash[i]) + return FALSE; + } + } + return TRUE; +} + +static cairo_bool_t +_cairo_cogl_path_stroke_meta_equal (const void *key_a, + const void *key_b) +{ + const cairo_cogl_path_stroke_meta_t *meta0 = key_a; + const cairo_cogl_path_stroke_meta_t *meta1 = key_b; + + if (meta0->tolerance != meta1->tolerance) + return FALSE; + + if (!_cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style)) + return FALSE; + + if (!_cairo_path_fixed_equal (&meta0->path, &meta1->path)) + return FALSE; + + return TRUE; +} + +static void +_cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta) +{ + _cairo_stroke_style_fini (&meta->style); + _cairo_path_fixed_fini (&meta->path); + cogl_object_unref (meta->prim); + + _cairo_freelist_free (meta->freelist, meta); +} + +static unsigned long +_cairo_cogl_stroke_style_hash (unsigned long hash, + const cairo_stroke_style_t *style) +{ + unsigned int i; + hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width)); + hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap)); + hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join)); + hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit)); + hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes)); + hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset)); + for (i = 0; i < style->num_dashes; i++) + hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double)); + return hash; +} + +static cairo_int_status_t +_cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + double tolerance, + cairo_bool_t one_shot, + CoglPrimitive **primitive, + cairo_matrix_t *transform) +{ + cairo_traps_t traps; + cairo_int_status_t status; + cairo_cogl_path_stroke_meta_t meta; + cairo_cogl_path_stroke_meta_t *acquired_meta; + cairo_cogl_path_stroke_meta_t *insert_meta = NULL; + cairo_matrix_t identity; + cairo_cogl_device_t *dev = to_device (surface->base.device); + unsigned long hash; + + *primitive = NULL; + + status = _cairo_cogl_get_untransformed_path (&meta.path, + path, + transform); + if (unlikely (status)) + return status; + + hash = _cairo_path_fixed_hash (&meta.path); + hash = _cairo_cogl_stroke_style_hash (hash, style); + hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance)); + meta.base.hash = hash; + meta.tolerance = tolerance; + + status = _cairo_stroke_style_init_copy (&meta.style, style); + if (unlikely (status)) { + _cairo_path_fixed_fini (&meta.path); + return status; + } + + acquired_meta = _cairo_cache_lookup (&dev->path_stroke_prim_cache, + &meta.base); + + if (acquired_meta) { + // g_print ("stroke cache hit"); + *primitive = cogl_object_ref (acquired_meta->prim); + _cairo_path_fixed_fini (&meta.path); + return CAIRO_STATUS_SUCCESS; + } + + _cairo_traps_init (&traps); + + cairo_matrix_init_identity (&identity); + status = _cairo_path_fixed_stroke_polygon_to_traps (&meta.path, + style, + &identity, + &identity, + tolerance, + &traps); + if (unlikely (status)) + goto BAIL; + + if (traps.num_traps == 0) { + status = CAIRO_INT_STATUS_NOTHING_TO_DO; + goto BAIL; + } + + *primitive = _cairo_cogl_traps_to_composite_prim (surface, + &traps, + one_shot); + if (unlikely (!*primitive)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + insert_meta = + _cairo_freelist_alloc (&dev->path_stroke_meta_freelist); + if (unlikely (!insert_meta)) { + status = CAIRO_INT_STATUS_NO_MEMORY; + goto BAIL; + } + + insert_meta->base.hash = meta.base.hash; + insert_meta->base.size = + traps.num_traps * sizeof (CoglVertexP2) * 6; + insert_meta->tolerance = tolerance; + insert_meta->prim = cogl_object_ref (*primitive); + insert_meta->freelist = &dev->path_stroke_meta_freelist; + + status = _cairo_stroke_style_init_copy (&insert_meta->style, + style); + if (unlikely (status)) { + _cairo_stroke_style_fini (&insert_meta->style); + free (insert_meta); + insert_meta = NULL; + goto BAIL; + } + + status = _cairo_path_fixed_init_copy (&insert_meta->path, + &meta.path); + if (unlikely (status)) + goto BAIL; + + if (unlikely (_cairo_cache_insert (&dev->path_stroke_prim_cache, + &insert_meta->base))) + { + g_warning ("Stroke primitive cache insertion unsuccessful"); + goto BAIL; + } + + _cairo_path_fixed_fini (&meta.path); + _cairo_stroke_style_fini (&meta.style); + _cairo_traps_fini (&traps); + + return status; + +BAIL: + if (*primitive) { + cogl_object_unref (*primitive); + *primitive = NULL; + } + if (insert_meta) + _cairo_cogl_path_stroke_meta_destroy (insert_meta); + _cairo_path_fixed_fini (&meta.path); + _cairo_stroke_style_fini (&meta.style); + _cairo_traps_fini (&traps); + + return status; +} + +static void +_cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface, + cairo_path_fixed_t *path, + int *clip_stack_depth, + cairo_fill_rule_t fill_rule, + double tolerance) +{ + cairo_rectangle_int_t extents; + cairo_int_status_t status; + CoglPrimitive *prim; + cairo_matrix_t transform; + CoglMatrix matrix; + double x1, y1, x2, y2; + + status = _cairo_cogl_fill_to_primitive (surface, + path, + fill_rule, + tolerance, + FALSE, + &prim, + &transform); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + /* If the clip is of zero fill area, set all clipped */ + cogl_framebuffer_push_scissor_clip (surface->framebuffer, + 0, 0, 0, 0); + (*clip_stack_depth)++; + return; + } else if (unlikely (status)) { + g_warning ("Failed to get primitive for clip path while " + "flushing journal"); + goto BAIL; + } + + float transformfv[16] = { + transform.xx, transform.yx, 0, 0, + transform.xy, transform.yy, 0, 0, + 0, 0, 1, 0, + transform.x0, transform.y0, 0, 1 + }; + + cogl_matrix_init_from_array (&matrix, transformfv); + + cogl_framebuffer_push_matrix (surface->framebuffer); + cogl_framebuffer_transform (surface->framebuffer, &matrix); + + _cairo_path_fixed_approximate_clip_extents (path, &extents); + + /* The extents need to be transformed by the inverse of the + * modelview matrix because they are in terms of un-transformed + * device coordinates and the bounds coordinates will be + * transformed by the modelview matrix */ + status = cairo_matrix_invert (&transform); + if (unlikely (status)) { + g_warning ("Could not apply clip due to invalid matrix from " + "path transformation"); + goto BAIL; + } + + x1 = extents.x; + y1 = extents.y; + x2 = extents.x + extents.width; + y2 = extents.y + extents.height; + _cairo_matrix_transform_bounding_box (&transform, + &x1, &y1, &x2, &y2, + NULL); + + cogl_framebuffer_push_primitive_clip (surface->framebuffer, prim, + x1, y1, x2, y2); + (*clip_stack_depth)++; + + cogl_framebuffer_pop_matrix (surface->framebuffer); + +BAIL: + if (prim) + cogl_object_unref (prim); +} + +/* This is the way in which we handle CAIRO_EXTEND_NONE set on the + * source or mask pattern surfaces, as well as unbounded operators. + * First, we limit the rendering area to the region which will not be + * sampled from beyond the source or mask textures with additional clip + * paths, which were created when we obtained the original pipeline. + * The region will also be limited by the drawing area due to the fact + * we are drawing with the original primitive's vertices. + * + * In order to handle unbounded operators, we do a second rendering pass + * for places outside of such region. We limit the rending to outside + * this region by using a depth buffer to preserve all places where + * rendering took place during the first pass. For this region, we also + * have to remove the CAIRO_EXTEND_NONE clips if the operator is not + * bound by their respective contents. Because OpenGL sets all vertex + * z-values to 0.0 if none are supplied in the attributes data (we only + * supply x and y values), it will update the region in the buffer to a + * value over the default clearing value of 1.0. Given that the default + * test function is GL_LESS, we don't have to set z attributes on the + * vertices of the second rendering pass either, as 0.0 will never be + * less than 0.0. If cogl ever adds a method to clip out a primitive + * instead of just clipping it in, we may be able to use a more + * efficient method using the stencil buffer. */ +static void +_cairo_cogl_apply_tex_clips (cairo_cogl_surface_t *surface, + int *clip_stack_depth, + cairo_cogl_pipeline_t *pipeline) +{ + CoglDepthState depth_state; + CoglBool cogl_status; + + /* Enable the depth test if it will be needed */ + if ((!pipeline->mask_bounded && pipeline->has_mask_tex_clip) || + (!pipeline->src_bounded && pipeline->has_src_tex_clip)) + { + cogl_depth_state_init (&depth_state); + cogl_depth_state_set_test_enabled (&depth_state, TRUE); + cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline, + &depth_state, + NULL); + if (unlikely (cogl_status != TRUE)) + g_warning ("Error setting depth state for unbounded render"); + + /* Clear the depth buffer to 1.0. The color values are unused + * placeholders. */ + cogl_framebuffer_clear4f (surface->framebuffer, + COGL_BUFFER_BIT_DEPTH, + 0.0, 0.0, 0.0, 0.0); + } + + if (pipeline->mask_bounded && !pipeline->src_bounded) { + /* Push mask clip first so later we can pop the source clip + * and still be bound by the mask clip */ + if (pipeline->has_mask_tex_clip) + _cairo_cogl_set_path_prim_clip (surface, + &pipeline->mask_tex_clip, + clip_stack_depth, + CAIRO_FILL_RULE_WINDING, + 0.0); + if (pipeline->has_src_tex_clip) + _cairo_cogl_set_path_prim_clip (surface, + &pipeline->src_tex_clip, + clip_stack_depth, + CAIRO_FILL_RULE_WINDING, + 0.0); + } else { + if (pipeline->has_src_tex_clip) + _cairo_cogl_set_path_prim_clip (surface, + &pipeline->src_tex_clip, + clip_stack_depth, + CAIRO_FILL_RULE_WINDING, + 0.0); + if (pipeline->has_mask_tex_clip) + _cairo_cogl_set_path_prim_clip (surface, + &pipeline->mask_tex_clip, + clip_stack_depth, + CAIRO_FILL_RULE_WINDING, + 0.0); + } +} + +/* Get the pipeline for the second pass */ +static CoglPipeline * +_cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface, + cairo_operator_t op) +{ + CoglPipeline *unbounded_pipeline; + CoglDepthState depth_state; + CoglBool cogl_status; + cairo_cogl_device_t *dev = to_device(surface->base.device); + + /* If a template pipeline exists for any given operator, the + * corresponding solid template pipeline always exists */ + unbounded_pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_color4f (unbounded_pipeline, 0.0, 0.0, 0.0, 0.0); + + /* Enable depth test on second-pass pipeline */ + cogl_depth_state_init (&depth_state); + cogl_depth_state_set_test_enabled (&depth_state, TRUE); + cogl_status = cogl_pipeline_set_depth_state (unbounded_pipeline, + &depth_state, + NULL); + if (unlikely (cogl_status != TRUE)) + g_warning ("Error setting depth state for unbounded render"); + + return unbounded_pipeline; +} + +static void +_cairo_cogl_unbounded_render (cairo_cogl_surface_t *surface, + int *clip_stack_depth, + cairo_cogl_pipeline_t *pipeline, + cairo_bool_t *needs_vertex_render) +{ + /* We will need a second rendering of the original vertices if we + * still need to be bounded by the mask but had a source tex clip */ + *needs_vertex_render = FALSE; + + /* Pop all unbounded tex clips. Do not pop clips the operator is + * bounded by, so that we can still be bounded by them during the + * second pass (vertex render or extents render). */ + if (pipeline->mask_bounded && pipeline->src_bounded) { + /* We don't need a second pass if it will just be in the same + * region as the first */ + } else if (pipeline->src_bounded) { + if (pipeline->has_mask_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + } else if (pipeline->mask_bounded) { + if (pipeline->has_src_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + *needs_vertex_render = TRUE; + } + } else { + if (pipeline->has_src_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + if (pipeline->has_mask_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + } + + /* If an operator is unbounded by the mask, we need to render the + * second transparent pass within the full unbounded extents */ + if (!pipeline->mask_bounded) { + CoglPipeline *unbounded_pipeline; + + /* Draw a transparent rectangle to cover the entire extents */ + unbounded_pipeline = + _cairo_cogl_setup_unbounded_area_pipeline (surface, + pipeline->op); + cogl_framebuffer_draw_rectangle (surface->framebuffer, + unbounded_pipeline, + pipeline->unbounded_extents.x, + pipeline->unbounded_extents.y, + pipeline->unbounded_extents.x + pipeline->unbounded_extents.width, + pipeline->unbounded_extents.y + pipeline->unbounded_extents.height); + cogl_object_unref (unbounded_pipeline); + } +} + +static void +_cairo_cogl_post_unbounded_render (cairo_cogl_surface_t *surface, + int *clip_stack_depth, + cairo_cogl_pipeline_t *pipeline) +{ + CoglDepthState depth_state; + CoglBool cogl_status; + + /* Disable the depth test */ + if ((!pipeline->mask_bounded && pipeline->has_mask_tex_clip) || + (!pipeline->src_bounded && pipeline->has_src_tex_clip)) + { + cogl_depth_state_init (&depth_state); + cogl_depth_state_set_test_enabled (&depth_state, FALSE); + cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline, + &depth_state, + NULL); + if (unlikely (cogl_status != TRUE)) + g_warning ("Error setting depth state after unbounded render"); + } + + /* Pop all bounded tex clips (those that were not popped before) */ + if (pipeline->src_bounded && pipeline->mask_bounded) { + if (pipeline->has_src_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + if (pipeline->has_mask_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + } else if (pipeline->src_bounded) { + if (pipeline->has_src_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + } else if (pipeline->mask_bounded) { + if (pipeline->has_mask_tex_clip) { + cogl_framebuffer_pop_clip (surface->framebuffer); + (*clip_stack_depth)--; + } + } else { + /* We have already popped all of the clips in the + * unbounded_render function */ + } +} + +static void +_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface) +{ + GList *l; + cairo_cogl_device_t *dev; + int clip_stack_depth = 0; + int i; + + if (!surface->journal) + return; + + dev = to_device(surface->base.device); + if (dev->buffer_stack && dev->buffer_stack_offset) { + cogl_buffer_unmap (dev->buffer_stack); + cogl_object_unref (dev->buffer_stack); + dev->buffer_stack = NULL; + } + + if (unlikely (_cairo_cogl_surface_ensure_framebuffer (surface))) { + g_warning ("Could not get framebuffer for flushing journal"); + assert (0); + } + + cogl_framebuffer_push_matrix (surface->framebuffer); + + for (l = surface->journal->head; l; l = l->next) { + cairo_cogl_journal_entry_t *entry = l->data; + + switch (entry->type) + { + case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: { + cairo_cogl_journal_clip_entry_t *clip_entry = + (cairo_cogl_journal_clip_entry_t *)entry; + cairo_clip_path_t *path; + + for (i = 0; i < clip_stack_depth; i++) + cogl_framebuffer_pop_clip (surface->framebuffer); + clip_stack_depth = 0; + + if (clip_entry->clip == NULL) + continue; // there is no clip + + for (path = clip_entry->clip->path, i = 0; + path; + path = path->prev, i++) + { + _cairo_cogl_set_path_prim_clip (surface, + &path->path, + &clip_stack_depth, + path->fill_rule, + path->tolerance); + } + + if (clip_entry->clip->num_boxes > 0) { + cairo_path_fixed_t boxes_path; + + _cairo_path_fixed_init (&boxes_path); + for (int i = 0; i < clip_entry->clip->num_boxes; i++) { + if (unlikely (_cairo_path_fixed_add_box (&boxes_path, + &clip_entry->clip->boxes[i]))) + { + g_warning ("Could not add all clip boxes while " + "flushing journal"); + break; + } + } + + _cairo_cogl_set_path_prim_clip (surface, + &boxes_path, + &clip_stack_depth, + CAIRO_FILL_RULE_WINDING, + 0.0); + + _cairo_path_fixed_fini (&boxes_path); + } + + surface->n_clip_updates_per_frame++; + break; + } + case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: { + cairo_cogl_journal_rect_entry_t *rect_entry = + (cairo_cogl_journal_rect_entry_t *)entry; + float tex_coords[8]; + float x1 = rect_entry->x; + float y1 = rect_entry->y; + float x2 = rect_entry->x + rect_entry->width; + float y2 = rect_entry->y + rect_entry->height; + cairo_matrix_t *ctm = &rect_entry->ctm; + float ctmfv[16] = { + ctm->xx, ctm->yx, 0, 0, + ctm->xy, ctm->yy, 0, 0, + 0, 0, 1, 0, + ctm->x0, ctm->y0, 0, 1 + }; + CoglMatrix transform; + cairo_bool_t needs_vertex_render; + CoglPipeline *unbounded_pipeline; + + cogl_matrix_init_from_array (&transform, ctmfv); + + _cairo_cogl_apply_tex_clips (surface, + &clip_stack_depth, + rect_entry->pipeline); + + if (rect_entry->pipeline->n_layers) { + g_assert (rect_entry->pipeline->n_layers <= 2); + tex_coords[0] = x1; + tex_coords[1] = y1; + tex_coords[2] = x2; + tex_coords[3] = y2; + if (rect_entry->pipeline->n_layers > 1) + memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4); + } + + cogl_framebuffer_push_matrix (surface->framebuffer); + cogl_framebuffer_transform (surface->framebuffer, &transform); + cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer, + rect_entry->pipeline->pipeline, + x1, y1, + x2, y2, + tex_coords, + 4 * rect_entry->pipeline->n_layers); + + _cairo_cogl_unbounded_render (surface, + &clip_stack_depth, + rect_entry->pipeline, + &needs_vertex_render); + if (needs_vertex_render) { + unbounded_pipeline = + _cairo_cogl_setup_unbounded_area_pipeline (surface, + rect_entry->pipeline->op); + cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer, + unbounded_pipeline, + x1, y1, + x2, y2, + tex_coords, + 4 * rect_entry->pipeline->n_layers); + cogl_object_unref (unbounded_pipeline); + } + _cairo_cogl_post_unbounded_render (surface, + &clip_stack_depth, + rect_entry->pipeline); + + cogl_framebuffer_pop_matrix (surface->framebuffer); + break; + } + case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: { + cairo_cogl_journal_prim_entry_t *prim_entry = + (cairo_cogl_journal_prim_entry_t *)entry; + CoglMatrix transform; + cairo_bool_t needs_vertex_render; + CoglPipeline *unbounded_pipeline; + + _cairo_cogl_apply_tex_clips (surface, + &clip_stack_depth, + prim_entry->pipeline); + + cogl_framebuffer_push_matrix (surface->framebuffer); + cairo_matrix_t *ctm = &prim_entry->transform; + float ctmfv[16] = { + ctm->xx, ctm->yx, 0, 0, + ctm->xy, ctm->yy, 0, 0, + 0, 0, 1, 0, + ctm->x0, ctm->y0, 0, 1 + }; + cogl_matrix_init_from_array (&transform, ctmfv); + cogl_framebuffer_transform (surface->framebuffer, &transform); + + /* If the primitive is NULL, it means we just draw the + * unbounded rectangle */ + if (prim_entry->primitive) + cogl_primitive_draw (prim_entry->primitive, + surface->framebuffer, + prim_entry->pipeline->pipeline); + + _cairo_cogl_unbounded_render (surface, + &clip_stack_depth, + prim_entry->pipeline, + &needs_vertex_render); + if (needs_vertex_render && prim_entry->primitive) { + unbounded_pipeline = + _cairo_cogl_setup_unbounded_area_pipeline (surface, + prim_entry->pipeline->op); + cogl_primitive_draw (prim_entry->primitive, + surface->framebuffer, + unbounded_pipeline); + cogl_object_unref (unbounded_pipeline); + } + _cairo_cogl_post_unbounded_render (surface, + &clip_stack_depth, + prim_entry->pipeline); + + cogl_framebuffer_pop_matrix (surface->framebuffer); + break; + } + default: + assert (0); /* not reached! */ + } + } + + cogl_framebuffer_pop_matrix (surface->framebuffer); + + for (i = 0; i < clip_stack_depth; i++) + cogl_framebuffer_pop_clip (surface->framebuffer); + + _cairo_cogl_journal_discard (surface); +} + +static cairo_status_t +_cairo_cogl_surface_flush (void *abstract_surface, + unsigned flags) +{ + cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + _cairo_cogl_journal_flush (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_cogl_surface_finish (void *abstract_surface) +{ + cairo_cogl_surface_t *surface = abstract_surface; + + if (surface->texture) + cogl_object_unref (surface->texture); + + if (surface->framebuffer) + cogl_object_unref (surface->framebuffer); + + if (surface->journal) + _cairo_cogl_journal_free (surface); + + /*XXX wtf */ + cairo_device_release (surface->base.device); + + return CAIRO_STATUS_SUCCESS; +} + +static CoglTextureComponents +get_components_from_cairo_content (cairo_content_t content) +{ + /* We use RGBA for color-only surfaces due to the fact that cogl + * does not provide a padded XRGB format, thereby making us use + * RGBA formats to represent e.g. CAIRO_FORMAT_RGB24 and doing very + * expensive format conversions on the cpu when images are read + * back */ + return (content & CAIRO_CONTENT_COLOR) ? + COGL_TEXTURE_COMPONENTS_RGBA : + COGL_TEXTURE_COMPONENTS_A; +} + +static CoglPixelFormat +get_default_cogl_format_from_components (CoglTextureComponents components) +{ + switch (components) + { + case COGL_TEXTURE_COMPONENTS_A: + return COGL_PIXEL_FORMAT_A_8; + case COGL_TEXTURE_COMPONENTS_RG: + return COGL_PIXEL_FORMAT_RG_88; + case COGL_TEXTURE_COMPONENTS_RGB: + case COGL_TEXTURE_COMPONENTS_RGBA: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + return COGL_PIXEL_FORMAT_BGRA_8888_PRE; +#else + return COGL_PIXEL_FORMAT_ARGB_8888_PRE; +#endif + case COGL_TEXTURE_COMPONENTS_DEPTH: + return COGL_PIXEL_FORMAT_DEPTH_32; + default: + return 0; + } +} + +static CoglTextureComponents +get_components_from_cairo_format (cairo_format_t format) +{ + switch (format) + { + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_A8: + return COGL_TEXTURE_COMPONENTS_A; + + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB96F: + return COGL_TEXTURE_COMPONENTS_RGB; + + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGBA128F: + return COGL_TEXTURE_COMPONENTS_RGBA; + + case CAIRO_FORMAT_INVALID: + default: + g_warning("Cairo format unrepresentable by cogl"); + return 0; + } +} + +static CoglPixelFormat +get_cogl_format_from_cairo_format (cairo_format_t cairo_format); + +/* XXX: We often use RGBA format for onscreen framebuffers so make sure + * to handle CAIRO_FORMAT_INVALID sensibly */ +static cairo_format_t +get_cairo_format_from_cogl_format (CoglPixelFormat format) +{ + switch ((int)format) + { + case COGL_PIXEL_FORMAT_A_8: + return CAIRO_FORMAT_A8; + case COGL_PIXEL_FORMAT_RGB_565: + return CAIRO_FORMAT_RGB16_565; + case COGL_PIXEL_FORMAT_RG_88: + g_warning ("cairo cannot handle red-green textures"); + return CAIRO_FORMAT_INVALID; + case COGL_PIXEL_FORMAT_DEPTH_32: + g_warning ("cairo cannot handle depth textures"); + return CAIRO_FORMAT_INVALID; + + case COGL_PIXEL_FORMAT_BGRA_8888_PRE: + case COGL_PIXEL_FORMAT_ARGB_8888_PRE: + case COGL_PIXEL_FORMAT_RGBA_8888_PRE: + /* Note: this is ambiguous since CAIRO_FORMAT_RGB24 + * would also map to the same CoglPixelFormat */ + return CAIRO_FORMAT_ARGB32; + + default: + g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d", + format, + format & COGL_A_BIT, + format & COGL_BGR_BIT, + format & COGL_PREMULT_BIT, + format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT)); + return CAIRO_FORMAT_INVALID; + } +} + +static CoglPixelFormat +get_cogl_format_from_cairo_format (cairo_format_t cairo_format) +{ + switch (cairo_format) + { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + return COGL_PIXEL_FORMAT_BGRA_8888_PRE; +#else + return COGL_PIXEL_FORMAT_ARGB_8888_PRE; +#endif + case CAIRO_FORMAT_A8: + return COGL_PIXEL_FORMAT_A_8; + case CAIRO_FORMAT_RGB16_565: + return COGL_PIXEL_FORMAT_RGB_565; + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB96F: + case CAIRO_FORMAT_RGBA128F: + return 0; + } + + g_warn_if_reached (); + return 0; +} + +static cairo_surface_t * +_cairo_cogl_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_cogl_surface_t *reference_surface = abstract_surface; + cairo_cogl_surface_t *surface; + CoglTexture *texture; + cairo_status_t status; + cairo_cogl_device_t *dev = + to_device(reference_surface->base.device); + int tex_width = width; + int tex_height = height; + + /* In the case of lack of NPOT texture support, we allocate texture + * with dimensions of the next power of two */ + if (!dev->has_npots) { + tex_width = pow (2, ceil (log2 (tex_width))); + tex_height = pow (2, ceil (log2 (tex_height))); + } + + texture = cogl_texture_2d_new_with_size (dev->cogl_context, + tex_width, tex_height); + if (!texture) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + cogl_texture_set_components (texture, + get_components_from_cairo_content (content)); + + surface = (cairo_cogl_surface_t *) + _cairo_cogl_surface_create_full (dev, content, NULL, texture); + if (unlikely (surface->base.status)) + return &surface->base; + + /* The surface will take a reference on the texture */ + cogl_object_unref (texture); + + /* If we passed a texture with larger dimensions, we need to set + * the surface dimensions */ + surface->width = width; + surface->height = height; + + status = _cairo_cogl_surface_ensure_framebuffer (surface); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + return &surface->base; +} + +static cairo_status_t +_cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface, + cairo_rectangle_int_t *interest, + cairo_image_surface_t **image_out) +{ + cairo_image_surface_t *image; + cairo_status_t status; + cairo_format_t cairo_format; + CoglPixelFormat cogl_format; + + status = _cairo_cogl_surface_ensure_framebuffer (surface); + if (unlikely (status)) + return status; + + if (surface->texture) + { + cogl_format = + get_default_cogl_format_from_components ( + cogl_texture_get_components (surface->texture) ); + cairo_format = get_cairo_format_from_cogl_format (cogl_format); + } else { + cairo_format = + _cairo_format_from_content (surface->base.content); + cogl_format = get_cogl_format_from_cairo_format (cairo_format); + } + + image = (cairo_image_surface_t *) + cairo_image_surface_create (cairo_format, + surface->width, + surface->height); + if (image->base.status) + return image->base.status; + + cogl_framebuffer_read_pixels (surface->framebuffer, 0, 0, + surface->width, surface->height, + cogl_format, image->data); + + *image_out = image; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_cogl_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_cogl_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (unlikely (_cairo_surface_flush (abstract_surface, 0))) + g_warning ("Error flushing journal while acquiring image"); + + if (surface->texture) { + CoglTextureComponents components = + cogl_texture_get_components(surface->texture); + CoglPixelFormat cogl_format = + get_default_cogl_format_from_components (components); + cairo_format_t cairo_format = + get_cairo_format_from_cogl_format (cogl_format); + if (cairo_format == CAIRO_FORMAT_INVALID) { + cairo_format = CAIRO_FORMAT_ARGB32; + cogl_format = + get_cogl_format_from_cairo_format (cairo_format); + } + + /* We use the actual texture dimensions here instead, because + * if we have a larger texture than the surface dimensions for + * devices not supporting NPOT textures, the surface dimensions + * will not be able to fit the data */ + cairo_image_surface_t *image = (cairo_image_surface_t *) + cairo_image_surface_create (cairo_format, + cogl_texture_get_width (surface->texture), + cogl_texture_get_height (surface->texture)); + if (image->base.status) + return image->base.status; + + cogl_texture_get_data (surface->texture, + cogl_format, + 0, + image->data); + + /* If the texture dimensions were different than the surface + * dimensions, this will set them to the correct values. + * Because the stride stays the same, it will still function + * correctly */ + image->width = surface->width; + image->height = surface->height; + + image->base.is_clear = FALSE; + *image_out = image; + } else { + cairo_rectangle_int_t extents = { + 0, 0, surface->width, surface->height + }; + status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents, + image_out); + if (unlikely (status)) + return status; + } + + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_cogl_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_cogl_surface_clear (cairo_cogl_surface_t *surface, + const cairo_color_t *color) +{ + /* Anything batched in the journal up until now is redundant... */ + _cairo_cogl_journal_discard (surface); + + /* XXX: we currently implicitly clear the depth and stencil buffer here + * but since we use the framebuffer_discard extension when available I + * suppose this doesn't matter too much. + * + * The main concern is that we want to avoid re-loading an external z + * buffer at the start of each frame, but also many gpu architectures have + * optimizations for how they handle the depth/stencil buffers and can get + * upset if they aren't cleared together at the start of the frame. + * + * FIXME: we need a way to assert that the clip stack currently isn't + * using the stencil buffer before clearing it here! + */ + cogl_framebuffer_clear4f (surface->framebuffer, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH | + COGL_BUFFER_BIT_STENCIL, + color->red * color->alpha, + color->green * color->alpha, + color->blue * color->alpha, + color->alpha); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y, + cairo_fixed_t width, + cairo_fixed_t height) +{ + cairo_status_t status; + + status = _cairo_path_fixed_move_to (path, x, y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, width, 0); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, 0, height); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, -width, 0); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_close_path (path); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + + +static CoglPipelineWrapMode +get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode, + cairo_cogl_device_t *dev) +{ + switch (extend_mode) + { + case CAIRO_EXTEND_NONE: + return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + case CAIRO_EXTEND_PAD: + return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + case CAIRO_EXTEND_REPEAT: + return COGL_PIPELINE_WRAP_MODE_REPEAT; + case CAIRO_EXTEND_REFLECT: + if (!dev->has_mirrored_repeat) + /* If the hardware cannot support mirrored repeating, we + * emulate it elsewhere */ + return COGL_PIPELINE_WRAP_MODE_REPEAT; + else + return COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT; + } + assert (0); /* not reached */ + return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; +} + +static CoglPipelineFilter +get_cogl_filter_for_filter (cairo_filter_t filter) +{ + switch (filter) + { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return COGL_PIPELINE_FILTER_NEAREST; + + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + return COGL_PIPELINE_FILTER_LINEAR; + + case CAIRO_FILTER_GAUSSIAN: + default: + g_warning("Invalid pattern filter"); + return COGL_PIPELINE_FILTER_NEAREST; + } +} + +static void +_cairo_cogl_matrix_all_scale (cairo_matrix_t *matrix, + double xscale, + double yscale) +{ + /* Since cairo_matrix_scale does not scale the x0 and y0 components, + * which is required for scaling translations to normalized + * coordinates, use a custom solution here. */ + matrix->xx *= xscale; + matrix->yx *= yscale; + matrix->xy *= xscale; + matrix->yy *= yscale; + matrix->x0 *= xscale; + matrix->y0 *= yscale; +} + +static CoglTexture * +_cairo_cogl_scale_texture (CoglContext *context, + CoglTexture *texture_in, + unsigned int new_width, + unsigned int new_height, + cairo_bool_t do_mirror_texture, + cairo_bool_t always_new_texture) +{ + CoglTexture *texture_out = NULL; + CoglPipeline *copying_pipeline = NULL; + CoglFramebuffer *fb = NULL; + CoglError *error = NULL; + unsigned int tex_width = new_width; + unsigned int tex_height = new_height; + + /* If the texture is already in the desired dimensions and we are + * not mirroring it, copying it, or reading from different extents, + * return it unmodified */ + if (!do_mirror_texture && !always_new_texture && + new_width == cogl_texture_get_width (texture_in) && + new_height == cogl_texture_get_height (texture_in)) + return texture_in; + + if (do_mirror_texture) { + tex_width *= 2; + tex_height *= 2; + } + + texture_out = + cogl_texture_2d_new_with_size (context, tex_width, tex_height); + if (unlikely (!texture_out)) { + g_warning ("Failed to get texture for scaling"); + goto BAIL; + } + + cogl_texture_set_components (texture_out, + cogl_texture_get_components (texture_in)); + + fb = cogl_offscreen_new_with_texture (texture_out); + if (unlikely (!cogl_framebuffer_allocate (fb, &error))) { + g_warning ("Could not get framebuffer for texture scaling: %s", + error->message); + cogl_error_free (error); + goto BAIL; + } + + cogl_framebuffer_orthographic (fb, 0, 0, + tex_width, tex_height, + -1, 100); + + copying_pipeline = cogl_pipeline_new (context); + cogl_pipeline_set_layer_texture (copying_pipeline, 0, texture_in); + cogl_pipeline_set_layer_filters (copying_pipeline, 0, + COGL_PIPELINE_FILTER_NEAREST, + COGL_PIPELINE_FILTER_NEAREST); + + if (do_mirror_texture) { + /* Draw four rectangles to the new texture with the appropriate + * reflection on each one */ + + const float rect_coordinates[32] = { + /* Rectangle 1 */ + 0, 0, 0.5 * tex_width, 0.5 * tex_height, + 0, 0, 1, 1, + + /* Rectangle 2 */ + tex_width, 0, 0.5 * tex_width, 0.5 * tex_height, + 0, 0, 1, 1, + + /* Rectangle 3 */ + 0, tex_height, 0.5 * tex_width, 0.5 * tex_height, + 0, 0, 1, 1, + + /* Rectangle 4 */ + tex_width, tex_height, 0.5 * tex_width, 0.5 * tex_height, + 0, 0, 1, 1 + }; + + cogl_framebuffer_draw_textured_rectangles (fb, + copying_pipeline, + rect_coordinates, + 4); + } else { + cogl_framebuffer_draw_textured_rectangle (fb, + copying_pipeline, + 0, 0, + tex_width, + tex_height, + 0, 0, 1, 1); + } + + cogl_object_unref (fb); + cogl_object_unref (copying_pipeline); + cogl_object_unref (texture_in); + + return texture_out; + +BAIL: + if (texture_out) + cogl_object_unref (texture_out); + if (fb) + cogl_object_unref (fb); + if (copying_pipeline) + cogl_object_unref (copying_pipeline); + + return NULL; +} + +/* NB: a reference for the texture is transferred to the caller which + * should be unrefed */ +static CoglTexture * +_cairo_cogl_acquire_cogl_surface_texture (cairo_cogl_surface_t *reference_surface, + cairo_surface_t *surface, + const cairo_rectangle_int_t *surface_extents, + const cairo_matrix_t *pattern_matrix, + cairo_matrix_t *out_matrix, + const cairo_bool_t need_mirrored_texture, + cairo_bool_t *is_mirrored_texture) +{ + CoglTexture *texture; + cairo_surface_t *clone = NULL; + cairo_cogl_surface_t *cogl_surface = + (cairo_cogl_surface_t *)surface; + cairo_bool_t do_mirror_texture; + cairo_cogl_device_t *dev = + to_device (reference_surface->base.device); + double xscale, yscale; + int new_width = surface_extents->width; + int new_height = surface_extents->height; + + if (surface_extents->x < 0 || surface_extents->y < 0 || + (surface_extents->x + surface_extents->width) > + cogl_surface->width || + (surface_extents->y + surface_extents->height) > + cogl_surface->height) + return NULL; + + *out_matrix = *pattern_matrix; + *is_mirrored_texture = FALSE; + + if (unlikely (_cairo_surface_flush (surface, 0))) { + g_warning ("Error flushing source surface while getting " + "pattern texture"); + goto BAIL; + } + + *is_mirrored_texture = + need_mirrored_texture || cogl_surface->is_mirrored_snapshot; + do_mirror_texture = + need_mirrored_texture && !cogl_surface->is_mirrored_snapshot; + + /* There seems to be a bug in which cogl isn't flushing its own + * internal journal when reading from dependent sub-textures. + * If this is ever fixed, the following block of code can be + * removed. */ + { + _cairo_cogl_surface_ensure_framebuffer (cogl_surface); + cogl_framebuffer_finish (cogl_surface->framebuffer); + } + /* We copy the surface to a new texture, thereby making a + * snapshot of it, as its contents may change between the time + * we log the pipeline and when we flush the journal. The sub + * texture itself cannot be used while drawing primitives, so we do + * a copy to a 2d texture. */ + texture = cogl_sub_texture_new (dev->cogl_context, + cogl_surface->texture, + surface_extents->x, + surface_extents->y, + surface_extents->width, + surface_extents->height); + if (unlikely (!texture)) + goto BAIL; + + /* If we do not support NPOT dimensions, scale the new texture to + * the next power of two while copying */ + if (!dev->has_npots) { + new_width = (int)pow (2, ceil (log2 (new_width))); + new_height = (int)pow (2, ceil (log2 (new_height))); + } + texture = _cairo_cogl_scale_texture (dev->cogl_context, + texture, + new_width, + new_height, + do_mirror_texture, + TRUE); + if (unlikely (!texture)) + goto BAIL; + + clone = + _cairo_cogl_surface_create_full (dev, + reference_surface->base.content, + NULL, + texture); + if (unlikely (clone->status)) { + g_warning ("Could not get clone surface for texture"); + goto BAIL; + } + _cairo_surface_attach_snapshot (surface, clone, NULL); + + /* Attaching the snapshot will take a reference on the clone surface... */ + cairo_surface_destroy (clone); + clone = NULL; + + /* Convert from un-normalized source coordinates in backend + * coordinates to normalized texture coordinates. */ + if (*is_mirrored_texture) { + xscale = 0.5 / surface_extents->width; + yscale = 0.5 / surface_extents->height; + } else { + xscale = 1.0 / surface_extents->width; + yscale = 1.0 / surface_extents->height; + } + _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale); + + return texture; + +BAIL: + if (texture) + cogl_object_unref (texture); + if (clone) + cairo_surface_destroy (clone); + + return NULL; +} + +/* NB: a reference for the texture is transferred to the caller which + * should be unrefed */ +static CoglTexture * +_cairo_cogl_acquire_recording_surface_texture (cairo_cogl_surface_t *reference_surface, + cairo_surface_t *surface, + const cairo_rectangle_int_t *extents, + const cairo_matrix_t *pattern_matrix, + cairo_matrix_t *out_matrix, + const cairo_bool_t need_mirrored_texture, + cairo_bool_t *is_mirrored_texture) +{ + CoglTexture *texture = NULL; + cairo_surface_t *clone = NULL; + cairo_cogl_device_t *dev = + to_device (reference_surface->base.device); + cairo_matrix_t transform; + int tex_height, tex_width; + double xscale, yscale; + + *is_mirrored_texture = FALSE; + + /* We will pre-transform all of the drawing by the pattern matrix + * and confine it to the required extents, so no later transform + * will be required */ + cairo_matrix_init_translate (out_matrix, -extents->x, -extents->y); + + cairo_matrix_init_translate (&transform, extents->x, extents->y); + cairo_matrix_multiply (&transform, &transform, pattern_matrix); + + if (!dev->has_npots) { + /* Record to a texture sized to the next power of two */ + tex_width = (int)pow (2, ceil (log2 (extents->width))); + tex_height = (int)pow (2, ceil (log2 (extents->height))); + + /* And scale accordingly */ + cairo_matrix_scale (&transform, + (double)extents->width / (double)tex_width, + (double)extents->height / (double)tex_height); + } else { + tex_width = extents->width; + tex_height = extents->height; + } + + texture = cogl_texture_2d_new_with_size (dev->cogl_context, + tex_width, + tex_height); + if (unlikely (!texture)) { + g_warning ("Failed to create texture for replaying recording " + "surface"); + goto BAIL; + } + + cogl_texture_set_components (texture, + get_components_from_cairo_content (surface->content)); + + /* Do not attach this as a snapshot, as it only represents part of + * the surface */ + clone = + _cairo_cogl_surface_create_full (dev, + reference_surface->base.content, + NULL, + texture); + if (unlikely (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone))) + { + g_warning ("Could not get framebuffer for replaying recording " + "surface"); + goto BAIL; + } + + if (unlikely (_cairo_recording_surface_replay_with_clip (surface, + &transform, + clone, + NULL))) + { + g_warning ("Could not replay recording surface"); + goto BAIL; + } + _cairo_cogl_journal_flush ((cairo_cogl_surface_t *)clone); + cairo_surface_destroy (clone); + + if (need_mirrored_texture) { + /* Scale to the same image extents, but mirror the texture, + * thereby making it larger */ + texture = _cairo_cogl_scale_texture (dev->cogl_context, + texture, + tex_width, + tex_height, + TRUE, + FALSE); + if (unlikely (!texture)) + goto BAIL; + + *is_mirrored_texture = TRUE; + } + + /* Convert from un-normalized source coordinates in backend + * coordinates to normalized texture coordinates. */ + if (*is_mirrored_texture) { + xscale = 0.5 / extents->width; + yscale = 0.5 / extents->height; + } else { + xscale = 1.0 / extents->width; + yscale = 1.0 / extents->height; + } + _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale); + + return texture; + +BAIL: + if (clone) + cairo_surface_destroy (clone); + if (texture) + cogl_object_unref (texture); + + return NULL; +} + +/* NB: a reference for the texture is transferred to the caller which + * should be unrefed */ +static CoglTexture * +_cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_surface, + cairo_surface_t *surface, + const cairo_matrix_t *pattern_matrix, + cairo_matrix_t *out_matrix, + const cairo_bool_t need_mirrored_texture, + cairo_bool_t *is_mirrored_texture) +{ + CoglTexture *texture = NULL; + cairo_image_surface_t *image; + cairo_image_surface_t *acquired_image = NULL; + void *image_extra; + cairo_image_surface_t *image_clone = NULL; + CoglBitmap *bitmap; + CoglError *error = NULL; + cairo_surface_t *clone = NULL; + CoglPixelFormat format; + cairo_cogl_device_t *dev = + to_device (reference_surface->base.device); + ptrdiff_t stride; + unsigned char *data; + double xscale, yscale; + + *out_matrix = *pattern_matrix; + *is_mirrored_texture = FALSE; + + if (_cairo_surface_is_image (surface)) { + image = (cairo_image_surface_t *)surface; + } else { + cairo_status_t status = + _cairo_surface_acquire_source_image (surface, + &acquired_image, + &image_extra); + if (unlikely (status)) { + g_warning ("acquire_source_image failed: %s [%d]", + cairo_status_to_string (status), status); + return NULL; + } + image = acquired_image; + } + + format = get_cogl_format_from_cairo_format (image->format); + if (!format) { + image_clone = _cairo_image_surface_coerce (image); + if (unlikely (image_clone->base.status)) { + g_warning ("image_surface_coerce failed"); + texture = NULL; + goto BAIL; + } + + format = + get_cogl_format_from_cairo_format (image_clone->format); + assert (format); + + image = image_clone; + } + + if (image->stride < 0) { + /* If the stride is negative, this modifies the data pointer so + * that all of the pixels are read into the texture, but + * upside-down. We then invert the matrix so the texture is + * read from the bottom up instead of from the top down. */ + stride = -image->stride; + data = image->data - stride * (image->height - 1); + + out_matrix->yx *= -1.0; + out_matrix->yy *= -1.0; + out_matrix->y0 += image->height; + } else { + stride = image->stride; + data = image->data; + } + + bitmap = cogl_bitmap_new_for_data (dev->cogl_context, + image->width, + image->height, + format, /* incoming */ + stride, + data); + + if (!dev->has_npots) + texture = + cogl_texture_2d_sliced_new_from_bitmap (bitmap, + COGL_TEXTURE_MAX_WASTE); + else + texture = cogl_texture_2d_new_from_bitmap (bitmap); + + /* The texture will have taken a reference on the bitmap */ + cogl_object_unref (bitmap); + + cogl_texture_set_components (texture, + get_components_from_cairo_format (image->format)); + + if (unlikely (!cogl_texture_allocate (texture, &error))) { + g_warning ("Failed to allocate texture: %s", error->message); + cogl_error_free (error); + goto BAIL; + } + + if (need_mirrored_texture) { + int new_width = image->width; + int new_height = image->height; + + /* If the device does not support npot textures, scale to the + * next power of two as well */ + if (!dev->has_npots) { + new_width = (int)pow (2, ceil (log2 (new_width))); + new_height = (int)pow (2, ceil (log2 (new_height))); + } + + texture = _cairo_cogl_scale_texture (dev->cogl_context, + texture, + new_width, + new_height, + TRUE, + FALSE); + if (unlikely (!texture)) + goto BAIL; + + *is_mirrored_texture = TRUE; + } else if (!dev->has_npots) { + /* We need to scale the texture up if the hardware does not + * support npots */ + + /* Get dimensions for the next power of two */ + int new_width = (int)pow (2, ceil (log2 (image->width))); + int new_height = (int)pow (2, ceil (log2 (image->height))); + + texture = _cairo_cogl_scale_texture (dev->cogl_context, + texture, + new_width, + new_height, + FALSE, + FALSE); + if (unlikely (!texture)) + goto BAIL; + } + + clone = + _cairo_cogl_surface_create_full (dev, + reference_surface->base.content, + NULL, + texture); + if (unlikely (clone->status)) { + g_warning ("Unable to create clone surface for texture"); + goto BAIL; + } + + if (*is_mirrored_texture) + ((cairo_cogl_surface_t *)clone)->is_mirrored_snapshot = TRUE; + + if (_cairo_surface_is_subsurface (surface)) + _cairo_surface_subsurface_set_snapshot (surface, clone); + else + _cairo_surface_attach_snapshot (surface, clone, NULL); + + /* Attaching the snapshot will take a reference on the clone surface... */ + cairo_surface_destroy (clone); + clone = NULL; + + /* Convert from un-normalized source coordinates in backend + * coordinates to normalized texture coordinates. */ + if (*is_mirrored_texture) { + xscale = 0.5 / image->width; + yscale = 0.5 / image->height; + } else { + xscale = 1.0 / image->width; + yscale = 1.0 / image->height; + } + _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale); + + /* Release intermediate surface representations */ + if (image_clone) { + cairo_surface_destroy (&image_clone->base); + image_clone = NULL; + } + if (acquired_image) { + _cairo_surface_release_source_image (surface, + acquired_image, + image_extra); + acquired_image = NULL; + } + + return texture; + +BAIL: + if (clone) + cairo_surface_destroy (clone); + if (image_clone) + cairo_surface_destroy (&image_clone->base); + if (acquired_image) + _cairo_surface_release_source_image (surface, + acquired_image, + image_extra); + if (texture) + cogl_object_unref (texture); + + return NULL; +} + +static cairo_status_t +_cairo_cogl_create_tex_clip (cairo_path_fixed_t *tex_clip, + cairo_matrix_t inverse, + cairo_bool_t is_mirrored_texture) +{ + cairo_status_t status; + + status = cairo_matrix_invert (&inverse); + if (unlikely (status)) + return status; + + if (is_mirrored_texture) + status = + _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0, + _cairo_fixed_from_double (0.5), + _cairo_fixed_from_double (0.5)); + else + status = _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0, + CAIRO_FIXED_ONE, + CAIRO_FIXED_ONE); + if (unlikely (status)) + return status; + + _cairo_path_fixed_transform (tex_clip, &inverse); + + return CAIRO_STATUS_SUCCESS; +} + +/* NB: a reference for the texture is transferred to the caller which should + * be unrefed */ +static CoglTexture * +_cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern, + cairo_cogl_surface_t *destination, + const cairo_rectangle_int_t *extents, + cairo_cogl_texture_attributes_t *attributes, + cairo_path_fixed_t *tex_clip) +{ + CoglTexture *texture = NULL; + cairo_cogl_device_t *dev = to_device (destination->base.device); + cairo_bool_t is_mirrored_texture; + cairo_bool_t need_mirrored_texture = + (pattern->extend == CAIRO_EXTEND_REFLECT && + !dev->has_mirrored_repeat); + + switch ((int)pattern->type) + { + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_cogl_surface_t *clone; + cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface; + + clone = (cairo_cogl_surface_t *) + _cairo_surface_has_snapshot (surface, + &_cairo_cogl_surface_backend); + if (clone && clone->texture) + if ((!need_mirrored_texture) || clone->is_mirrored_snapshot) + { + texture = cogl_object_ref (clone->texture); + attributes->matrix = pattern->matrix; + is_mirrored_texture = clone->is_mirrored_snapshot; + + /* Convert from un-normalized source coordinates in + * backend coordinates to normalized texture + * coordinates. */ + _cairo_cogl_matrix_all_scale (&attributes->matrix, + 1.0 / clone->width, + 1.0 / clone->height); + }; + + if (!texture) { + cairo_rectangle_int_t surface_extents; + cairo_surface_t *unwrapped = + _cairo_surface_get_source (surface, &surface_extents); + + if (_cairo_surface_is_recording (surface)) { + texture = + _cairo_cogl_acquire_recording_surface_texture (destination, + surface, + extents, + &pattern->matrix, + &attributes->matrix, + need_mirrored_texture, + &is_mirrored_texture); + } else if (surface->type == CAIRO_SURFACE_TYPE_COGL && + ((cairo_cogl_surface_t *)unwrapped)->texture) { + texture = + _cairo_cogl_acquire_cogl_surface_texture (destination, + unwrapped, + &surface_extents, + &pattern->matrix, + &attributes->matrix, + need_mirrored_texture, + &is_mirrored_texture); + } + } + + if (!texture) + texture = + _cairo_cogl_acquire_generic_surface_texture (destination, + surface, + &pattern->matrix, + &attributes->matrix, + need_mirrored_texture, + &is_mirrored_texture); + + if (unlikely (!texture)) + return NULL; + + attributes->extend = pattern->extend; + attributes->filter = + get_cogl_filter_for_filter (pattern->filter); + attributes->has_component_alpha = pattern->has_component_alpha; + + attributes->s_wrap = + get_cogl_wrap_mode_for_extend (pattern->extend, dev); + attributes->t_wrap = attributes->s_wrap; + + /* In order to support CAIRO_EXTEND_NONE, we use the same wrap + * mode as CAIRO_EXTEND_PAD, but pass a clip to the drawing + * function to make sure that we never sample anything beyond + * the texture boundaries. */ + if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip) + if (_cairo_cogl_create_tex_clip (tex_clip, + attributes->matrix, + is_mirrored_texture)) + { + cogl_object_unref (texture); + return NULL; + } + + return texture; + } + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { + cairo_surface_t *surface; + cairo_matrix_t new_pattern_matrix; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + extents->width, extents->height); + if (_cairo_surface_offset_paint (surface, + extents->x, extents->y, + CAIRO_OPERATOR_SOURCE, + pattern, NULL)) { + cairo_surface_destroy (surface); + return NULL; + } + + cairo_matrix_init_translate (&new_pattern_matrix, + -extents->x, -extents->y); + + texture = + _cairo_cogl_acquire_generic_surface_texture (destination, + surface, + &new_pattern_matrix, + &attributes->matrix, + need_mirrored_texture, + &is_mirrored_texture); + if (unlikely (!texture)) + goto BAIL; + + attributes->extend = pattern->extend; + attributes->filter = COGL_PIPELINE_FILTER_NEAREST; + attributes->has_component_alpha = pattern->has_component_alpha; + + /* any pattern extend modes have already been dealt with... */ + attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + attributes->t_wrap = attributes->s_wrap; + + /* In order to support CAIRO_EXTEND_NONE, we use the same wrap + * mode as CAIRO_EXTEND_PAD, but pass a clip to the drawing + * function to make sure that we never sample anything beyond + * the texture boundaries. */ + if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip) + if (_cairo_cogl_create_tex_clip (tex_clip, + attributes->matrix, + is_mirrored_texture)) + { + cogl_object_unref (texture); + cairo_surface_destroy (surface); + return NULL; + } + +BAIL: + cairo_surface_destroy (surface); + + return texture; + } + case CAIRO_PATTERN_TYPE_LINEAR: { + cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern; + cairo_cogl_linear_gradient_t *gradient; + cairo_cogl_linear_texture_entry_t *linear_texture; + cairo_int_status_t status; + double dist, scale; + + status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device), + pattern->extend, + linear_pattern->base.n_stops, + linear_pattern->base.stops, + need_mirrored_texture, + &gradient); + if (unlikely (status)) + return NULL; + + linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend); + + attributes->extend = pattern->extend; + attributes->filter = + get_cogl_filter_for_filter (pattern->filter); + attributes->has_component_alpha = pattern->has_component_alpha; + attributes->s_wrap = + get_cogl_wrap_mode_for_extend (pattern->extend, dev); + attributes->t_wrap = attributes->s_wrap; + + attributes->matrix = pattern->matrix; + + double a = linear_pattern->pd2.x - linear_pattern->pd1.x; + double b = linear_pattern->pd2.y - linear_pattern->pd1.y; + double angle = - atan2f (b, a); + + cairo_matrix_rotate (&attributes->matrix, angle); + + cairo_matrix_translate (&attributes->matrix, + -linear_pattern->pd1.x, + -linear_pattern->pd1.y); + + /* Convert from un-normalized source coordinates in backend + * coordinates to normalized texture coordinates. */ + dist = sqrtf (a*a + b*b); + if (need_mirrored_texture) + scale = 0.5 / dist; + else + scale = 1.0 / dist; + _cairo_cogl_matrix_all_scale (&attributes->matrix, + scale, scale); + + return cogl_object_ref (linear_texture->texture); + } + default: + g_warning ("Unsupported source type"); + return NULL; + } +} + +static cairo_bool_t +set_blend (CoglPipeline *pipeline, const char *blend_string) +{ + CoglError *error = NULL; + if (unlikely (!cogl_pipeline_set_blend (pipeline, + blend_string, + &error))) + { + g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string); + cogl_error_free (error); + return FALSE; + } + return TRUE; +} + +static cairo_bool_t +_cairo_cogl_setup_op_state (CoglPipeline *pipeline, + cairo_operator_t op) +{ + cairo_bool_t status = FALSE; + + switch ((int)op) + { + case CAIRO_OPERATOR_OVER: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))"); + break; + case CAIRO_OPERATOR_IN: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)"); + break; + case CAIRO_OPERATOR_OUT: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), 0)"); + break; + case CAIRO_OPERATOR_ATOP: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], DST_COLOR * (1 - SRC_COLOR[A]))"); + break; + case CAIRO_OPERATOR_DEST: + status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR)"); + break; + case CAIRO_OPERATOR_DEST_OVER: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)"); + break; + case CAIRO_OPERATOR_DEST_IN: + status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])"); + break; + case CAIRO_OPERATOR_DEST_OUT: + status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * (1 - SRC_COLOR[A]))"); + break; + case CAIRO_OPERATOR_DEST_ATOP: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * SRC_COLOR[A])"); + break; + case CAIRO_OPERATOR_XOR: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * (1 - SRC_COLOR[A]))"); + break; + /* In order to handle SOURCE with a mask, we use two passes. The + * first consists of a CAIRO_OPERATOR_DEST_OUT with the source alpha + * replaced by the mask alpha in order to multiply all the + * destination values by one minus the mask alpha. The second pass + * (this one) then adds the source values, which have already been + * premultiplied by the mask alpha. */ + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)"); + break; + case CAIRO_OPERATOR_CLEAR: + /* Runtime check */ + /* CAIRO_OPERATOR_CLEAR is not supposed to use its own pipeline + * type. Use CAIRO_OPERATOR_DEST_OUT with the mask alpha as + * source alpha instead. */ + assert (0); + default: + g_warning ("Unsupported blend operator"); + assert (0); + } + + return status; +} + +static void +create_template_for_op_type (cairo_cogl_device_t *dev, + cairo_operator_t op, + cairo_cogl_template_type type) +{ + CoglPipeline *pipeline; + CoglColor color; + + if (dev->template_pipelines[op][type]) + return; + + cogl_color_init_from_4f (&color, 1.0f, 1.0f, 1.0f, 1.0f); + + if (!dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]) { + CoglPipeline *base = cogl_pipeline_new (dev->cogl_context); + + if (!_cairo_cogl_setup_op_state (base, op)) { + cogl_object_unref (base); + return; + } + + dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base; + } + + switch ((int)type) + { + case CAIRO_COGL_TEMPLATE_TYPE_SOLID: + return; + case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color); + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGBA = MODULATE (PRIMARY, CONSTANT[A])", + NULL); + break; + case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGBA = MODULATE (PRIMARY, TEXTURE[A])", + NULL); + break; + case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + break; + case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); + cogl_pipeline_set_layer_combine (pipeline, 1, + "RGBA = MODULATE (PREVIOUS, CONSTANT[A])", + NULL); + break; + case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_null_texture (pipeline, 1, + COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_combine (pipeline, 1, + "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", + NULL); + break; + case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + /* We do not set the combine color when we use this template + * pipeline, so the source texture alpha will be replaces by + * ones */ + cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color); + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGB = REPLACE (TEXTURE)" + "A = REPLACE (CONSTANT)", + NULL); + break; + case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + /* We do not set the combine color when we use this template + * pipeline, so the source texture alpha will be replaces by + * ones */ + cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color); + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGB = REPLACE (TEXTURE)" + "A = REPLACE (CONSTANT)", + NULL); + cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); + cogl_pipeline_set_layer_combine (pipeline, 1, + "RGBA = MODULATE (PREVIOUS, CONSTANT[A])", + NULL); + break; + case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA: + pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]); + cogl_pipeline_set_layer_null_texture (pipeline, 0, + COGL_TEXTURE_TYPE_2D); + /* We do not set the combine color when we use this template + * pipeline, so the source texture alpha will be replaces by + * ones */ + cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color); + cogl_pipeline_set_layer_combine (pipeline, 0, + "RGB = REPLACE (TEXTURE)" + "A = REPLACE (CONSTANT)", + NULL); + cogl_pipeline_set_layer_null_texture (pipeline, 1, + COGL_TEXTURE_TYPE_2D); + cogl_pipeline_set_layer_combine (pipeline, 1, + "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", + NULL); + break; + default: + g_warning ("Invalid cogl pipeline template type"); + return; + } + + dev->template_pipelines[op][type] = pipeline; +} + +static void +set_layer_texture_with_attributes (CoglPipeline *pipeline, + int layer_index, + CoglTexture *texture, + cairo_cogl_texture_attributes_t *attributes, + cairo_matrix_t *path_transform) +{ + cairo_matrix_t m; + + cogl_pipeline_set_layer_texture (pipeline, layer_index, texture); + + cogl_pipeline_set_layer_filters (pipeline, + layer_index, + attributes->filter, + attributes->filter); + + /* We multiply in the path transform here so that we read texture + * values from coordinates that are consistent with the coordinates + * of the path after it is transformed by the modelview matrix */ + if (path_transform) + cairo_matrix_multiply (&m, path_transform, &attributes->matrix); + else + m = attributes->matrix; + + if (!_cairo_matrix_is_identity (&m)) { + float texture_matrixfv[16] = { + m.xx, m.yx, 0, 0, + m.xy, m.yy, 0, 0, + 0, 0, 1, 0, + m.x0, m.y0, 0, 1 + }; + CoglMatrix texture_matrix; + cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv); + cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix); + } + + if (attributes->s_wrap != attributes->t_wrap) { + cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap); + cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap); + } else { + cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap); + } +} + +/* This takes an argument of a pointer to an array of two pointers to + * #cairo_cogl_pipeline_t. On failure, both pointers will be set to + * NULL */ +static void +get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t **pipelines, + const cairo_pattern_t *mask, + const cairo_pattern_t *source, + cairo_operator_t op, + cairo_cogl_surface_t *destination, + cairo_composite_rectangles_t *extents, + cairo_matrix_t *path_transform) +{ + cairo_cogl_template_type template_type; + cairo_cogl_device_t *dev = to_device(destination->base.device); + + pipelines[0] = NULL; + pipelines[1] = NULL; + + switch ((int)source->type) + { + case CAIRO_PATTERN_TYPE_SOLID: + if (mask) { + /* If the mask surface has no alpha content, we use a mask + * of solid ones */ + if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) || + (mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR)) + template_type = + CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID; + else + template_type = + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID; + } else { + template_type = CAIRO_COGL_TEMPLATE_TYPE_SOLID; + } + break; + case CAIRO_PATTERN_TYPE_SURFACE: + /* If the source does not have alpha content, we have to use + * a specialized set of texture combining functions in order to + * ensure that if we have a CAIRO_FORMAT_RGB24 source, we are + * ignoring the alpha and replacing it with ones. Otherwise, we + * use the template types for any other type of non-solid + * source. */ + if (((cairo_surface_pattern_t *)source)->surface->content == + CAIRO_CONTENT_COLOR) + { + if (mask) { + /* If the mask surface has no alpha content, we use a + * mask of solid ones */ + if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) || + (mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR)) + template_type = + CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA; + else + template_type = + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA; + } else { + template_type = + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA; + } + break; + } + // else fall through + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + if (mask) { + /* If the mask surface has no alpha content, we use a mask + * of solid ones */ + if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) || + (mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR)) + template_type = + CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE; + else + template_type = + CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE; + } else { + template_type = CAIRO_COGL_TEMPLATE_TYPE_TEXTURE; + } + break; + default: + g_warning ("Unsupported source type"); + return; + } + + /* pipelines[0] is for pre-rendering the mask alpha in the case + * that it cannot be represented through the source color alpha + * value. For more details, go to the description in + * _cairo_cogl_setup_op_state */ + if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) { + cairo_cogl_template_type prerender_type; + + pipelines[0] = g_new (cairo_cogl_pipeline_t, 1); + + if (mask && mask->type != CAIRO_PATTERN_TYPE_SOLID) + prerender_type = CAIRO_COGL_TEMPLATE_TYPE_SOLID; + else + prerender_type = CAIRO_COGL_TEMPLATE_TYPE_TEXTURE; + + /* Lazily create pipeline templates */ + if (unlikely (dev->template_pipelines[CAIRO_OPERATOR_DEST_OUT][prerender_type] == NULL)) + create_template_for_op_type (dev, + CAIRO_OPERATOR_DEST_OUT, + prerender_type); + + pipelines[0]->pipeline = + cogl_pipeline_copy (dev->template_pipelines[CAIRO_OPERATOR_DEST_OUT][prerender_type]); + + pipelines[0]->mask_bounded = + _cairo_operator_bounded_by_mask (op); + pipelines[0]->src_bounded = + _cairo_operator_bounded_by_source (op); + pipelines[0]->op = CAIRO_OPERATOR_DEST_OUT; + pipelines[0]->n_layers = 0; + pipelines[0]->has_src_tex_clip = FALSE; + pipelines[0]->has_mask_tex_clip = FALSE; + pipelines[0]->unbounded_extents = extents->unbounded; + } + + /* pipelines[1] is for normal rendering, modulating the mask with + * the source. Most operators will only need this pipeline. */ + if (op != CAIRO_OPERATOR_CLEAR) { + pipelines[1] = g_new (cairo_cogl_pipeline_t, 1); + + /* Lazily create pipeline templates */ + if (unlikely (dev->template_pipelines[op][template_type] == NULL)) + create_template_for_op_type (dev, op, template_type); + + pipelines[1]->pipeline = + cogl_pipeline_copy (dev->template_pipelines[op][template_type]); + + pipelines[1]->mask_bounded = + _cairo_operator_bounded_by_mask (op); + pipelines[1]->src_bounded = + _cairo_operator_bounded_by_source (op); + pipelines[1]->op = op; + pipelines[1]->n_layers = 0; + pipelines[1]->has_src_tex_clip = FALSE; + pipelines[1]->has_mask_tex_clip = FALSE; + pipelines[1]->unbounded_extents = extents->unbounded; + } + + if (pipelines[1]) { + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; + cogl_pipeline_set_color4f (pipelines[1]->pipeline, + solid_pattern->color.red * solid_pattern->color.alpha, + solid_pattern->color.green * solid_pattern->color.alpha, + solid_pattern->color.blue * solid_pattern->color.alpha, + solid_pattern->color.alpha); + } else { + cairo_cogl_texture_attributes_t attributes; + + _cairo_path_fixed_init (&pipelines[1]->src_tex_clip); + + CoglTexture *texture = + _cairo_cogl_acquire_pattern_texture (source, destination, + &extents->bounded, + &attributes, + &pipelines[1]->src_tex_clip); + if (unlikely (!texture)) + goto BAIL; + set_layer_texture_with_attributes (pipelines[1]->pipeline, + pipelines[1]->n_layers++, + texture, + &attributes, + path_transform); + cogl_object_unref (texture); + + if (pipelines[1]->src_tex_clip.buf.base.num_ops > 0) + pipelines[1]->has_src_tex_clip = TRUE; + else + _cairo_path_fixed_fini (&pipelines[1]->src_tex_clip); + } + } + + if (mask) { + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask; + CoglColor color; + cogl_color_init_from_4f (&color, + solid_pattern->color.red * solid_pattern->color.alpha, + solid_pattern->color.green * solid_pattern->color.alpha, + solid_pattern->color.blue * solid_pattern->color.alpha, + solid_pattern->color.alpha); + if (pipelines[1]) + cogl_pipeline_set_layer_combine_constant (pipelines[1]->pipeline, + pipelines[1]->n_layers++, + &color); + if (pipelines[0]) + cogl_pipeline_set_color (pipelines[0]->pipeline, + &color); + /* If the only component present in our mask is a color + * component, skip setting the layer texture, as we already + * set a solid of uniform ones on it during the template + * creation process */ + } else if (!(mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR)) { + cairo_cogl_texture_attributes_t attributes; + cairo_path_fixed_t mask_tex_clip; + + _cairo_path_fixed_init (&mask_tex_clip); + + CoglTexture *texture = + _cairo_cogl_acquire_pattern_texture (mask, destination, + &extents->bounded, + &attributes, + &mask_tex_clip); + if (unlikely (!texture)) + goto BAIL; + if (pipelines[1]) { + if (mask_tex_clip.buf.base.num_ops > 0) { + pipelines[1]->has_mask_tex_clip = TRUE; + if (unlikely (_cairo_path_fixed_init_copy (&pipelines[1]->mask_tex_clip, + &mask_tex_clip))) + goto BAIL; + } + set_layer_texture_with_attributes (pipelines[1]->pipeline, + pipelines[1]->n_layers++, + texture, + &attributes, + path_transform); + } + if (pipelines[0]) { + if (mask_tex_clip.buf.base.num_ops > 0) { + pipelines[0]->has_mask_tex_clip = TRUE; + if (unlikely (_cairo_path_fixed_init_copy (&pipelines[0]->mask_tex_clip, + &mask_tex_clip))) + goto BAIL; + } + set_layer_texture_with_attributes (pipelines[0]->pipeline, + pipelines[0]->n_layers++, + texture, + &attributes, + path_transform); + } + + _cairo_path_fixed_fini (&mask_tex_clip); + cogl_object_unref (texture); + } + } + + return; + +BAIL: + if (pipelines[0]) { + cogl_object_unref (pipelines[0]->pipeline); + if (pipelines[0]->has_src_tex_clip) + _cairo_path_fixed_fini (&pipelines[0]->src_tex_clip); + if (pipelines[0]->has_mask_tex_clip) + _cairo_path_fixed_fini (&pipelines[0]->mask_tex_clip); + g_free (pipelines[0]); + pipelines[0] = NULL; + } + if (pipelines[1]) { + cogl_object_unref (pipelines[1]->pipeline); + if (pipelines[1]->has_src_tex_clip) + _cairo_path_fixed_fini (&pipelines[1]->src_tex_clip); + if (pipelines[1]->has_mask_tex_clip) + _cairo_path_fixed_fini (&pipelines[1]->mask_tex_clip); + g_free (pipelines[1]); + pipelines[1] = NULL; + } +} + +#if 0 +CoglPrimitive * +_cairo_cogl_rectangle_new_p2t2t2 (CoglContext *cogl_context, + float x, + float y, + float width, + float height) +{ + CoglVertexP2 vertices[] = { + {x, y}, {x, y + height}, {x + width, y + height}, + {x, y}, {x + width, y + height}, {x + width, y} + }; + CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (cogl_context, + sizeof (vertices)); + CoglAttribute *pos = cogl_attribute_new (buffer, + "cogl_position_in", + sizeof (CoglVertexP2), + 0, + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + CoglAttribute *tex_coords0 = cogl_attribute_new (buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP2), + 0, + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + CoglAttribute *tex_coords0 = cogl_attribute_new (buffer, + "cogl_tex_coord0_in", + sizeof (CoglVertexP2), + 0, + 2, + COGL_ATTRIBUTE_TYPE_FLOAT); + CoglPrimitive *prim; + + cogl_buffer_set_data (buffer, 0, vertices, sizeof (vertices)); + + /* The attributes will now keep the buffer alive... */ + cogl_object_unref (buffer); + + prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES, + 6, pos, tex_coords, NULL); + + /* The primitive will now keep the attribute alive... */ + cogl_object_unref (pos); + + return prim; +} +#endif + +static void +_cairo_cogl_log_clip (cairo_cogl_surface_t *surface, + const cairo_clip_t *clip) +{ + if (!_cairo_clip_equal (clip, surface->last_clip)) { + _cairo_cogl_journal_log_clip (surface, clip); + _cairo_clip_destroy (surface->last_clip); + surface->last_clip = _cairo_clip_copy (clip); + } +} + +static void +_cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface, + cairo_composite_rectangles_t *composite) +{ + cairo_clip_t *clip = composite->clip; + + if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) + clip = NULL; + + if (clip == NULL) { + if (_cairo_composite_rectangles_can_reduce_clip (composite, + surface->last_clip)) + return; + } + + _cairo_cogl_log_clip (surface, clip); +} + +static cairo_bool_t +is_operator_supported (cairo_operator_t op) +{ + switch ((int)op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + return TRUE; + + default: + g_warning("cairo-cogl: Blend operator not supported"); + return FALSE; + } +} + +static cairo_int_status_t +_cairo_cogl_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_cogl_surface_t *surface; + cairo_int_status_t status; + cairo_matrix_t identity; + cairo_cogl_pipeline_t *pipelines[2]; + cairo_composite_rectangles_t extents; + + if (clip == NULL) { + status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface); + if (unlikely (status)) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) + return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT); + else if (source->type == CAIRO_PATTERN_TYPE_SOLID && + (op == CAIRO_OPERATOR_SOURCE || + (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) { + return _cairo_cogl_surface_clear (abstract_surface, + &((cairo_solid_pattern_t *) source)->color); + } + } + + /* fall back to handling the paint in terms of a rectangle... */ + + surface = (cairo_cogl_surface_t *)abstract_surface; + + if (!is_operator_supported (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = + _cairo_composite_rectangles_init_for_paint (&extents, + &surface->base, + op, + source, + clip); + if (unlikely (status)) + return status; + + get_source_mask_operator_destination_pipelines (pipelines, + NULL, + source, + op, + surface, + &extents, + NULL); + if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto BAIL; + } + + _cairo_cogl_maybe_log_clip (surface, &extents); + + cairo_matrix_init_identity (&identity); + if (pipelines[0]) + _cairo_cogl_journal_log_rectangle (surface, + pipelines[0], + extents.bounded.x, + extents.bounded.y, + extents.bounded.width, + extents.bounded.height, + &identity); + if (pipelines[1]) + _cairo_cogl_journal_log_rectangle (surface, + pipelines[1], + extents.bounded.x, + extents.bounded.y, + extents.bounded.width, + extents.bounded.height, + &identity); + +BAIL: + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +static cairo_int_status_t +_cairo_cogl_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_cogl_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + cairo_cogl_pipeline_t *pipelines[2]; + cairo_matrix_t identity; + + /* XXX: Use this to smoke test the acquire_source/dest_image fallback + * paths... */ + //return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!is_operator_supported (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &surface->base, + op, source, mask, clip); + if (unlikely (status)) + return status; + + get_source_mask_operator_destination_pipelines (pipelines, + mask, + source, + op, + surface, + &extents, + NULL); + if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto BAIL; + } + + _cairo_cogl_maybe_log_clip (surface, &extents); + + cairo_matrix_init_identity (&identity); + if (pipelines[0]) + _cairo_cogl_journal_log_rectangle (surface, + pipelines[0], + extents.bounded.x, + extents.bounded.y, + extents.bounded.width, + extents.bounded.height, + &identity); + if (pipelines[1]) + _cairo_cogl_journal_log_rectangle (surface, + pipelines[1], + extents.bounded.x, + extents.bounded.y, + extents.bounded.width, + extents.bounded.height, + &identity); + +BAIL: + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +static cairo_int_status_t +_cairo_cogl_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_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; + cairo_composite_rectangles_t extents; + cairo_cogl_pipeline_t *pipelines[2]; + cairo_int_status_t status; + cairo_matrix_t transform; + CoglPrimitive *prim = NULL; + + if (! is_operator_supported (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, + op, source, path, + style, + ctm, + clip); + if (unlikely (status)) + return status; + + status = _cairo_cogl_stroke_to_primitive (surface, path, style, + tolerance, TRUE, &prim, + &transform); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO + && _cairo_operator_bounded_by_mask (op) == FALSE) { + /* Just render the unbounded rectangle */ + prim = NULL; + } else if (unlikely (status)) { + goto BAIL; + } + + get_source_mask_operator_destination_pipelines (pipelines, + NULL, + source, + op, + surface, + &extents, + &transform); + if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto BAIL; + } + + _cairo_cogl_maybe_log_clip (surface, &extents); + + if (pipelines[0]) + _cairo_cogl_journal_log_primitive (surface, + pipelines[0], + prim, + &transform); + if (pipelines[1]) + _cairo_cogl_journal_log_primitive (surface, + pipelines[1], + prim, + &transform); + +BAIL: + /* The journal will take a reference on the primitive... */ + if (prim) + cogl_object_unref (prim); + + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +static cairo_int_status_t +_cairo_cogl_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_cogl_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + cairo_matrix_t transform; + CoglPrimitive *prim = NULL; + cairo_cogl_pipeline_t *pipelines[2]; + + if (! is_operator_supported (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &surface->base, + op, source, path, + clip); + if (unlikely (status)) + return status; + + status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, + tolerance, TRUE, &prim, + &transform); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO + && _cairo_operator_bounded_by_mask (op) == FALSE) { + /* Just render the unbounded rectangle */ + prim = NULL; + } else if (unlikely (status)) { + goto BAIL; + } + + get_source_mask_operator_destination_pipelines (pipelines, + NULL, + source, + op, + surface, + &extents, + &transform); + if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto BAIL; + } + + _cairo_cogl_maybe_log_clip (surface, &extents); + + if (pipelines[0]) + _cairo_cogl_journal_log_primitive (surface, + pipelines[0], + prim, + &transform); + if (pipelines[1]) + _cairo_cogl_journal_log_primitive (surface, + pipelines[1], + prim, + &transform); + +BAIL: + /* The journal will take a reference on the prim */ + if (prim) + cogl_object_unref (prim); + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +/* Mostly taken from #cairo_vg_surface.c */ +/* TODO: implement actual font support, with either cogl-pango's glyph + * cache or our own */ +static cairo_int_status_t +_cairo_cogl_surface_show_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_status_t status = CAIRO_STATUS_SUCCESS; + cairo_path_fixed_t path; + int num_chunk_glyphs; + int i; + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + +#define GLYPH_CHUNK_SIZE 100 + + /* Chunk glyphs in order to avoid large computation overheads + * during tessellation of long strings */ + for (i = 0; i < num_glyphs; i += GLYPH_CHUNK_SIZE) { + num_chunk_glyphs = (num_glyphs - i) < GLYPH_CHUNK_SIZE ? + (num_glyphs - i) : GLYPH_CHUNK_SIZE; + + _cairo_path_fixed_init (&path); + status = _cairo_scaled_font_glyph_path (scaled_font, + &glyphs[i], + num_chunk_glyphs, + &path); + if (unlikely (status)) + goto BAIL; + + status = _cairo_cogl_surface_fill (abstract_surface, + op, source, &path, + CAIRO_FILL_RULE_WINDING, + CAIRO_GSTATE_TOLERANCE_DEFAULT, + CAIRO_ANTIALIAS_DEFAULT, + clip); + + _cairo_path_fixed_fini (&path); + } + +#undef GLYPH_CHUNK_SIZE + + return CAIRO_STATUS_SUCCESS; + +BAIL: + _cairo_path_fixed_fini (&path); + + return status; +} + +const cairo_surface_backend_t _cairo_cogl_surface_backend = { + CAIRO_SURFACE_TYPE_COGL, + _cairo_cogl_surface_finish, + _cairo_default_context_create, + + _cairo_cogl_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, + _cairo_cogl_surface_acquire_source_image, + _cairo_cogl_surface_release_source_image, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_cogl_surface_get_extents, + NULL, /* get_font_options */ + + _cairo_cogl_surface_flush, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_cogl_surface_paint, + _cairo_cogl_surface_mask, + _cairo_cogl_surface_stroke, + _cairo_cogl_surface_fill, + NULL, /* fill_stroke */ + _cairo_cogl_surface_show_glyphs, +}; + +static cairo_surface_t * +_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev, + cairo_content_t content, + CoglFramebuffer *framebuffer, + CoglTexture *texture) +{ + cairo_cogl_surface_t *surface; + cairo_status_t status; + + status = cairo_device_acquire (&dev->base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = _cairo_malloc (sizeof (cairo_cogl_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->is_mirrored_snapshot = FALSE; + + surface->framebuffer = framebuffer; + if (framebuffer) { + surface->width = cogl_framebuffer_get_width (framebuffer); + surface->height = cogl_framebuffer_get_height (framebuffer); + cogl_object_ref (framebuffer); + } + + /* FIXME: If texture == NULL and we are given an offscreen framebuffer + * then we want a way to poke inside the framebuffer to get a texture */ + surface->texture = texture; + if (texture) { + if (!framebuffer) { + surface->width = cogl_texture_get_width (texture); + surface->height = cogl_texture_get_height (texture); + } + cogl_object_ref (texture); + } + + surface->journal = NULL; + + surface->last_clip = NULL; + + surface->n_clip_updates_per_frame = 0; + + surface->path_is_rectangle = FALSE; + surface->user_path = NULL; + + _cairo_surface_init (&surface->base, + &_cairo_cogl_surface_backend, + &dev->base, + content, + FALSE); /* is_vector */ + + return &surface->base; +} + +cairo_surface_t * +cairo_cogl_surface_create_for_fb (cairo_device_t *abstract_device, + CoglFramebuffer *framebuffer, + cairo_content_t content) +{ + cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device; + + if (abstract_device == NULL) + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR); + + if (abstract_device->status) + return _cairo_surface_create_in_error (abstract_device->status); + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + return _cairo_cogl_surface_create_full (dev, + content, + framebuffer, + NULL); +} +slim_hidden_def (cairo_cogl_surface_create_for_fb); + +cairo_surface_t * +cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device, + cairo_content_t content, + int width, int height) +{ + CoglFramebuffer *fb; + CoglTextureComponents components; + CoglError *error = NULL; + cairo_surface_t *surface; + cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device; + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + /* We don't yet have a way to set the components of a framebuffer */ + components = get_components_from_cairo_content (content); + + fb = cogl_onscreen_new (dev->cogl_context, width, height); + + if (unlikely (!cogl_framebuffer_allocate (fb, &error))) { + g_warning ("Could not allocate framebuffer for onscreen " + "surface: %s", error->message); + cogl_error_free (error); + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR); + } + cogl_framebuffer_orthographic (fb, 0, 0, width, height, -1, 100); + + surface = cairo_cogl_surface_create_for_fb (abstract_device, + fb, + content); + + /* The surface will take a reference on the framebuffer */ + cogl_object_unref (fb); + + return surface; +} +slim_hidden_def (cairo_cogl_onscreen_surface_create); + +cairo_surface_t * +cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device, + cairo_content_t content, + int width, int height) +{ + CoglFramebuffer *fb; + CoglTexture *tex; + CoglError *error = NULL; + cairo_surface_t *surface; + cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device; + int tex_width = width; + int tex_height = height; + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + /* If we cannot use an NPOT texture, allocate the texture in power + * of two dimensions instead */ + if (!dev->has_npots) { + tex_width = (int)pow (2, ceil (log2 (tex_width))); + tex_height = (int)pow (2, ceil (log2 (tex_height))); + } + + tex = cogl_texture_2d_new_with_size (dev->cogl_context, + tex_width, tex_height); + cogl_texture_set_components (tex, + get_components_from_cairo_content (content)); + fb = cogl_offscreen_new_with_texture (tex); + + if (unlikely (!cogl_framebuffer_allocate (fb, &error))) { + g_warning ("Could not allocate framebuffer for offscreen " + "surface: %s", error->message); + cogl_error_free (error); + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR); + } + cogl_framebuffer_orthographic (fb, 0, 0, + tex_width, tex_height, + -1, 100); + + /* The framebuffer will take a reference on the texture */ + cogl_object_unref (tex); + + surface = cairo_cogl_surface_create_for_fb (abstract_device, + fb, + content); + + /* The surface will take a reference on the framebuffer */ + cogl_object_unref (fb); + + ((cairo_cogl_surface_t *)surface)->width = width; + ((cairo_cogl_surface_t *)surface)->height = height; + + return surface; +} +slim_hidden_def (cairo_cogl_offscreen_surface_create); + +CoglFramebuffer * +cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface) +{ + cairo_cogl_surface_t *surface; + + if (abstract_surface->backend != &_cairo_cogl_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + surface = (cairo_cogl_surface_t *) abstract_surface; + + return surface->framebuffer; +} +slim_hidden_def (cairo_cogl_surface_get_framebuffer); + +CoglTexture * +cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface) +{ + cairo_cogl_surface_t *surface; + + if (abstract_surface->backend != &_cairo_cogl_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + surface = (cairo_cogl_surface_t *) abstract_surface; + + return surface->texture; +} +slim_hidden_def (cairo_cogl_surface_get_texture); + +static cairo_status_t +_cairo_cogl_device_flush (void *device) +{ + cairo_status_t status; + cairo_cogl_device_t *dev = device; + + status = cairo_device_acquire (device); + if (unlikely (status)) + return status; + + /* XXX: we don't need to flush Cogl here, we just need to flush + * any batching we do of compositing primitives. */ + + if (dev->buffer_stack && dev->buffer_stack_offset) { + cogl_buffer_unmap (dev->buffer_stack); + cogl_object_unref (dev->buffer_stack); + dev->buffer_stack = NULL; + } + + cairo_device_release (device); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_cogl_device_finish (void *device) +{ + cairo_status_t status; + cairo_cogl_device_t *dev = device; + int i, j; + + status = cairo_device_acquire (device); + if (unlikely (status)) + return; + + /* XXX: Drop references to external resources */ + + _cairo_cache_fini (&dev->linear_cache); + _cairo_cache_fini (&dev->path_fill_prim_cache); + _cairo_cache_fini (&dev->path_stroke_prim_cache); + + _cairo_freelist_fini (&dev->path_fill_meta_freelist); + _cairo_freelist_fini (&dev->path_stroke_meta_freelist); + + if (dev->buffer_stack && dev->buffer_stack_offset) { + cogl_buffer_unmap (dev->buffer_stack); + cogl_object_unref (dev->buffer_stack); + dev->buffer_stack = NULL; + } + + for (i = 0; i < CAIRO_OPERATOR_SATURATE; i++) + for (j = 0; j < CAIRO_COGL_TEMPLATE_TYPE_COUNT; j++) + if (dev->template_pipelines[i][j] != NULL) { + cogl_object_unref (dev->template_pipelines[i][j]); + dev->template_pipelines[i][j] = NULL; + } + + cogl_object_unref (dev->cogl_context); + + cairo_device_release (device); +} + +static void +_cairo_cogl_device_destroy (void *device) +{ + cairo_cogl_device_t *dev = device; + + g_free (dev); +} + +static const cairo_device_backend_t _cairo_cogl_device_backend = { + CAIRO_DEVICE_TYPE_COGL, + + NULL, /* lock */ + NULL, /* unlock */ + + _cairo_cogl_device_flush, + _cairo_cogl_device_finish, + _cairo_cogl_device_destroy, +}; + +cairo_device_t * +cairo_cogl_device_create (CoglContext *cogl_context) +{ + cairo_cogl_device_t *dev = g_new (cairo_cogl_device_t, 1); + cairo_status_t status; + + dev->cogl_context = cogl_object_ref (cogl_context); + + dev->has_npots = + cogl_has_features (cogl_context, + COGL_FEATURE_ID_TEXTURE_NPOT_BASIC, + COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT, + 0); + + dev->has_mirrored_repeat = + cogl_has_feature (cogl_context, + COGL_FEATURE_ID_MIRRORED_REPEAT); + + dev->buffer_stack = NULL; + dev->buffer_stack_size = 4096; + + /* Set all template pipelines to NULL */ + memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines)); + + status = _cairo_cache_init (&dev->linear_cache, + _cairo_cogl_linear_gradient_equal, + NULL, + (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy, + CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE); + if (unlikely (status)) { + g_free (dev); + return _cairo_device_create_in_error (status); + } + + status = _cairo_cache_init (&dev->path_fill_prim_cache, + _cairo_cogl_path_fill_meta_equal, + NULL, + (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy, + CAIRO_COGL_PATH_META_CACHE_SIZE); + + status = _cairo_cache_init (&dev->path_stroke_prim_cache, + _cairo_cogl_path_stroke_meta_equal, + NULL, + (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy, + CAIRO_COGL_PATH_META_CACHE_SIZE); + + _cairo_freelist_init (&dev->path_fill_meta_freelist, + sizeof(cairo_cogl_path_fill_meta_t)); + _cairo_freelist_init (&dev->path_stroke_meta_freelist, + sizeof(cairo_cogl_path_stroke_meta_t)); + + _cairo_device_init (&dev->base, &_cairo_cogl_device_backend); + return &dev->base; +} +slim_hidden_def (cairo_cogl_device_create); + +cairo_status_t +cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface) +{ + cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; + + if (abstract_surface->backend != &_cairo_cogl_surface_backend) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + cairo_surface_flush (abstract_surface); + + if (surface->framebuffer) + if (cogl_is_onscreen (surface->framebuffer)) + cogl_onscreen_swap_buffers (surface->framebuffer); + + //g_print ("n_clip_updates_per_frame = %d\n", surface->n_clip_updates_per_frame); + surface->n_clip_updates_per_frame = 0; + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_cogl_surface_end_frame); + +cairo_status_t +cairo_cogl_surface_synchronize (cairo_surface_t *abstract_surface) +{ + cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface; + + if (abstract_surface->backend != &_cairo_cogl_surface_backend) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + if (surface->framebuffer) + cogl_framebuffer_finish (surface->framebuffer); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_cogl_surface_synchronize); diff --git a/gfx/cairo/cairo/src/cairo-cogl.h b/gfx/cairo/cairo/src/cairo-cogl.h new file mode 100644 index 000000000000..b7a5b8e5c016 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cogl.h @@ -0,0 +1,86 @@ +/* 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 Mozilla Corporation. + * + * Contributor(s): + * Robert Bragg + */ + +#ifndef CAIRO_COGL_H +#define CAIRO_COGL_H + +#include "cairo.h" + +#if CAIRO_HAS_COGL_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_device_t * +cairo_cogl_device_create (CoglContext *context); + +cairo_public cairo_surface_t * +cairo_cogl_onscreen_surface_create (cairo_device_t *device, + cairo_content_t content, + int width, int height); + +cairo_public cairo_surface_t * +cairo_cogl_offscreen_surface_create (cairo_device_t *device, + cairo_content_t content, + int width, int height); + +cairo_public cairo_surface_t * +cairo_cogl_surface_create_for_fb (cairo_device_t *device, + CoglFramebuffer *framebuffer, + cairo_content_t content); + +cairo_public CoglFramebuffer * +cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface); + +/* If NPOT textures are not supported, the contents of interests may + * only be in the lowest-coordinate corner of the texture obtained from + * this function */ +cairo_public CoglTexture * +cairo_cogl_surface_get_texture (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_cogl_surface_end_frame (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_cogl_surface_synchronize (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_COGL_SURFACE*/ +# error Cairo was not compiled with support for the Cogl backend +#endif /* CAIRO_HAS_COGL_SURFACE*/ + +#endif /* CAIRO_COGL_H */ diff --git a/gfx/cairo/cairo/src/cairo-color.c b/gfx/cairo/cairo/src/cairo-color.c index d20fea4d2f3c..c2a11a177380 100644 --- a/gfx/cairo/cairo/src/cairo-color.c +++ b/gfx/cairo/cairo/src/cairo-color.c @@ -77,32 +77,14 @@ _cairo_stock_color (cairo_stock_t stock) } } -void -_cairo_color_init (cairo_color_t *color) -{ - *color = cairo_color_white; -} - -void -_cairo_color_init_rgb (cairo_color_t *color, - double red, double green, double blue) -{ - _cairo_color_init_rgba (color, red, green, blue, 1.0); -} - /* Convert a double in [0.0, 1.0] to an integer in [0, 65535] - * The conversion is designed to divide the input range into 65536 - * equally-sized regions. This is achieved by multiplying by 65536 and - * then special-casing the result of an input value of 1.0 so that it - * maps to 65535 instead of 65536. + * The conversion is designed to choose the integer i such that + * i / 65535.0 is as close as possible to the input value. */ uint16_t _cairo_color_double_to_short (double d) { - uint32_t i; - i = (uint32_t) (d * 65536); - i -= (i >> 16); - return i; + return d * 65535.0 + 0.5; } static void diff --git a/gfx/cairo/cairo/src/cairo-combsort-private.h b/gfx/cairo/cairo/src/cairo-combsort-inline.h similarity index 80% rename from gfx/cairo/cairo/src/cairo-combsort-private.h rename to gfx/cairo/cairo/src/cairo-combsort-inline.h index bb7abb477697..d359faeb5b86 100644 --- a/gfx/cairo/cairo/src/cairo-combsort-private.h +++ b/gfx/cairo/cairo/src/cairo-combsort-inline.h @@ -69,3 +69,26 @@ NAME (TYPE *base, unsigned int nmemb) \ } \ } while (swapped); \ } + +#define CAIRO_COMBSORT_DECLARE_WITH_DATA(NAME, TYPE, CMP) \ +static void \ +NAME (TYPE *base, unsigned int nmemb, void *data) \ +{ \ + unsigned int gap = nmemb; \ + unsigned int i, j; \ + int swapped; \ + do { \ + gap = _cairo_combsort_newgap (gap); \ + swapped = gap > 1; \ + for (i = 0; i < nmemb-gap ; i++) { \ + j = i + gap; \ + if (CMP (base[i], base[j], data) > 0 ) { \ + TYPE tmp; \ + tmp = base[i]; \ + base[i] = base[j]; \ + base[j] = tmp; \ + swapped = 1; \ + } \ + } \ + } while (swapped); \ +} diff --git a/gfx/cairo/cairo/src/cairo-compiler-private.h b/gfx/cairo/cairo/src/cairo-compiler-private.h index 18dc661bda29..4562bea66631 100644 --- a/gfx/cairo/cairo/src/cairo-compiler-private.h +++ b/gfx/cairo/cairo/src/cairo-compiler-private.h @@ -69,7 +69,7 @@ * call sites. The macro works by renaming `f' to an internal name * in the symbol table and hiding that. As far as cairo internal * calls are concerned they're calling a library internal function - * and thus don't need to bounce via the PLT. + * and thus don't need to bounce via the procedure linkage table (PLT). * * slim_hidden_def(f) * @@ -114,7 +114,9 @@ /* slim_internal.h */ #define CAIRO_HAS_HIDDEN_SYMBOLS 1 -#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) && !defined(__sun) +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && \ + (defined(__ELF__) || defined(__APPLE__)) && \ + !defined(__sun) #define cairo_private_no_warn __attribute__((__visibility__("hidden"))) #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) #define cairo_private_no_warn __hidden @@ -181,70 +183,37 @@ #endif #if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) -#define _CAIRO_BOOLEAN_EXPR(expr) \ - __extension__ ({ \ - int _cairo_boolean_var_; \ - if (expr) \ - _cairo_boolean_var_ = 1; \ - else \ - _cairo_boolean_var_ = 0; \ - _cairo_boolean_var_; \ -}) -#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1)) -#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0)) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#define unlikely(expr) (__builtin_expect (!!(expr), 0)) #else #define likely(expr) (expr) #define unlikely(expr) (expr) #endif -/* - * clang-cl supports __attribute__, but MSVC doesn't, so we need to make sure - * we do this if not GNUC but also if not clang either. - */ -#if !defined(__GNUC__) && !defined(__clang__) +#ifndef __GNUC__ #undef __attribute__ #define __attribute__(x) #endif #if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) -#define snprintf _snprintf -#define popen _popen -#define pclose _pclose +#define access _access +#define fdopen _fdopen #define hypot _hypot +#define pclose _pclose +#define popen _popen +#define strdup _strdup +#define unlink _unlink +#if _MSC_VER < 1900 + #define vsnprintf _vsnprintf + #define snprintf _snprintf +#endif #endif #ifdef _MSC_VER - -#define HAVE_WIN32_ATOMIC_PRIMITIVES 1 - #ifndef __cplusplus #undef inline #define inline __inline #endif - -/* there are currently linkage problems that arise when trying to include intrin.h in c++: - * D:\sdks\v7.0\include\winnt.h(3674) : error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed - * so avoid defining ffs in c++ code for now */ -#ifndef __cplusplus -/* Add a definition of ffs */ -#include -#pragma intrinsic(_BitScanForward) -static __forceinline int -ffs (int x) -{ - unsigned long i; - - if (_BitScanForward(&i, x) != 0) - return i + 1; - - return 0; -} -#endif - -#elif defined(__WIN32__) && defined(__GNUC__) - -#define ffs(x) __builtin_ffs(x) - #endif #if defined(_MSC_VER) && defined(_M_IX86) diff --git a/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h b/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h index 8c3c5abcc7a9..fd7728995de5 100644 --- a/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h +++ b/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h @@ -38,6 +38,8 @@ #define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H #include "cairo-types-private.h" +#include "cairo-error-private.h" +#include "cairo-pattern-private.h" CAIRO_BEGIN_DECLS @@ -51,55 +53,107 @@ CAIRO_BEGIN_DECLS * */ struct _cairo_composite_rectangles { + cairo_surface_t *surface; + cairo_operator_t op; + cairo_rectangle_int_t source; cairo_rectangle_int_t mask; - cairo_rectangle_int_t bounded; /* dst */ - cairo_rectangle_int_t unbounded; /* clip */ + cairo_rectangle_int_t destination; + + cairo_rectangle_int_t bounded; /* source? IN mask? IN unbounded */ + cairo_rectangle_int_t unbounded; /* destination IN clip */ uint32_t is_bounded; + + cairo_rectangle_int_t source_sample_area; + cairo_rectangle_int_t mask_sample_area; + + cairo_pattern_union_t source_pattern; + cairo_pattern_union_t mask_pattern; + const cairo_pattern_t *original_source_pattern; + const cairo_pattern_t *original_mask_pattern; + + cairo_clip_t *clip; /* clip will be reduced to the minimal container */ }; cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip); + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip); + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_clip_t *clip); + const cairo_path_fixed_t *path, + const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_boxes_t *boxes, + const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_polygon_t *polygon, + const cairo_clip_t *clip); cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, - cairo_clip_t *clip, + const cairo_clip_t *clip, cairo_bool_t *overlap); +cairo_private cairo_int_status_t +_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents, + const cairo_box_t *box); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents, + const cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite, + cairo_boxes_t *damage); + +cairo_private void +_cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents); + +CAIRO_END_DECLS + #endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-composite-rectangles.c b/gfx/cairo/cairo/src/cairo-composite-rectangles.c index 7f6484339d55..f102eddbce90 100644 --- a/gfx/cairo/cairo/src/cairo-composite-rectangles.c +++ b/gfx/cairo/cairo/src/cairo-composite-rectangles.c @@ -35,153 +35,412 @@ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-error-private.h" #include "cairo-composite-rectangles-private.h" +#include "cairo-pattern-private.h" /* A collection of routines to facilitate writing compositors. */ +void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents) +{ + _cairo_clip_destroy (extents->clip); +} + +static void +_cairo_composite_reduce_pattern (const cairo_pattern_t *src, + cairo_pattern_union_t *dst) +{ + int tx, ty; + + _cairo_pattern_init_static_copy (&dst->base, src); + if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID) + return; + + dst->base.filter = _cairo_pattern_analyze_filter (&dst->base); + + tx = ty = 0; + if (_cairo_matrix_is_pixman_translation (&dst->base.matrix, + dst->base.filter, + &tx, &ty)) + { + dst->base.matrix.x0 = tx; + dst->base.matrix.y0 = ty; + } +} + static inline cairo_bool_t _cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - extents->unbounded = *surface_extents; + if (_cairo_clip_is_all_clipped (clip)) + return FALSE; - if (clip != NULL) { - const cairo_rectangle_int_t *clip_extents; + extents->surface = surface; + extents->op = op; - clip_extents = _cairo_clip_get_extents (clip); - if (clip_extents == NULL) - return FALSE; + _cairo_surface_get_extents (surface, &extents->destination); + extents->clip = NULL; - if (! _cairo_rectangle_intersect (&extents->unbounded, clip_extents)) - return FALSE; - } + extents->unbounded = extents->destination; + if (clip && ! _cairo_rectangle_intersect (&extents->unbounded, + _cairo_clip_get_extents (clip))) + return FALSE; extents->bounded = extents->unbounded; extents->is_bounded = _cairo_operator_bounded_by_either (op); - _cairo_pattern_get_extents (source, &extents->source); + extents->original_source_pattern = source; + _cairo_composite_reduce_pattern (source, &extents->source_pattern); + + _cairo_pattern_get_extents (&extents->source_pattern.base, + &extents->source, + surface->is_vector); if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) { if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source)) return FALSE; } + extents->original_mask_pattern = NULL; + extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID; + extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */ + extents->mask_pattern.solid.color.alpha_short = 0xffff; + return TRUE; } cairo_int_status_t _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } - extents->mask = extents->bounded; + extents->mask = extents->destination; + + extents->clip = _cairo_clip_reduce_for_composite (clip, extents); + if (_cairo_clip_is_all_clipped (extents->clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (! _cairo_rectangle_intersect (&extents->unbounded, + _cairo_clip_get_extents (extents->clip))) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) + _cairo_pattern_sampled_area (&extents->source_pattern.base, + &extents->bounded, + &extents->source_sample_area); + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents) +_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents, + const cairo_clip_t *clip) { - cairo_bool_t ret; - - ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask); - if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) + if ((!_cairo_rectangle_intersect (&extents->bounded, &extents->mask)) && + (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)) return CAIRO_INT_STATUS_NOTHING_TO_DO; + if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { + extents->unbounded = extents->bounded; + } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { + if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + extents->clip = _cairo_clip_reduce_for_composite (clip, extents); + if (_cairo_clip_is_all_clipped (extents->clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (! _cairo_rectangle_intersect (&extents->unbounded, + _cairo_clip_get_extents (extents->clip))) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (! _cairo_rectangle_intersect (&extents->bounded, + _cairo_clip_get_extents (extents->clip)) && + extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) + _cairo_pattern_sampled_area (&extents->source_pattern.base, + &extents->bounded, + &extents->source_sample_area); + if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) { + _cairo_pattern_sampled_area (&extents->mask_pattern.base, + &extents->bounded, + &extents->mask_sample_area); + if (extents->mask_sample_area.width == 0 || + extents->mask_sample_area.height == 0) { + _cairo_composite_rectangles_fini (extents); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + } + return CAIRO_STATUS_SUCCESS; } +cairo_int_status_t +_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents, + const cairo_box_t *box) +{ + cairo_rectangle_int_t rect; + cairo_clip_t *clip; + + _cairo_box_round_to_rectangle (box, &rect); + if (rect.x == extents->source.x && + rect.y == extents->source.y && + rect.width == extents->source.width && + rect.height == extents->source.height) + { + return CAIRO_INT_STATUS_SUCCESS; + } + + _cairo_rectangle_intersect (&extents->source, &rect); + + rect = extents->bounded; + if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) && + extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (rect.width == extents->bounded.width && + rect.height == extents->bounded.height) + return CAIRO_INT_STATUS_SUCCESS; + + if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { + extents->unbounded = extents->bounded; + } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { + if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + clip = extents->clip; + extents->clip = _cairo_clip_reduce_for_composite (clip, extents); + if (clip != extents->clip) + _cairo_clip_destroy (clip); + + if (_cairo_clip_is_all_clipped (extents->clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (! _cairo_rectangle_intersect (&extents->unbounded, + _cairo_clip_get_extents (extents->clip))) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) + _cairo_pattern_sampled_area (&extents->source_pattern.base, + &extents->bounded, + &extents->source_sample_area); + if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) { + _cairo_pattern_sampled_area (&extents->mask_pattern.base, + &extents->bounded, + &extents->mask_sample_area); + if (extents->mask_sample_area.width == 0 || + extents->mask_sample_area.height == 0) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents, + const cairo_box_t *box) +{ + cairo_rectangle_int_t mask; + cairo_clip_t *clip; + + _cairo_box_round_to_rectangle (box, &mask); + if (mask.x == extents->mask.x && + mask.y == extents->mask.y && + mask.width == extents->mask.width && + mask.height == extents->mask.height) + { + return CAIRO_INT_STATUS_SUCCESS; + } + + _cairo_rectangle_intersect (&extents->mask, &mask); + + mask = extents->bounded; + if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) && + extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (mask.width == extents->bounded.width && + mask.height == extents->bounded.height) + return CAIRO_INT_STATUS_SUCCESS; + + if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) { + extents->unbounded = extents->bounded; + } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) { + if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + clip = extents->clip; + extents->clip = _cairo_clip_reduce_for_composite (clip, extents); + if (clip != extents->clip) + _cairo_clip_destroy (clip); + + if (_cairo_clip_is_all_clipped (extents->clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (! _cairo_rectangle_intersect (&extents->unbounded, + _cairo_clip_get_extents (extents->clip))) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) + _cairo_pattern_sampled_area (&extents->source_pattern.base, + &extents->bounded, + &extents->source_sample_area); + if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) { + _cairo_pattern_sampled_area (&extents->mask_pattern.base, + &extents->bounded, + &extents->mask_sample_area); + if (extents->mask_sample_area.width == 0 || + extents->mask_sample_area.height == 0) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + cairo_int_status_t _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } - _cairo_pattern_get_extents (mask, &extents->mask); + extents->original_mask_pattern = mask; + _cairo_composite_reduce_pattern (mask, &extents->mask_pattern); + _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask, surface->is_vector); - return _cairo_composite_rectangles_intersect (extents); + return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, - cairo_clip_t *clip) + const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask); + _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, surface->is_vector, &extents->mask); - return _cairo_composite_rectangles_intersect (extents); + return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_clip_t *clip) + const cairo_path_fixed_t *path, + const cairo_clip_t *clip) { if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } _cairo_path_fixed_approximate_fill_extents (path, &extents->mask); - return _cairo_composite_rectangles_intersect (extents); + return _cairo_composite_rectangles_intersect (extents, clip); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_polygon_t *polygon, + const cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); + return _cairo_composite_rectangles_intersect (extents, clip); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_boxes_t *boxes, + const cairo_clip_t *clip) +{ + cairo_box_t box; + + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + _cairo_boxes_extents (boxes, &box); + _cairo_box_round_to_rectangle (&box, &extents->mask); + return _cairo_composite_rectangles_intersect (extents, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, - cairo_clip_t *clip, + const cairo_clip_t *clip, cairo_bool_t *overlap) { cairo_status_t status; - if (! _cairo_composite_rectangles_init (extents, - surface_extents, - op, source, clip)) - { + if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) return CAIRO_INT_STATUS_NOTHING_TO_DO; + + /* Computing the exact bbox and the overlap is expensive. + * First perform a cheap test to see if the glyphs are all clipped out. + */ + if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK && + _cairo_scaled_font_glyph_approximate_extents (scaled_font, + glyphs, num_glyphs, + &extents->mask)) + { + if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; } status = _cairo_scaled_font_glyph_device_extents (scaled_font, @@ -191,5 +450,50 @@ _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *exten if (unlikely (status)) return status; - return _cairo_composite_rectangles_intersect (extents); + if (overlap && *overlap && + scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE && + _cairo_pattern_is_opaque_solid (&extents->source_pattern.base)) + { + *overlap = FALSE; + } + + return _cairo_composite_rectangles_intersect (extents, clip); +} + +cairo_bool_t +_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite, + cairo_clip_t *clip) +{ + cairo_rectangle_int_t extents; + cairo_box_t box; + + if (clip == NULL) + return TRUE; + + extents = composite->destination; + if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) + _cairo_rectangle_intersect (&extents, &composite->source); + if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) + _cairo_rectangle_intersect (&extents, &composite->mask); + + _cairo_box_from_rectangle (&box, &extents); + return _cairo_clip_contains_box (clip, &box); +} + +cairo_int_status_t +_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite, + cairo_boxes_t *damage) +{ + cairo_int_status_t status; + int n; + + for (n = 0; n < composite->clip->num_boxes; n++) { + status = _cairo_boxes_add (damage, + CAIRO_ANTIALIAS_NONE, + &composite->clip->boxes[n]); + if (unlikely (status)) + return status; + } + + return CAIRO_INT_STATUS_SUCCESS; } diff --git a/gfx/cairo/cairo/src/cairo-compositor-private.h b/gfx/cairo/cairo/src/cairo-compositor-private.h new file mode 100644 index 000000000000..019972333f34 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-compositor-private.h @@ -0,0 +1,365 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_COMPOSITOR_PRIVATE_H +#define CAIRO_COMPOSITOR_PRIVATE_H + +#include "cairo-composite-rectangles-private.h" + +CAIRO_BEGIN_DECLS + +typedef struct { + cairo_scaled_font_t *font; + cairo_glyph_t *glyphs; + int num_glyphs; + cairo_bool_t use_mask; + cairo_rectangle_int_t extents; +} cairo_composite_glyphs_info_t; + +struct cairo_compositor { + const cairo_compositor_t *delegate; + + cairo_warn cairo_int_status_t + (*paint) (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents); + + cairo_warn cairo_int_status_t + (*mask) (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents); + + cairo_warn cairo_int_status_t + (*stroke) (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + 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_warn cairo_int_status_t + (*fill) (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias); + + cairo_warn cairo_int_status_t + (*glyphs) (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap); +}; + +struct cairo_mask_compositor { + cairo_compositor_t base; + + cairo_int_status_t (*acquire) (void *surface); + cairo_int_status_t (*release) (void *surface); + + cairo_int_status_t (*set_clip_region) (void *surface, + cairo_region_t *clip_region); + + cairo_surface_t * (*pattern_to_surface) (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_int_status_t (*draw_image_boxes) (void *surface, + cairo_image_surface_t *image, + cairo_boxes_t *boxes, + int dx, int dy); + + cairo_int_status_t (*copy_boxes) (void *surface, + cairo_surface_t *src, + cairo_boxes_t *boxes, + const cairo_rectangle_int_t *extents, + int dx, int dy); + + cairo_int_status_t + (*fill_rectangles) (void *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rectangles, + int num_rects); + + cairo_int_status_t + (*fill_boxes) (void *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes); + + cairo_int_status_t + (*check_composite) (const cairo_composite_rectangles_t *extents); + + cairo_int_status_t + (*composite) (void *dst, + cairo_operator_t op, + 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 + (*composite_boxes) (void *surface, + cairo_operator_t op, + cairo_surface_t *source, + cairo_surface_t *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_int_status_t + (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs); + cairo_int_status_t + (*composite_glyphs) (void *surface, + 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); +}; + +struct cairo_traps_compositor { + cairo_compositor_t base; + + cairo_int_status_t + (*acquire) (void *surface); + + cairo_int_status_t + (*release) (void *surface); + + cairo_int_status_t + (*set_clip_region) (void *surface, + cairo_region_t *clip_region); + + cairo_surface_t * + (*pattern_to_surface) (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_int_status_t (*draw_image_boxes) (void *surface, + cairo_image_surface_t *image, + cairo_boxes_t *boxes, + int dx, int dy); + + cairo_int_status_t (*copy_boxes) (void *surface, + cairo_surface_t *src, + cairo_boxes_t *boxes, + const cairo_rectangle_int_t *extents, + int dx, int dy); + + cairo_int_status_t + (*fill_boxes) (void *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes); + + cairo_int_status_t + (*check_composite) (const cairo_composite_rectangles_t *extents); + + cairo_int_status_t + (*composite) (void *dst, + cairo_operator_t op, + 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 + (*lerp) (void *_dst, + 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_int_status_t + (*composite_boxes) (void *surface, + cairo_operator_t op, + cairo_surface_t *source, + cairo_surface_t *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_int_status_t + (*composite_traps) (void *dst, + cairo_operator_t op, + cairo_surface_t *source, + 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_int_status_t + (*composite_tristrip) (void *dst, + cairo_operator_t op, + cairo_surface_t *source, + 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 *tristrip); + + cairo_int_status_t + (*check_composite_glyphs) (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs); + cairo_int_status_t + (*composite_glyphs) (void *surface, + 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 extern const cairo_compositor_t __cairo_no_compositor; +cairo_private extern const cairo_compositor_t _cairo_fallback_compositor; + +cairo_private void +_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor, + const cairo_compositor_t *delegate); + +cairo_private void +_cairo_shape_mask_compositor_init (cairo_compositor_t *compositor, + const cairo_compositor_t *delegate); + +cairo_private void +_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor, + const cairo_compositor_t *delegate); + +cairo_private cairo_int_status_t +_cairo_compositor_paint (const cairo_compositor_t *compositor, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_compositor_mask (const cairo_compositor_t *compositor, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_compositor_stroke (const cairo_compositor_t *compositor, + cairo_surface_t *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_private cairo_int_status_t +_cairo_compositor_fill (const cairo_compositor_t *compositor, + cairo_surface_t *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_private cairo_int_status_t +_cairo_compositor_glyphs (const cairo_compositor_t *compositor, + cairo_surface_t *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_END_DECLS + +#endif /* CAIRO_COMPOSITOR_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-compositor.c b/gfx/cairo/cairo/src/cairo-compositor.c new file mode 100644 index 000000000000..b31413b99646 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-compositor.c @@ -0,0 +1,268 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" +#include "cairo-damage-private.h" +#include "cairo-error-private.h" + +cairo_int_status_t +_cairo_compositor_paint (const cairo_compositor_t *compositor, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + status = _cairo_composite_rectangles_init_for_paint (&extents, surface, + op, source, + clip); + if (unlikely (status)) + return status; + + do { + while (compositor->paint == NULL) + compositor = compositor->delegate; + + status = compositor->paint (compositor, &extents); + + compositor = compositor->delegate; + } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { + TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", + __FUNCTION__, + extents.unbounded.x, extents.unbounded.y, + extents.unbounded.width, extents.unbounded.height)); + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + } + + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +cairo_int_status_t +_cairo_compositor_mask (const cairo_compositor_t *compositor, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + status = _cairo_composite_rectangles_init_for_mask (&extents, surface, + op, source, mask, + clip); + if (unlikely (status)) + return status; + + do { + while (compositor->mask == NULL) + compositor = compositor->delegate; + + status = compositor->mask (compositor, &extents); + + compositor = compositor->delegate; + } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { + TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", + __FUNCTION__, + extents.unbounded.x, extents.unbounded.y, + extents.unbounded.width, extents.unbounded.height)); + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + } + + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +cairo_int_status_t +_cairo_compositor_stroke (const cairo_compositor_t *compositor, + cairo_surface_t *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_composite_rectangles_t extents; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (_cairo_pen_vertices_needed (tolerance, style->line_width/2, ctm) <= 1) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, surface, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + do { + while (compositor->stroke == NULL) + compositor = compositor->delegate; + + status = compositor->stroke (compositor, &extents, + path, style, ctm, ctm_inverse, + tolerance, antialias); + + compositor = compositor->delegate; + } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { + TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", + __FUNCTION__, + extents.unbounded.x, extents.unbounded.y, + extents.unbounded.width, extents.unbounded.height)); + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + } + + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +cairo_int_status_t +_cairo_compositor_fill (const cairo_compositor_t *compositor, + cairo_surface_t *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_composite_rectangles_t extents; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + status = _cairo_composite_rectangles_init_for_fill (&extents, surface, + op, source, path, + clip); + if (unlikely (status)) + return status; + + do { + while (compositor->fill == NULL) + compositor = compositor->delegate; + + status = compositor->fill (compositor, &extents, + path, fill_rule, tolerance, antialias); + + compositor = compositor->delegate; + } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { + TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", + __FUNCTION__, + extents.unbounded.x, extents.unbounded.y, + extents.unbounded.width, extents.unbounded.height)); + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + } + + _cairo_composite_rectangles_fini (&extents); + + return status; +} + +cairo_int_status_t +_cairo_compositor_glyphs (const cairo_compositor_t *compositor, + cairo_surface_t *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_composite_rectangles_t extents; + cairo_bool_t overlap; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, &overlap); + if (unlikely (status)) + return status; + + do { + while (compositor->glyphs == NULL) + compositor = compositor->delegate; + + status = compositor->glyphs (compositor, &extents, + scaled_font, glyphs, num_glyphs, overlap); + + compositor = compositor->delegate; + } while (status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) { + TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n", + __FUNCTION__, + extents.unbounded.x, extents.unbounded.y, + extents.unbounded.width, extents.unbounded.height)); + surface->damage = _cairo_damage_add_rectangle (surface->damage, + &extents.unbounded); + } + + _cairo_composite_rectangles_fini (&extents); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-contour-inline.h b/gfx/cairo/cairo/src/cairo-contour-inline.h new file mode 100644 index 000000000000..7972c1ac5e08 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-contour-inline.h @@ -0,0 +1,80 @@ +/* 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_CONTOUR_INLINE_H +#define CAIRO_CONTOUR_INLINE_H + +#include "cairo-contour-private.h" + +CAIRO_BEGIN_DECLS + +static inline cairo_int_status_t +_cairo_contour_add_point (cairo_contour_t *contour, + const cairo_point_t *point) +{ + struct _cairo_contour_chain *tail = contour->tail; + + if (unlikely (tail->num_points == tail->size_points)) + return __cairo_contour_add_point (contour, point); + + tail->points[tail->num_points++] = *point; + return CAIRO_INT_STATUS_SUCCESS; +} + +static inline cairo_point_t * +_cairo_contour_first_point (cairo_contour_t *c) +{ + return &c->chain.points[0]; +} + +static inline cairo_point_t * +_cairo_contour_last_point (cairo_contour_t *c) +{ + return &c->tail->points[c->tail->num_points-1]; +} + +static inline void +_cairo_contour_remove_last_point (cairo_contour_t *contour) +{ + if (contour->chain.num_points == 0) + return; + + if (--contour->tail->num_points == 0) + __cairo_contour_remove_last_chain (contour); +} + +CAIRO_END_DECLS + +#endif /* CAIRO_CONTOUR_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-contour-private.h b/gfx/cairo/cairo/src/cairo-contour-private.h new file mode 100644 index 000000000000..1dfc46f3acd7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-contour-private.h @@ -0,0 +1,124 @@ +/* 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_CONTOUR_PRIVATE_H +#define CAIRO_CONTOUR_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-error-private.h" +#include "cairo-list-private.h" + +#include + +CAIRO_BEGIN_DECLS + +/* A contour is simply a closed chain of points that divide the infinite plane + * into inside and outside. Each contour is a simple polygon, that is it + * contains no holes or self-intersections, but maybe either concave or convex. + */ + +struct _cairo_contour_chain { + cairo_point_t *points; + int num_points, size_points; + struct _cairo_contour_chain *next; +}; + +struct _cairo_contour_iter { + cairo_point_t *point; + cairo_contour_chain_t *chain; +}; + +struct _cairo_contour { + cairo_list_t next; + int direction; + cairo_contour_chain_t chain, *tail; + + cairo_point_t embedded_points[64]; +}; + +/* Initial definition of a shape is a set of contours (some representing holes) */ +struct _cairo_shape { + cairo_list_t contours; +}; + +typedef struct _cairo_shape cairo_shape_t; + +#if 0 +cairo_private cairo_status_t +_cairo_shape_init_from_polygon (cairo_shape_t *shape, + const cairo_polygon_t *polygon); + +cairo_private cairo_status_t +_cairo_shape_reduce (cairo_shape_t *shape, double tolerance); +#endif + +cairo_private void +_cairo_contour_init (cairo_contour_t *contour, + int direction); + +cairo_private cairo_int_status_t +__cairo_contour_add_point (cairo_contour_t *contour, + const cairo_point_t *point); + +cairo_private void +_cairo_contour_simplify (cairo_contour_t *contour, double tolerance); + +cairo_private void +_cairo_contour_reverse (cairo_contour_t *contour); + +cairo_private cairo_int_status_t +_cairo_contour_add (cairo_contour_t *dst, + const cairo_contour_t *src); + +cairo_private cairo_int_status_t +_cairo_contour_add_reversed (cairo_contour_t *dst, + const cairo_contour_t *src); + +cairo_private void +__cairo_contour_remove_last_chain (cairo_contour_t *contour); + +cairo_private void +_cairo_contour_reset (cairo_contour_t *contour); + +cairo_private void +_cairo_contour_fini (cairo_contour_t *contour); + +cairo_private void +_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour); + +CAIRO_END_DECLS + +#endif /* CAIRO_CONTOUR_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-contour.c b/gfx/cairo/cairo/src/cairo-contour.c new file mode 100644 index 000000000000..9ad75bdbf8df --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-contour.c @@ -0,0 +1,453 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2008 Chris Wilson + * 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 Carl Worth + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-combsort-inline.h" +#include "cairo-contour-inline.h" +#include "cairo-contour-private.h" + +void +_cairo_contour_init (cairo_contour_t *contour, + int direction) +{ + contour->direction = direction; + contour->chain.points = contour->embedded_points; + contour->chain.next = NULL; + contour->chain.num_points = 0; + contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points); + contour->tail = &contour->chain; +} + +cairo_int_status_t +__cairo_contour_add_point (cairo_contour_t *contour, + const cairo_point_t *point) +{ + cairo_contour_chain_t *tail = contour->tail; + cairo_contour_chain_t *next; + + assert (tail->next == NULL); + + next = _cairo_malloc_ab_plus_c (tail->size_points*2, + sizeof (cairo_point_t), + sizeof (cairo_contour_chain_t)); + if (unlikely (next == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + next->size_points = tail->size_points*2; + next->num_points = 1; + next->points = (cairo_point_t *)(next+1); + next->next = NULL; + tail->next = next; + contour->tail = next; + + next->points[0] = *point; + return CAIRO_INT_STATUS_SUCCESS; +} + +static void +first_inc (cairo_contour_t *contour, + cairo_point_t **p, + cairo_contour_chain_t **chain) +{ + if (*p == (*chain)->points + (*chain)->num_points) { + assert ((*chain)->next); + *chain = (*chain)->next; + *p = &(*chain)->points[0]; + } else + ++*p; +} + +static void +last_dec (cairo_contour_t *contour, + cairo_point_t **p, + cairo_contour_chain_t **chain) +{ + if (*p == (*chain)->points) { + cairo_contour_chain_t *prev; + assert (*chain != &contour->chain); + for (prev = &contour->chain; prev->next != *chain; prev = prev->next) + ; + *chain = prev; + *p = &(*chain)->points[(*chain)->num_points-1]; + } else + --*p; +} + +void +_cairo_contour_reverse (cairo_contour_t *contour) +{ + cairo_contour_chain_t *first_chain, *last_chain; + cairo_point_t *first, *last; + + contour->direction = -contour->direction; + + if (contour->chain.num_points <= 1) + return; + + first_chain = &contour->chain; + last_chain = contour->tail; + + first = &first_chain->points[0]; + last = &last_chain->points[last_chain->num_points-1]; + + while (first != last) { + cairo_point_t p; + + p = *first; + *first = *last; + *last = p; + + first_inc (contour, &first, &first_chain); + last_dec (contour, &last, &last_chain); + } +} + +cairo_int_status_t +_cairo_contour_add (cairo_contour_t *dst, + const cairo_contour_t *src) +{ + const cairo_contour_chain_t *chain; + cairo_int_status_t status; + int i; + + for (chain = &src->chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + status = _cairo_contour_add_point (dst, &chain->points[i]); + if (unlikely (status)) + return status; + } + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static inline cairo_bool_t +iter_next (cairo_contour_iter_t *iter) +{ + if (iter->point == &iter->chain->points[iter->chain->size_points-1]) { + iter->chain = iter->chain->next; + if (iter->chain == NULL) + return FALSE; + + iter->point = &iter->chain->points[0]; + return TRUE; + } else { + iter->point++; + return TRUE; + } +} + +static cairo_bool_t +iter_equal (const cairo_contour_iter_t *i1, + const cairo_contour_iter_t *i2) +{ + return i1->chain == i2->chain && i1->point == i2->point; +} + +static void +iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour) +{ + iter->chain = &contour->chain; + iter->point = &contour->chain.points[0]; +} + +static void +iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour) +{ + iter->chain = contour->tail; + iter->point = &contour->tail->points[contour->tail->num_points-1]; +} + +static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour, + const cairo_contour_chain_t *chain) +{ + const cairo_contour_chain_t *prev; + + if (chain == &contour->chain) + return NULL; + + for (prev = &contour->chain; prev->next != chain; prev = prev->next) + ; + + return prev; +} + +cairo_int_status_t +_cairo_contour_add_reversed (cairo_contour_t *dst, + const cairo_contour_t *src) +{ + const cairo_contour_chain_t *last; + cairo_int_status_t status; + int i; + + if (src->chain.num_points == 0) + return CAIRO_INT_STATUS_SUCCESS; + + for (last = src->tail; last; last = prev_const_chain (src, last)) { + for (i = last->num_points-1; i >= 0; i--) { + status = _cairo_contour_add_point (dst, &last->points[i]); + if (unlikely (status)) + return status; + } + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_uint64_t +point_distance_sq (const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int32_t dx = p1->x - p2->x; + int32_t dy = p1->y - p2->y; + return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy); +} + +#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX) +#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX) + +static cairo_bool_t +_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance, + const cairo_contour_iter_t *first, + const cairo_contour_iter_t *last) +{ + cairo_contour_iter_t iter, furthest; + uint64_t max_error; + int x0, y0; + int nx, ny; + int count; + + iter = *first; + iter_next (&iter); + if (iter_equal (&iter, last)) + return FALSE; + + x0 = first->point->x; + y0 = first->point->y; + nx = last->point->y - y0; + ny = x0 - last->point->x; + + count = 0; + max_error = 0; + do { + cairo_point_t *p = iter.point; + if (! DELETED(p)) { + uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y); + if (d * d > max_error) { + max_error = d * d; + furthest = iter; + } + count++; + } + iter_next (&iter); + } while (! iter_equal (&iter, last)); + if (count == 0) + return FALSE; + + if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) { + cairo_bool_t simplified; + + simplified = FALSE; + simplified |= _cairo_contour_simplify_chain (contour, tolerance, + first, &furthest); + simplified |= _cairo_contour_simplify_chain (contour, tolerance, + &furthest, last); + return simplified; + } else { + iter = *first; + iter_next (&iter); + do { + MARK_DELETED (iter.point); + iter_next (&iter); + } while (! iter_equal (&iter, last)); + + return TRUE; + } +} + +void +_cairo_contour_simplify (cairo_contour_t *contour, double tolerance) +{ + cairo_contour_chain_t *chain; + cairo_point_t *last = NULL; + cairo_contour_iter_t iter, furthest; + cairo_bool_t simplified; + uint64_t max = 0; + int i; + + if (contour->chain.num_points <= 2) + return; + + tolerance = tolerance * CAIRO_FIXED_ONE; + tolerance *= tolerance; + + /* stage 1: vertex reduction */ + for (chain = &contour->chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + if (last == NULL || + point_distance_sq (last, &chain->points[i]) > tolerance) { + last = &chain->points[i]; + } else { + MARK_DELETED (&chain->points[i]); + } + } + } + + /* stage2: polygon simplification using Douglas-Peucker */ + do { + last = &contour->chain.points[0]; + iter_init (&furthest, contour); + max = 0; + for (chain = &contour->chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + uint64_t d; + + if (DELETED (&chain->points[i])) + continue; + + d = point_distance_sq (last, &chain->points[i]); + if (d > max) { + furthest.chain = chain; + furthest.point = &chain->points[i]; + max = d; + } + } + } + assert (max); + + simplified = FALSE; + iter_init (&iter, contour); + simplified |= _cairo_contour_simplify_chain (contour, tolerance, + &iter, &furthest); + + iter_init_last (&iter, contour); + if (! iter_equal (&furthest, &iter)) + simplified |= _cairo_contour_simplify_chain (contour, tolerance, + &furthest, &iter); + } while (simplified); + + iter_init (&iter, contour); + for (chain = &contour->chain; chain; chain = chain->next) { + int num_points = chain->num_points; + chain->num_points = 0; + for (i = 0; i < num_points; i++) { + if (! DELETED(&chain->points[i])) { + if (iter.point != &chain->points[i]) + *iter.point = chain->points[i]; + iter.chain->num_points++; + iter_next (&iter); + } + } + } + + if (iter.chain) { + cairo_contour_chain_t *next; + + for (chain = iter.chain->next; chain; chain = next) { + next = chain->next; + free (chain); + } + + iter.chain->next = NULL; + contour->tail = iter.chain; + } +} + +void +_cairo_contour_reset (cairo_contour_t *contour) +{ + _cairo_contour_fini (contour); + _cairo_contour_init (contour, contour->direction); +} + +void +_cairo_contour_fini (cairo_contour_t *contour) +{ + cairo_contour_chain_t *chain, *next; + + for (chain = contour->chain.next; chain; chain = next) { + next = chain->next; + free (chain); + } +} + +void +_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour) +{ + cairo_contour_chain_t *chain; + int num_points, size_points; + int i; + + num_points = 0; + size_points = 0; + for (chain = &contour->chain; chain; chain = chain->next) { + num_points += chain->num_points; + size_points += chain->size_points; + } + + fprintf (file, "contour: direction=%d, num_points=%d / %d\n", + contour->direction, num_points, size_points); + + num_points = 0; + for (chain = &contour->chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + fprintf (file, " [%d] = (%f, %f)\n", + num_points++, + _cairo_fixed_to_double (chain->points[i].x), + _cairo_fixed_to_double (chain->points[i].y)); + } + } +} + +void +__cairo_contour_remove_last_chain (cairo_contour_t *contour) +{ + cairo_contour_chain_t *chain; + + if (contour->tail == &contour->chain) + return; + + for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next) + ; + free (contour->tail); + contour->tail = chain; + chain->next = NULL; +} diff --git a/gfx/cairo/cairo/src/cairo-d2d-private-fx.h b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h deleted file mode 100644 index 1756c57f6192..000000000000 --- a/gfx/cairo/cairo/src/cairo-d2d-private-fx.h +++ /dev/null @@ -1,1164 +0,0 @@ -#if 0 -// -// FX Version: fx_4_0 -// Child effect (requires effect pool): false -// -// 1 local buffer(s) -// -cbuffer cb0 -{ - float4 QuadDesc; // Offset: 0, size: 16 - float4 TexCoords; // Offset: 16, size: 16 - float4 TextColor; // Offset: 32, size: 16 -} - -// -// 3 local object(s) -// -Texture2D tex; -BlendState bTextBlend -{ - AlphaToCoverageEnable = bool(FALSE /* 0 */); - BlendEnable[0] = bool(TRUE /* 1 */); - SrcBlend[0] = uint(SRC1_COLOR /* 16 */); - DestBlend[0] = uint(INV_SRC1_COLOR /* 17 */); - BlendOp[0] = uint(ADD /* 1 */); - SrcBlendAlpha[0] = uint(SRC1_ALPHA /* 18 */); - DestBlendAlpha[0] = uint(INV_SRC1_ALPHA /* 19 */); - BlendOpAlpha[0] = uint(ADD /* 1 */); - RenderTargetWriteMask[0] = byte(0x0f); -}; -SamplerState sSampler -{ - Texture = tex; - AddressU = uint(CLAMP /* 3 */); - AddressV = uint(CLAMP /* 3 */); -}; - -// -// 2 technique(s) -// -technique10 SampleTexture -{ - pass P0 - { - VertexShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Buffer Definitions: - // - // cbuffer cb0 - // { - // - // float4 QuadDesc; // Offset: 0 Size: 16 - // float4 TexCoords; // Offset: 16 Size: 16 - // float4 TextColor; // Offset: 32 Size: 16 [unused] - // - // } - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // cb0 cbuffer NA NA 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // POSITION 0 xyz 0 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float xyzw - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Constant buffer to DX9 shader constant mappings: - // - // Target Reg Buffer Start Reg # of Regs Data Conversion - // ---------- ------- --------- --------- ---------------------- - // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) - // - // - // Runtime generated constant mappings: - // - // Target Reg Constant Description - // ---------- -------------------------------------------------- - // c0 Vertex Shader position offset - // - // - // Level9 shader bytecode: - // - vs_2_x - def c3, 0, 1, 0, 0 - dcl_texcoord v0 - mad oT0.xy, v0, c2.zwzw, c2 - mad r0.xy, v0, c1.zwzw, c1 - add oPos.xy, r0, c0 - mov oPos.zw, c3.xyxy - - // approximately 4 instruction slots used - vs_4_0 - dcl_constantbuffer cb0[2], immediateIndexed - dcl_input v0.xy - dcl_output_siv o0.xyzw, position - dcl_output o1.xy - mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx - mov o0.zw, l(0,0,0,1.000000) - mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx - ret - // Approximately 4 instruction slots used - - }; - GeometryShader = NULL; - PixelShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // sSampler sampler NA NA 0 1 - // tex texture float4 2d 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Target 0 xyzw 0 TARGET float xyzw - // - // - // Sampler/Resource to DX9 shader sampler mappings: - // - // Target Sampler Source Sampler Source Resource - // -------------- --------------- ---------------- - // s0 s0 t0 - // - // - // Level9 shader bytecode: - // - ps_2_x - dcl t0.xy - dcl_2d s0 - texld r0, t0, s0 - mov oC0, r0 - - // approximately 2 instruction slots used (1 texture, 1 arithmetic) - ps_4_0 - dcl_sampler s0, mode_default - dcl_resource_texture2d (float,float,float,float) t0 - dcl_input_ps linear v1.xy - dcl_output o0.xyzw - sample o0.xyzw, v1.xyxx, t0.xyzw, s0 - ret - // Approximately 2 instruction slots used - - }; - } - -} - -technique10 SampleTextTexture -{ - pass P0 - { - AB_BlendFactor = float4(0, 0, 0, 0); - AB_SampleMask = uint(0xffffffff); - BlendState = bTextBlend; - VertexShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Buffer Definitions: - // - // cbuffer cb0 - // { - // - // float4 QuadDesc; // Offset: 0 Size: 16 - // float4 TexCoords; // Offset: 16 Size: 16 - // float4 TextColor; // Offset: 32 Size: 16 [unused] - // - // } - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // cb0 cbuffer NA NA 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // POSITION 0 xyz 0 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float xyzw - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Constant buffer to DX9 shader constant mappings: - // - // Target Reg Buffer Start Reg # of Regs Data Conversion - // ---------- ------- --------- --------- ---------------------- - // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) - // - // - // Runtime generated constant mappings: - // - // Target Reg Constant Description - // ---------- -------------------------------------------------- - // c0 Vertex Shader position offset - // - // - // Level9 shader bytecode: - // - vs_2_x - def c3, 0, 1, 0, 0 - dcl_texcoord v0 - mad oT0.xy, v0, c2.zwzw, c2 - mad r0.xy, v0, c1.zwzw, c1 - add oPos.xy, r0, c0 - mov oPos.zw, c3.xyxy - - // approximately 4 instruction slots used - vs_4_0 - dcl_constantbuffer cb0[2], immediateIndexed - dcl_input v0.xy - dcl_output_siv o0.xyzw, position - dcl_output o1.xy - mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx - mov o0.zw, l(0,0,0,1.000000) - mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx - ret - // Approximately 4 instruction slots used - - }; - GeometryShader = NULL; - PixelShader = asm { - // - // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 - // - // - // Buffer Definitions: - // - // cbuffer cb0 - // { - // - // float4 QuadDesc; // Offset: 0 Size: 16 [unused] - // float4 TexCoords; // Offset: 16 Size: 16 [unused] - // float4 TextColor; // Offset: 32 Size: 16 - // - // } - // - // - // Resource Bindings: - // - // Name Type Format Dim Slot Elements - // ------------------------------ ---------- ------- ----------- ---- -------- - // sSampler sampler NA NA 0 1 - // tex texture float4 2d 0 1 - // cb0 cbuffer NA NA 0 1 - // - // - // - // Input signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Position 0 xyzw 0 POS float - // TEXCOORD 0 xy 1 NONE float xy - // - // - // Output signature: - // - // Name Index Mask Register SysValue Format Used - // -------------------- ----- ------ -------- -------- ------ ------ - // SV_Target 0 xyzw 0 TARGET float xyzw - // SV_Target 1 xyzw 1 TARGET float xyzw - // - // - // Constant buffer to DX9 shader constant mappings: - // - // Target Reg Buffer Start Reg # of Regs Data Conversion - // ---------- ------- --------- --------- ---------------------- - // c0 cb0 2 1 ( FLT, FLT, FLT, FLT) - // - // - // Sampler/Resource to DX9 shader sampler mappings: - // - // Target Sampler Source Sampler Source Resource - // -------------- --------------- ---------------- - // s0 s0 t0 - // - // - // Level9 shader bytecode: - // - ps_2_x - dcl t0.xy - dcl_2d s0 - mov oC0, c0 - texld r0, t0, s0 - mul r0, r0.zyxy, c0.w - mov oC1, r0 - - // approximately 4 instruction slots used (1 texture, 3 arithmetic) - ps_4_0 - dcl_constantbuffer cb0[3], immediateIndexed - dcl_sampler s0, mode_default - dcl_resource_texture2d (float,float,float,float) t0 - dcl_input_ps linear v1.xy - dcl_output o0.xyzw - dcl_output o1.xyzw - dcl_temps 1 - mov o0.xyzw, cb0[2].xyzw - sample r0.xyzw, v1.xyxx, t0.xyzw, s0 - mul o1.xyzw, r0.zyxy, cb0[2].wwww - ret - // Approximately 4 instruction slots used - - }; - } - -} - -#endif - -const BYTE g_main[] = -{ - 68, 88, 66, 67, 53, 137, - 246, 90, 48, 255, 136, 62, - 98, 150, 163, 150, 147, 186, - 203, 53, 1, 0, 0, 0, - 225, 18, 0, 0, 1, 0, - 0, 0, 36, 0, 0, 0, - 70, 88, 49, 48, 181, 18, - 0, 0, 1, 16, 255, 254, - 1, 0, 0, 0, 3, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 57, 16, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 99, 98, - 48, 0, 102, 108, 111, 97, - 116, 52, 0, 8, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 16, 0, 0, - 0, 16, 0, 0, 0, 16, - 0, 0, 0, 10, 33, 0, - 0, 81, 117, 97, 100, 68, - 101, 115, 99, 0, 84, 101, - 120, 67, 111, 111, 114, 100, - 115, 0, 84, 101, 120, 116, - 67, 111, 108, 111, 114, 0, - 84, 101, 120, 116, 117, 114, - 101, 50, 68, 0, 72, 0, - 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 12, 0, - 0, 0, 116, 101, 120, 0, - 66, 108, 101, 110, 100, 83, - 116, 97, 116, 101, 0, 114, - 0, 0, 0, 2, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, - 0, 0, 0, 98, 84, 101, - 120, 116, 66, 108, 101, 110, - 100, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 16, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 17, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 18, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 19, 0, - 0, 0, 1, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 15, 0, - 0, 0, 83, 97, 109, 112, - 108, 101, 114, 83, 116, 97, - 116, 101, 0, 16, 1, 0, - 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 21, 0, 0, - 0, 115, 83, 97, 109, 112, - 108, 101, 114, 0, 1, 0, - 0, 0, 2, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 2, 0, 0, 0, - 3, 0, 0, 0, 83, 97, - 109, 112, 108, 101, 84, 101, - 120, 116, 117, 114, 101, 0, - 80, 48, 0, 188, 3, 0, - 0, 68, 88, 66, 67, 211, - 96, 210, 105, 17, 130, 48, - 194, 178, 234, 96, 122, 215, - 146, 217, 132, 1, 0, 0, - 0, 188, 3, 0, 0, 6, - 0, 0, 0, 56, 0, 0, - 0, 228, 0, 0, 0, 168, - 1, 0, 0, 36, 2, 0, - 0, 48, 3, 0, 0, 100, - 3, 0, 0, 65, 111, 110, - 57, 164, 0, 0, 0, 164, - 0, 0, 0, 0, 2, 254, - 255, 112, 0, 0, 0, 52, - 0, 0, 0, 1, 0, 36, - 0, 0, 0, 48, 0, 0, - 0, 48, 0, 0, 0, 36, - 0, 1, 0, 48, 0, 0, - 0, 0, 0, 2, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 254, - 255, 81, 0, 0, 5, 3, - 0, 15, 160, 0, 0, 0, - 0, 0, 0, 128, 63, 0, - 0, 0, 0, 0, 0, 0, - 0, 31, 0, 0, 2, 5, - 0, 0, 128, 0, 0, 15, - 144, 4, 0, 0, 4, 0, - 0, 3, 224, 0, 0, 228, - 144, 2, 0, 238, 160, 2, - 0, 228, 160, 4, 0, 0, - 4, 0, 0, 3, 128, 0, - 0, 228, 144, 1, 0, 238, - 160, 1, 0, 228, 160, 2, - 0, 0, 3, 0, 0, 3, - 192, 0, 0, 228, 128, 0, - 0, 228, 160, 1, 0, 0, - 2, 0, 0, 12, 192, 3, - 0, 68, 160, 255, 255, 0, - 0, 83, 72, 68, 82, 188, - 0, 0, 0, 64, 0, 1, - 0, 47, 0, 0, 0, 89, - 0, 0, 4, 70, 142, 32, - 0, 0, 0, 0, 0, 2, - 0, 0, 0, 95, 0, 0, - 3, 50, 16, 16, 0, 0, - 0, 0, 0, 103, 0, 0, - 4, 242, 32, 16, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 101, 0, 0, 3, 50, - 32, 16, 0, 1, 0, 0, - 0, 50, 0, 0, 11, 50, - 32, 16, 0, 0, 0, 0, - 0, 70, 16, 16, 0, 0, - 0, 0, 0, 230, 138, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 70, 128, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, - 8, 194, 32, 16, 0, 0, - 0, 0, 0, 2, 64, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 63, 50, - 0, 0, 11, 50, 32, 16, - 0, 1, 0, 0, 0, 70, - 16, 16, 0, 0, 0, 0, - 0, 230, 138, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 70, 128, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 4, 1, 0, 0, 1, - 0, 0, 0, 64, 0, 0, - 0, 1, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 254, - 255, 0, 1, 0, 0, 208, - 0, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 99, - 98, 48, 0, 60, 0, 0, - 0, 3, 0, 0, 0, 88, - 0, 0, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 160, 0, 0, - 0, 0, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 188, 0, 0, - 0, 16, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 198, 0, 0, - 0, 32, 0, 0, 0, 16, - 0, 0, 0, 0, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 81, 117, 97, - 100, 68, 101, 115, 99, 0, - 171, 171, 171, 1, 0, 3, - 0, 1, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 84, 101, 120, 67, 111, - 111, 114, 100, 115, 0, 84, - 101, 120, 116, 67, 111, 108, - 111, 114, 0, 77, 105, 99, - 114, 111, 115, 111, 102, 116, - 32, 40, 82, 41, 32, 72, - 76, 83, 76, 32, 83, 104, - 97, 100, 101, 114, 32, 67, - 111, 109, 112, 105, 108, 101, - 114, 32, 57, 46, 50, 57, - 46, 57, 53, 50, 46, 51, - 49, 49, 49, 0, 171, 171, - 171, 73, 83, 71, 78, 44, - 0, 0, 0, 1, 0, 0, - 0, 8, 0, 0, 0, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, - 0, 7, 3, 0, 0, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 171, 171, 171, 79, - 83, 71, 78, 80, 0, 0, - 0, 2, 0, 0, 0, 8, - 0, 0, 0, 56, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 15, - 0, 0, 0, 68, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 0, 0, - 0, 1, 0, 0, 0, 3, - 12, 0, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 171, 171, 171, 107, 1, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 2, 0, 0, - 0, 0, 0, 0, 0, 188, - 2, 0, 0, 68, 88, 66, - 67, 57, 173, 135, 37, 0, - 15, 237, 50, 142, 80, 59, - 160, 81, 240, 60, 171, 1, - 0, 0, 0, 188, 2, 0, - 0, 6, 0, 0, 0, 56, - 0, 0, 0, 164, 0, 0, - 0, 16, 1, 0, 0, 140, - 1, 0, 0, 48, 2, 0, - 0, 136, 2, 0, 0, 65, - 111, 110, 57, 100, 0, 0, - 0, 100, 0, 0, 0, 0, - 2, 255, 255, 60, 0, 0, - 0, 40, 0, 0, 0, 0, - 0, 40, 0, 0, 0, 40, - 0, 0, 0, 40, 0, 1, - 0, 36, 0, 0, 0, 40, - 0, 0, 0, 0, 0, 1, - 2, 255, 255, 31, 0, 0, - 2, 0, 0, 0, 128, 0, - 0, 3, 176, 31, 0, 0, - 2, 0, 0, 0, 144, 0, - 8, 15, 160, 66, 0, 0, - 3, 0, 0, 15, 128, 0, - 0, 228, 176, 0, 8, 228, - 160, 1, 0, 0, 2, 0, - 8, 15, 128, 0, 0, 228, - 128, 255, 255, 0, 0, 83, - 72, 68, 82, 100, 0, 0, - 0, 64, 0, 0, 0, 25, - 0, 0, 0, 90, 0, 0, - 3, 0, 96, 16, 0, 0, - 0, 0, 0, 88, 24, 0, - 4, 0, 112, 16, 0, 0, - 0, 0, 0, 85, 85, 0, - 0, 98, 16, 0, 3, 50, - 16, 16, 0, 1, 0, 0, - 0, 101, 0, 0, 3, 242, - 32, 16, 0, 0, 0, 0, - 0, 69, 0, 0, 9, 242, - 32, 16, 0, 0, 0, 0, - 0, 70, 16, 16, 0, 1, - 0, 0, 0, 70, 126, 16, - 0, 0, 0, 0, 0, 0, - 96, 16, 0, 0, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 156, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 255, - 255, 0, 1, 0, 0, 105, - 0, 0, 0, 92, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 101, - 0, 0, 0, 2, 0, 0, - 0, 5, 0, 0, 0, 4, - 0, 0, 0, 255, 255, 255, - 255, 0, 0, 0, 0, 1, - 0, 0, 0, 12, 0, 0, - 0, 115, 83, 97, 109, 112, - 108, 101, 114, 0, 116, 101, - 120, 0, 77, 105, 99, 114, - 111, 115, 111, 102, 116, 32, - 40, 82, 41, 32, 72, 76, - 83, 76, 32, 83, 104, 97, - 100, 101, 114, 32, 67, 111, - 109, 112, 105, 108, 101, 114, - 32, 57, 46, 50, 57, 46, - 57, 53, 50, 46, 51, 49, - 49, 49, 0, 171, 171, 73, - 83, 71, 78, 80, 0, 0, - 0, 2, 0, 0, 0, 8, - 0, 0, 0, 56, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 15, - 0, 0, 0, 68, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 0, 0, - 0, 1, 0, 0, 0, 3, - 3, 0, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 171, 171, 171, 79, 83, 71, - 78, 44, 0, 0, 0, 1, - 0, 0, 0, 8, 0, 0, - 0, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 15, 0, 0, - 0, 83, 86, 95, 84, 97, - 114, 103, 101, 116, 0, 171, - 171, 63, 5, 0, 0, 0, - 0, 0, 0, 83, 97, 109, - 112, 108, 101, 84, 101, 120, - 116, 84, 101, 120, 116, 117, - 114, 101, 0, 4, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 3, 0, 0, 0, 255, - 255, 255, 255, 188, 3, 0, - 0, 68, 88, 66, 67, 211, - 96, 210, 105, 17, 130, 48, - 194, 178, 234, 96, 122, 215, - 146, 217, 132, 1, 0, 0, - 0, 188, 3, 0, 0, 6, - 0, 0, 0, 56, 0, 0, - 0, 228, 0, 0, 0, 168, - 1, 0, 0, 36, 2, 0, - 0, 48, 3, 0, 0, 100, - 3, 0, 0, 65, 111, 110, - 57, 164, 0, 0, 0, 164, - 0, 0, 0, 0, 2, 254, - 255, 112, 0, 0, 0, 52, - 0, 0, 0, 1, 0, 36, - 0, 0, 0, 48, 0, 0, - 0, 48, 0, 0, 0, 36, - 0, 1, 0, 48, 0, 0, - 0, 0, 0, 2, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 254, - 255, 81, 0, 0, 5, 3, - 0, 15, 160, 0, 0, 0, - 0, 0, 0, 128, 63, 0, - 0, 0, 0, 0, 0, 0, - 0, 31, 0, 0, 2, 5, - 0, 0, 128, 0, 0, 15, - 144, 4, 0, 0, 4, 0, - 0, 3, 224, 0, 0, 228, - 144, 2, 0, 238, 160, 2, - 0, 228, 160, 4, 0, 0, - 4, 0, 0, 3, 128, 0, - 0, 228, 144, 1, 0, 238, - 160, 1, 0, 228, 160, 2, - 0, 0, 3, 0, 0, 3, - 192, 0, 0, 228, 128, 0, - 0, 228, 160, 1, 0, 0, - 2, 0, 0, 12, 192, 3, - 0, 68, 160, 255, 255, 0, - 0, 83, 72, 68, 82, 188, - 0, 0, 0, 64, 0, 1, - 0, 47, 0, 0, 0, 89, - 0, 0, 4, 70, 142, 32, - 0, 0, 0, 0, 0, 2, - 0, 0, 0, 95, 0, 0, - 3, 50, 16, 16, 0, 0, - 0, 0, 0, 103, 0, 0, - 4, 242, 32, 16, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 101, 0, 0, 3, 50, - 32, 16, 0, 1, 0, 0, - 0, 50, 0, 0, 11, 50, - 32, 16, 0, 0, 0, 0, - 0, 70, 16, 16, 0, 0, - 0, 0, 0, 230, 138, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 70, 128, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, - 8, 194, 32, 16, 0, 0, - 0, 0, 0, 2, 64, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 63, 50, - 0, 0, 11, 50, 32, 16, - 0, 1, 0, 0, 0, 70, - 16, 16, 0, 0, 0, 0, - 0, 230, 138, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 70, 128, 32, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 4, 1, 0, 0, 1, - 0, 0, 0, 64, 0, 0, - 0, 1, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 254, - 255, 0, 1, 0, 0, 208, - 0, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 99, - 98, 48, 0, 60, 0, 0, - 0, 3, 0, 0, 0, 88, - 0, 0, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 160, 0, 0, - 0, 0, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 188, 0, 0, - 0, 16, 0, 0, 0, 16, - 0, 0, 0, 2, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 198, 0, 0, - 0, 32, 0, 0, 0, 16, - 0, 0, 0, 0, 0, 0, - 0, 172, 0, 0, 0, 0, - 0, 0, 0, 81, 117, 97, - 100, 68, 101, 115, 99, 0, - 171, 171, 171, 1, 0, 3, - 0, 1, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 84, 101, 120, 67, 111, - 111, 114, 100, 115, 0, 84, - 101, 120, 116, 67, 111, 108, - 111, 114, 0, 77, 105, 99, - 114, 111, 115, 111, 102, 116, - 32, 40, 82, 41, 32, 72, - 76, 83, 76, 32, 83, 104, - 97, 100, 101, 114, 32, 67, - 111, 109, 112, 105, 108, 101, - 114, 32, 57, 46, 50, 57, - 46, 57, 53, 50, 46, 51, - 49, 49, 49, 0, 171, 171, - 171, 73, 83, 71, 78, 44, - 0, 0, 0, 1, 0, 0, - 0, 8, 0, 0, 0, 32, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, - 0, 7, 3, 0, 0, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 171, 171, 171, 79, - 83, 71, 78, 80, 0, 0, - 0, 2, 0, 0, 0, 8, - 0, 0, 0, 56, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 3, 0, 0, - 0, 0, 0, 0, 0, 15, - 0, 0, 0, 68, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3, 0, 0, - 0, 1, 0, 0, 0, 3, - 12, 0, 0, 83, 86, 95, - 80, 111, 115, 105, 116, 105, - 111, 110, 0, 84, 69, 88, - 67, 79, 79, 82, 68, 0, - 171, 171, 171, 73, 8, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 2, 0, 0, - 0, 0, 0, 0, 0, 16, - 4, 0, 0, 68, 88, 66, - 67, 175, 129, 196, 242, 129, - 85, 126, 90, 106, 179, 87, - 12, 194, 2, 170, 102, 1, - 0, 0, 0, 16, 4, 0, - 0, 6, 0, 0, 0, 56, - 0, 0, 0, 204, 0, 0, - 0, 148, 1, 0, 0, 16, - 2, 0, 0, 108, 3, 0, - 0, 196, 3, 0, 0, 65, - 111, 110, 57, 140, 0, 0, - 0, 140, 0, 0, 0, 0, - 2, 255, 255, 88, 0, 0, - 0, 52, 0, 0, 0, 1, - 0, 40, 0, 0, 0, 52, - 0, 0, 0, 52, 0, 1, - 0, 36, 0, 0, 0, 52, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 1, - 2, 255, 255, 31, 0, 0, - 2, 0, 0, 0, 128, 0, - 0, 3, 176, 31, 0, 0, - 2, 0, 0, 0, 144, 0, - 8, 15, 160, 1, 0, 0, - 2, 0, 8, 15, 128, 0, - 0, 228, 160, 66, 0, 0, - 3, 0, 0, 15, 128, 0, - 0, 228, 176, 0, 8, 228, - 160, 5, 0, 0, 3, 0, - 0, 15, 128, 0, 0, 70, - 128, 0, 0, 255, 160, 1, - 0, 0, 2, 1, 8, 15, - 128, 0, 0, 228, 128, 255, - 255, 0, 0, 83, 72, 68, - 82, 192, 0, 0, 0, 64, - 0, 0, 0, 48, 0, 0, - 0, 89, 0, 0, 4, 70, - 142, 32, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 90, - 0, 0, 3, 0, 96, 16, - 0, 0, 0, 0, 0, 88, - 24, 0, 4, 0, 112, 16, - 0, 0, 0, 0, 0, 85, - 85, 0, 0, 98, 16, 0, - 3, 50, 16, 16, 0, 1, - 0, 0, 0, 101, 0, 0, - 3, 242, 32, 16, 0, 0, - 0, 0, 0, 101, 0, 0, - 3, 242, 32, 16, 0, 1, - 0, 0, 0, 104, 0, 0, - 2, 1, 0, 0, 0, 54, - 0, 0, 6, 242, 32, 16, - 0, 0, 0, 0, 0, 70, - 142, 32, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 69, - 0, 0, 9, 242, 0, 16, - 0, 0, 0, 0, 0, 70, - 16, 16, 0, 1, 0, 0, - 0, 70, 126, 16, 0, 0, - 0, 0, 0, 0, 96, 16, - 0, 0, 0, 0, 0, 56, - 0, 0, 8, 242, 32, 16, - 0, 1, 0, 0, 0, 102, - 4, 16, 0, 0, 0, 0, - 0, 246, 143, 32, 0, 0, - 0, 0, 0, 2, 0, 0, - 0, 62, 0, 0, 1, 83, - 84, 65, 84, 116, 0, 0, - 0, 4, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 68, 69, - 70, 84, 1, 0, 0, 1, - 0, 0, 0, 144, 0, 0, - 0, 3, 0, 0, 0, 28, - 0, 0, 0, 0, 4, 255, - 255, 0, 1, 0, 0, 32, - 1, 0, 0, 124, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 133, - 0, 0, 0, 2, 0, 0, - 0, 5, 0, 0, 0, 4, - 0, 0, 0, 255, 255, 255, - 255, 0, 0, 0, 0, 1, - 0, 0, 0, 12, 0, 0, - 0, 137, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 115, 83, 97, - 109, 112, 108, 101, 114, 0, - 116, 101, 120, 0, 99, 98, - 48, 0, 171, 171, 171, 137, - 0, 0, 0, 3, 0, 0, - 0, 168, 0, 0, 0, 48, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 240, - 0, 0, 0, 0, 0, 0, - 0, 16, 0, 0, 0, 0, - 0, 0, 0, 252, 0, 0, - 0, 0, 0, 0, 0, 12, - 1, 0, 0, 16, 0, 0, - 0, 16, 0, 0, 0, 0, - 0, 0, 0, 252, 0, 0, - 0, 0, 0, 0, 0, 22, - 1, 0, 0, 32, 0, 0, - 0, 16, 0, 0, 0, 2, - 0, 0, 0, 252, 0, 0, - 0, 0, 0, 0, 0, 81, - 117, 97, 100, 68, 101, 115, - 99, 0, 171, 171, 171, 1, - 0, 3, 0, 1, 0, 4, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 101, 120, - 67, 111, 111, 114, 100, 115, - 0, 84, 101, 120, 116, 67, - 111, 108, 111, 114, 0, 77, - 105, 99, 114, 111, 115, 111, - 102, 116, 32, 40, 82, 41, - 32, 72, 76, 83, 76, 32, - 83, 104, 97, 100, 101, 114, - 32, 67, 111, 109, 112, 105, - 108, 101, 114, 32, 57, 46, - 50, 57, 46, 57, 53, 50, - 46, 51, 49, 49, 49, 0, - 171, 171, 171, 73, 83, 71, - 78, 80, 0, 0, 0, 2, - 0, 0, 0, 8, 0, 0, - 0, 56, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 15, 0, 0, - 0, 68, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 1, - 0, 0, 0, 3, 3, 0, - 0, 83, 86, 95, 80, 111, - 115, 105, 116, 105, 111, 110, - 0, 84, 69, 88, 67, 79, - 79, 82, 68, 0, 171, 171, - 171, 79, 83, 71, 78, 68, - 0, 0, 0, 2, 0, 0, - 0, 8, 0, 0, 0, 56, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 0, 0, 0, - 0, 15, 0, 0, 0, 56, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 3, - 0, 0, 0, 1, 0, 0, - 0, 15, 0, 0, 0, 83, - 86, 95, 84, 97, 114, 103, - 101, 116, 0, 171, 171, 29, - 12, 0, 0, 0, 0, 0, - 0, 4, 0, 0, 0, 48, - 0, 0, 0, 0, 0, 0, - 0, 3, 0, 0, 0, 255, - 255, 255, 255, 0, 0, 0, - 0, 43, 0, 0, 0, 15, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 52, - 0, 0, 0, 15, 0, 0, - 0, 0, 0, 0, 0, 16, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 62, 0, 0, - 0, 15, 0, 0, 0, 0, - 0, 0, 0, 32, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 110, 0, 0, 0, 82, - 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 0, - 0, 0, 0, 153, 0, 0, - 0, 125, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, - 255, 9, 0, 0, 0, 36, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 164, - 0, 0, 0, 37, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 176, 0, 0, - 0, 38, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 188, 0, 0, 0, 39, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 200, - 0, 0, 0, 40, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 212, 0, 0, - 0, 41, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 224, 0, 0, 0, 42, - 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 236, - 0, 0, 0, 43, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 248, 0, 0, - 0, 44, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 4, 1, 0, 0, 0, - 0, 0, 0, 57, 1, 0, - 0, 29, 1, 0, 0, 0, - 0, 0, 0, 255, 255, 255, - 255, 3, 0, 0, 0, 55, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 110, - 0, 0, 0, 46, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 66, 1, 0, - 0, 47, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 78, 1, 0, 0, 0, - 0, 0, 0, 90, 1, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 104, 1, 0, - 0, 3, 0, 0, 0, 0, - 0, 0, 0, 6, 0, 0, - 0, 0, 0, 0, 0, 7, - 0, 0, 0, 43, 5, 0, - 0, 8, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 51, 5, 0, 0, 7, - 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 255, - 7, 0, 0, 7, 8, 0, - 0, 1, 0, 0, 0, 0, - 0, 0, 0, 104, 1, 0, - 0, 6, 0, 0, 0, 0, - 0, 0, 0, 10, 0, 0, - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 25, 8, 0, - 0, 11, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 61, 8, 0, 0, 2, - 0, 0, 0, 0, 0, 0, - 0, 2, 0, 0, 0, 153, - 0, 0, 0, 6, 0, 0, - 0, 0, 0, 0, 0, 7, - 0, 0, 0, 9, 12, 0, - 0, 8, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 17, 12, 0, 0, 7, - 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 49, - 16, 0, 0 -}; diff --git a/gfx/cairo/cairo/src/cairo-damage-private.h b/gfx/cairo/cairo/src/cairo-damage-private.h new file mode 100644 index 000000000000..97b177e86b45 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-damage-private.h @@ -0,0 +1,85 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_DAMAGE_PRIVATE_H +#define CAIRO_DAMAGE_PRIVATE_H + +#include "cairo-types-private.h" + +#include + +CAIRO_BEGIN_DECLS + +struct _cairo_damage { + cairo_status_t status; + cairo_region_t *region; + + int dirty, remain; + struct _cairo_damage_chunk { + struct _cairo_damage_chunk *next; + cairo_box_t *base; + int count; + int size; + } chunks, *tail; + cairo_box_t boxes[32]; +}; + +cairo_private cairo_damage_t * +_cairo_damage_create (void); + +cairo_private cairo_damage_t * +_cairo_damage_create_in_error (cairo_status_t status); + +cairo_private cairo_damage_t * +_cairo_damage_add_box (cairo_damage_t *damage, + const cairo_box_t *box); + +cairo_private cairo_damage_t * +_cairo_damage_add_rectangle (cairo_damage_t *damage, + const cairo_rectangle_int_t *rect); + +cairo_private cairo_damage_t * +_cairo_damage_add_region (cairo_damage_t *damage, + const cairo_region_t *region); + +cairo_private cairo_damage_t * +_cairo_damage_reduce (cairo_damage_t *damage); + +cairo_private void +_cairo_damage_destroy (cairo_damage_t *damage); + +CAIRO_END_DECLS + +#endif /* CAIRO_DAMAGE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-damage.c b/gfx/cairo/cairo/src/cairo-damage.c new file mode 100644 index 000000000000..97d9fe9095b8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-damage.c @@ -0,0 +1,241 @@ +/* + * Copyright © 2012 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 Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-damage-private.h" +#include "cairo-region-private.h" + +static const cairo_damage_t __cairo_damage__nil = { CAIRO_STATUS_NO_MEMORY }; + +cairo_damage_t * +_cairo_damage_create_in_error (cairo_status_t status) +{ + _cairo_error_throw (status); + return (cairo_damage_t *) &__cairo_damage__nil; +} + +cairo_damage_t * +_cairo_damage_create (void) +{ + cairo_damage_t *damage; + + damage = _cairo_malloc (sizeof (*damage)); + if (unlikely (damage == NULL)) { + _cairo_error_throw(CAIRO_STATUS_NO_MEMORY); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + damage->status = CAIRO_STATUS_SUCCESS; + damage->region = NULL; + damage->dirty = 0; + damage->tail = &damage->chunks; + damage->chunks.base = damage->boxes; + damage->chunks.size = ARRAY_LENGTH(damage->boxes); + damage->chunks.count = 0; + damage->chunks.next = NULL; + + damage->remain = damage->chunks.size; + + return damage; +} + +void +_cairo_damage_destroy (cairo_damage_t *damage) +{ + struct _cairo_damage_chunk *chunk, *next; + + if (damage == (cairo_damage_t *) &__cairo_damage__nil) + return; + + for (chunk = damage->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } + cairo_region_destroy (damage->region); + free (damage); +} + +static cairo_damage_t * +_cairo_damage_add_boxes(cairo_damage_t *damage, + const cairo_box_t *boxes, + int count) +{ + struct _cairo_damage_chunk *chunk; + int n, size; + + TRACE ((stderr, "%s x%d\n", __FUNCTION__, count)); + + if (damage == NULL) + damage = _cairo_damage_create (); + if (damage->status) + return damage; + + damage->dirty += count; + + n = count; + if (n > damage->remain) + n = damage->remain; + + memcpy (damage->tail->base + damage->tail->count, boxes, + n * sizeof (cairo_box_t)); + + count -= n; + damage->tail->count += n; + damage->remain -= n; + + if (count == 0) + return damage; + + size = 2 * damage->tail->size; + if (size < count) + size = (count + 64) & ~63; + + chunk = _cairo_malloc (sizeof (*chunk) + sizeof (cairo_box_t) * size); + if (unlikely (chunk == NULL)) { + _cairo_damage_destroy (damage); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + chunk->next = NULL; + chunk->base = (cairo_box_t *) (chunk + 1); + chunk->size = size; + chunk->count = count; + + damage->tail->next = chunk; + damage->tail = chunk; + + memcpy (damage->tail->base, boxes + n, + count * sizeof (cairo_box_t)); + damage->remain = size - count; + + return damage; +} + +cairo_damage_t * +_cairo_damage_add_box(cairo_damage_t *damage, + const cairo_box_t *box) +{ + TRACE ((stderr, "%s: (%d, %d),(%d, %d)\n", __FUNCTION__, + box->p1.x, box->p1.y, box->p2.x, box->p2.y)); + + return _cairo_damage_add_boxes(damage, box, 1); +} + +cairo_damage_t * +_cairo_damage_add_rectangle(cairo_damage_t *damage, + const cairo_rectangle_int_t *r) +{ + cairo_box_t box; + + TRACE ((stderr, "%s: (%d, %d)x(%d, %d)\n", __FUNCTION__, + r->x, r->y, r->width, r->height)); + + box.p1.x = r->x; + box.p1.y = r->y; + box.p2.x = r->x + r->width; + box.p2.y = r->y + r->height; + + return _cairo_damage_add_boxes(damage, &box, 1); +} + +cairo_damage_t * +_cairo_damage_add_region (cairo_damage_t *damage, + const cairo_region_t *region) +{ + cairo_box_t *boxes; + int nbox; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + boxes = _cairo_region_get_boxes (region, &nbox); + return _cairo_damage_add_boxes(damage, boxes, nbox); +} + +cairo_damage_t * +_cairo_damage_reduce (cairo_damage_t *damage) +{ + cairo_box_t *free_boxes = NULL; + cairo_box_t *boxes, *b; + struct _cairo_damage_chunk *chunk, *last; + + TRACE ((stderr, "%s: dirty=%d\n", __FUNCTION__, + damage ? damage->dirty : -1)); + if (damage == NULL || damage->status || !damage->dirty) + return damage; + + if (damage->region) { + cairo_region_t *region; + + region = damage->region; + damage->region = NULL; + + damage = _cairo_damage_add_region (damage, region); + cairo_region_destroy (region); + + if (unlikely (damage->status)) + return damage; + } + + boxes = damage->tail->base; + if (damage->dirty > damage->tail->size) { + boxes = free_boxes = _cairo_malloc (damage->dirty * sizeof (cairo_box_t)); + if (unlikely (boxes == NULL)) { + _cairo_damage_destroy (damage); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + b = boxes; + last = NULL; + } else { + b = boxes + damage->tail->count; + last = damage->tail; + } + + for (chunk = &damage->chunks; chunk != last; chunk = chunk->next) { + memcpy (b, chunk->base, chunk->count * sizeof (cairo_box_t)); + b += chunk->count; + } + + damage->region = _cairo_region_create_from_boxes (boxes, damage->dirty); + free (free_boxes); + + if (unlikely (damage->region->status)) { + _cairo_damage_destroy (damage); + return (cairo_damage_t *) &__cairo_damage__nil; + } + + damage->dirty = 0; + return damage; +} diff --git a/gfx/cairo/cairo/src/cairo-debug.c b/gfx/cairo/cairo/src/cairo-debug.c index e9e72b6aa93b..6acdea9dde86 100644 --- a/gfx/cairo/cairo/src/cairo-debug.c +++ b/gfx/cairo/cairo/src/cairo-debug.c @@ -34,6 +34,7 @@ */ #include "cairoint.h" +#include "cairo-image-surface-private.h" /** * cairo_debug_reset_static_data: @@ -55,6 +56,8 @@ * functions have been called as necessary). If there are active cairo * objects, this call is likely to cause a crash, (eg. an assertion * failure due to a hash table being destroyed when non-empty). + * + * Since: 1.0 **/ void cairo_debug_reset_static_data (void) @@ -83,11 +86,13 @@ cairo_debug_reset_static_data (void) _cairo_image_reset_static_data (); + _cairo_image_compositor_reset_static_data (); + #if CAIRO_HAS_DRM_SURFACE _cairo_drm_device_reset_static_data (); #endif - _cairo_reset_static_data (); + _cairo_default_context_reset_static_data (); CAIRO_MUTEX_FINALIZE (); } @@ -118,9 +123,16 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface) width = image->width*2; break; case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_ARGB32: width = image->width*4; break; + case CAIRO_FORMAT_RGB96F: + width = image->width*12; + break; + case CAIRO_FORMAT_RGBA128F: + width = image->width*16; + break; case CAIRO_FORMAT_INVALID: default: /* XXX compute width from pixman bpp */ @@ -229,18 +241,19 @@ _print_close (void *closure) } void -_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) +_cairo_debug_print_path (FILE *stream, const cairo_path_fixed_t *path) { cairo_status_t status; + cairo_box_t box; - printf ("path: extents=(%f, %f), (%f, %f)\n", + fprintf (stream, + "path: extents=(%f, %f), (%f, %f)\n", _cairo_fixed_to_double (path->extents.p1.x), _cairo_fixed_to_double (path->extents.p1.y), _cairo_fixed_to_double (path->extents.p2.x), _cairo_fixed_to_double (path->extents.p2.y)); status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _print_move_to, _print_line_to, _print_curve_to, @@ -248,5 +261,65 @@ _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) stream); assert (status == CAIRO_STATUS_SUCCESS); - printf ("\n"); + if (_cairo_path_fixed_is_box (path, &box)) { + fprintf (stream, "[box (%d, %d), (%d, %d)]", + box.p1.x, box.p1.y, box.p2.x, box.p2.y); + } + + fprintf (stream, "\n"); +} + +void +_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon) +{ + int n; + + fprintf (stream, + "polygon: extents=(%f, %f), (%f, %f)\n", + _cairo_fixed_to_double (polygon->extents.p1.x), + _cairo_fixed_to_double (polygon->extents.p1.y), + _cairo_fixed_to_double (polygon->extents.p2.x), + _cairo_fixed_to_double (polygon->extents.p2.y)); + if (polygon->num_limits) { + fprintf (stream, + " : limit=(%f, %f), (%f, %f) x %d\n", + _cairo_fixed_to_double (polygon->limit.p1.x), + _cairo_fixed_to_double (polygon->limit.p1.y), + _cairo_fixed_to_double (polygon->limit.p2.x), + _cairo_fixed_to_double (polygon->limit.p2.y), + polygon->num_limits); + } + + for (n = 0; n < polygon->num_edges; n++) { + cairo_edge_t *edge = &polygon->edges[n]; + + fprintf (stream, + " [%d] = [(%f, %f), (%f, %f)], top=%f, bottom=%f, dir=%d\n", + n, + _cairo_fixed_to_double (edge->line.p1.x), + _cairo_fixed_to_double (edge->line.p1.y), + _cairo_fixed_to_double (edge->line.p2.x), + _cairo_fixed_to_double (edge->line.p2.y), + _cairo_fixed_to_double (edge->top), + _cairo_fixed_to_double (edge->bottom), + edge->dir); + + } +} + +void +_cairo_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix) +{ + fprintf (file, "[%g %g %g %g %g %g]\n", + matrix->xx, matrix->yx, + matrix->xy, matrix->yy, + matrix->x0, matrix->y0); +} + +void +_cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect) +{ + fprintf (file, "x: %d y: %d width: %d height: %d\n", + rect->x, rect->y, + rect->width, rect->height); } diff --git a/gfx/cairo/cairo/src/cairo-default-context-private.h b/gfx/cairo/cairo/src/cairo-default-context-private.h new file mode 100644 index 000000000000..fd159b496707 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-default-context-private.h @@ -0,0 +1,68 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 D. Worth + */ + +#ifndef CAIRO_DEFAULT_CONTEXT_PRIVATE_H +#define CAIRO_DEFAULT_CONTEXT_PRIVATE_H + +#include "cairo-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-fixed-private.h" + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_default_context cairo_default_context_t; + +struct _cairo_default_context { + cairo_t base; + + cairo_gstate_t *gstate; + cairo_gstate_t gstate_tail[2]; + cairo_gstate_t *gstate_freelist; + + cairo_path_fixed_t path[1]; +}; + +cairo_private cairo_t * +_cairo_default_context_create (void *target); + +cairo_private cairo_status_t +_cairo_default_context_init (cairo_default_context_t *cr, void *target); + +cairo_private void +_cairo_default_context_fini (cairo_default_context_t *cr); + +CAIRO_END_DECLS + +#endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-default-context.c b/gfx/cairo/cairo/src/cairo-default-context.c new file mode 100644 index 000000000000..d2c9cae10d47 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-default-context.c @@ -0,0 +1,1499 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-private.h" +#include "cairo-arc-private.h" +#include "cairo-backend-private.h" +#include "cairo-clip-inline.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-path-private.h" +#include "cairo-pattern-private.h" + +#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1) + +#if !defined(INFINITY) +#define INFINITY HUGE_VAL +#endif + +static freed_pool_t context_pool; + +void +_cairo_default_context_reset_static_data (void) +{ + _freed_pool_reset (&context_pool); +} + +void +_cairo_default_context_fini (cairo_default_context_t *cr) +{ + while (cr->gstate != &cr->gstate_tail[0]) { + if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist)) + break; + } + + _cairo_gstate_fini (cr->gstate); + cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */ + while (cr->gstate_freelist != NULL) { + cairo_gstate_t *gstate = cr->gstate_freelist; + cr->gstate_freelist = gstate->next; + free (gstate); + } + + _cairo_path_fixed_fini (cr->path); + + _cairo_fini (&cr->base); +} + +static void +_cairo_default_context_destroy (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_default_context_fini (cr); + + /* mark the context as invalid to protect against misuse */ + cr->base.status = CAIRO_STATUS_NULL_POINTER; + _freed_pool_put (&context_pool, cr); +} + +static cairo_surface_t * +_cairo_default_context_get_original_target (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_original_target (cr->gstate); +} + +static cairo_surface_t * +_cairo_default_context_get_current_target (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_target (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_save (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); +} + +static cairo_status_t +_cairo_default_context_restore (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + if (unlikely (_cairo_gstate_is_group (cr->gstate))) + return _cairo_error (CAIRO_STATUS_INVALID_RESTORE); + + return _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); +} + +static cairo_status_t +_cairo_default_context_push_group (void *abstract_cr, cairo_content_t content) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_surface_t *group_surface; + cairo_clip_t *clip; + cairo_status_t status; + + clip = _cairo_gstate_get_clip (cr->gstate); + if (_cairo_clip_is_all_clipped (clip)) { + group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); + status = group_surface->status; + if (unlikely (status)) + goto bail; + } else { + cairo_surface_t *parent_surface; + cairo_rectangle_int_t extents; + cairo_bool_t bounded, is_empty; + + parent_surface = _cairo_gstate_get_target (cr->gstate); + + if (unlikely (parent_surface->status)) + return parent_surface->status; + if (unlikely (parent_surface->finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + /* Get the extents that we'll use in creating our new group surface */ + bounded = _cairo_surface_get_extents (parent_surface, &extents); + if (clip) + /* XXX: This assignment just fixes a compiler warning? */ + is_empty = _cairo_rectangle_intersect (&extents, + _cairo_clip_get_extents (clip)); + + if (!bounded) { + /* XXX: Generic solution? */ + group_surface = cairo_recording_surface_create (content, NULL); + extents.x = extents.y = 0; + } else { + group_surface = _cairo_surface_create_scratch (parent_surface, + content, + extents.width, + extents.height, + CAIRO_COLOR_TRANSPARENT); + } + status = group_surface->status; + if (unlikely (status)) + goto bail; + + /* Set device offsets on the new surface so that logically it appears at + * the same location on the parent surface -- when we pop_group this, + * the source pattern will get fixed up for the appropriate target surface + * device offsets, so we want to set our own surface offsets from /that/, + * and not from the device origin. */ + cairo_surface_set_device_offset (group_surface, + parent_surface->device_transform.x0 - extents.x, + parent_surface->device_transform.y0 - extents.y); + + cairo_surface_set_device_scale (group_surface, + parent_surface->device_transform.xx, + parent_surface->device_transform.yy); + + /* If we have a current path, we need to adjust it to compensate for + * the device offset just applied. */ + _cairo_path_fixed_translate (cr->path, + _cairo_fixed_from_int (-extents.x), + _cairo_fixed_from_int (-extents.y)); + } + + /* create a new gstate for the redirect */ + status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); + if (unlikely (status)) + goto bail; + + status = _cairo_gstate_redirect_target (cr->gstate, group_surface); + +bail: + cairo_surface_destroy (group_surface); + return status; +} + +static cairo_pattern_t * +_cairo_default_context_pop_group (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_surface_t *group_surface; + cairo_pattern_t *group_pattern; + cairo_surface_t *parent_surface; + cairo_matrix_t group_matrix; + cairo_status_t status; + + /* Verify that we are at the right nesting level */ + if (unlikely (! _cairo_gstate_is_group (cr->gstate))) + return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP); + + /* Get a reference to the active surface before restoring */ + group_surface = _cairo_gstate_get_target (cr->gstate); + group_surface = cairo_surface_reference (group_surface); + + status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); + assert (status == CAIRO_STATUS_SUCCESS); + + parent_surface = _cairo_gstate_get_target (cr->gstate); + + group_pattern = cairo_pattern_create_for_surface (group_surface); + status = group_pattern->status; + if (unlikely (status)) + goto done; + + _cairo_gstate_get_matrix (cr->gstate, &group_matrix); + cairo_pattern_set_matrix (group_pattern, &group_matrix); + + /* If we have a current path, we need to adjust it to compensate for + * the device offset just removed. */ + _cairo_path_fixed_translate (cr->path, + _cairo_fixed_from_int (parent_surface->device_transform.x0 - group_surface->device_transform.x0), + _cairo_fixed_from_int (parent_surface->device_transform.y0 - group_surface->device_transform.y0)); + +done: + cairo_surface_destroy (group_surface); + + return group_pattern; +} + +static cairo_status_t +_cairo_default_context_set_source (void *abstract_cr, + cairo_pattern_t *source) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_source (cr->gstate, source); +} + +static cairo_bool_t +_current_source_matches_solid (const cairo_pattern_t *pattern, + double red, + double green, + double blue, + double alpha) +{ + cairo_color_t color; + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + return _cairo_color_equal (&color, + &((cairo_solid_pattern_t *) pattern)->color); +} + +static cairo_status_t +_cairo_default_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_pattern_t *pattern; + cairo_status_t status; + + if (_current_source_matches_solid (cr->gstate->source, + red, green, blue, alpha)) + return CAIRO_STATUS_SUCCESS; + + /* push the current pattern to the freed lists */ + _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); + + pattern = cairo_pattern_create_rgba (red, green, blue, alpha); + if (unlikely (pattern->status)) + return pattern->status; + + status = _cairo_default_context_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + return status; +} + +static cairo_status_t +_cairo_default_context_set_source_surface (void *abstract_cr, + cairo_surface_t *surface, + double x, + double y) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + cairo_status_t status; + + /* push the current pattern to the freed lists */ + _cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); + + pattern = cairo_pattern_create_for_surface (surface); + if (unlikely (pattern->status)) { + status = pattern->status; + cairo_pattern_destroy (pattern); + return status; + } + + cairo_matrix_init_translate (&matrix, -x, -y); + cairo_pattern_set_matrix (pattern, &matrix); + + status = _cairo_default_context_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + return status; +} + +static cairo_pattern_t * +_cairo_default_context_get_source (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_source (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_set_tolerance (void *abstract_cr, + double tolerance) +{ + cairo_default_context_t *cr = abstract_cr; + + if (tolerance < CAIRO_TOLERANCE_MINIMUM) + tolerance = CAIRO_TOLERANCE_MINIMUM; + + return _cairo_gstate_set_tolerance (cr->gstate, tolerance); +} + +static cairo_status_t +_cairo_default_context_set_operator (void *abstract_cr, cairo_operator_t op) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_operator (cr->gstate, op); +} + +static cairo_status_t +_cairo_default_context_set_opacity (void *abstract_cr, double opacity) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_opacity (cr->gstate, opacity); +} + +static cairo_status_t +_cairo_default_context_set_antialias (void *abstract_cr, + cairo_antialias_t antialias) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_antialias (cr->gstate, antialias); +} + +static cairo_status_t +_cairo_default_context_set_fill_rule (void *abstract_cr, + cairo_fill_rule_t fill_rule) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_fill_rule (cr->gstate, fill_rule); +} + +static cairo_status_t +_cairo_default_context_set_line_width (void *abstract_cr, + double line_width) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_line_width (cr->gstate, line_width); +} + +static cairo_status_t +_cairo_default_context_set_line_cap (void *abstract_cr, + cairo_line_cap_t line_cap) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_line_cap (cr->gstate, line_cap); +} + +static cairo_status_t +_cairo_default_context_set_line_join (void *abstract_cr, + cairo_line_join_t line_join) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_line_join (cr->gstate, line_join); +} + +static cairo_status_t +_cairo_default_context_set_dash (void *abstract_cr, + const double *dashes, + int num_dashes, + double offset) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_dash (cr->gstate, + dashes, num_dashes, offset); +} + +static cairo_status_t +_cairo_default_context_set_miter_limit (void *abstract_cr, + double limit) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_miter_limit (cr->gstate, limit); +} + +static cairo_antialias_t +_cairo_default_context_get_antialias (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_antialias (cr->gstate); +} + +static void +_cairo_default_context_get_dash (void *abstract_cr, + double *dashes, + int *num_dashes, + double *offset) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_get_dash (cr->gstate, dashes, num_dashes, offset); +} + +static cairo_fill_rule_t +_cairo_default_context_get_fill_rule (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_fill_rule (cr->gstate); +} + +static double +_cairo_default_context_get_line_width (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_line_width (cr->gstate); +} + +static cairo_line_cap_t +_cairo_default_context_get_line_cap (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_line_cap (cr->gstate); +} + +static cairo_line_join_t +_cairo_default_context_get_line_join (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_line_join (cr->gstate); +} + +static double +_cairo_default_context_get_miter_limit (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_miter_limit (cr->gstate); +} + +static cairo_operator_t +_cairo_default_context_get_operator (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_operator (cr->gstate); +} + +static double +_cairo_default_context_get_opacity (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_opacity (cr->gstate); +} + +static double +_cairo_default_context_get_tolerance (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_tolerance (cr->gstate); +} + + +/* Current transformation matrix */ + +static cairo_status_t +_cairo_default_context_translate (void *abstract_cr, + double tx, + double ty) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_translate (cr->gstate, tx, ty); +} + +static cairo_status_t +_cairo_default_context_scale (void *abstract_cr, + double sx, + double sy) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_scale (cr->gstate, sx, sy); +} + +static cairo_status_t +_cairo_default_context_rotate (void *abstract_cr, + double theta) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_rotate (cr->gstate, theta); +} + +static cairo_status_t +_cairo_default_context_transform (void *abstract_cr, + const cairo_matrix_t *matrix) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_transform (cr->gstate, matrix); +} + +static cairo_status_t +_cairo_default_context_set_matrix (void *abstract_cr, + const cairo_matrix_t *matrix) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_matrix (cr->gstate, matrix); +} + +static cairo_status_t +_cairo_default_context_set_identity_matrix (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_identity_matrix (cr->gstate); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_default_context_get_matrix (void *abstract_cr, + cairo_matrix_t *matrix) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_get_matrix (cr->gstate, matrix); +} + +static void +_cairo_default_context_user_to_device (void *abstract_cr, + double *x, + double *y) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_user_to_device (cr->gstate, x, y); +} + +static void +_cairo_default_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); +} + +static void +_cairo_default_context_device_to_user (void *abstract_cr, + double *x, + double *y) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_device_to_user (cr->gstate, x, y); +} + +static void +_cairo_default_context_device_to_user_distance (void *abstract_cr, + double *dx, + double *dy) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy); +} + +static void +_cairo_default_context_backend_to_user (void *abstract_cr, + double *x, + double *y) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_backend_to_user (cr->gstate, x, y); +} + +static void +_cairo_default_context_backend_to_user_distance (void *abstract_cr, double *dx, double *dy) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_backend_to_user_distance (cr->gstate, dx, dy); +} + +static void +_cairo_default_context_user_to_backend (void *abstract_cr, + double *x, + double *y) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_user_to_backend (cr->gstate, x, y); +} + +static void +_cairo_default_context_user_to_backend_distance (void *abstract_cr, + double *dx, + double *dy) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_user_to_backend_distance (cr->gstate, dx, dy); +} + +/* Path constructor */ + +static cairo_status_t +_cairo_default_context_new_path (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_path_fixed_fini (cr->path); + _cairo_path_fixed_init (cr->path); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_default_context_new_sub_path (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_path_fixed_new_sub_path (cr->path); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_default_context_move_to (void *abstract_cr, double x, double y) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t x_fixed, y_fixed; + + _cairo_gstate_user_to_backend (cr->gstate, &x, &y); + x_fixed = _cairo_fixed_from_double (x); + y_fixed = _cairo_fixed_from_double (y); + + return _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed); +} + +static cairo_status_t +_cairo_default_context_line_to (void *abstract_cr, double x, double y) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t x_fixed, y_fixed; + + _cairo_gstate_user_to_backend (cr->gstate, &x, &y); + x_fixed = _cairo_fixed_from_double (x); + y_fixed = _cairo_fixed_from_double (y); + + return _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); +} + +static cairo_status_t +_cairo_default_context_curve_to (void *abstract_cr, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t x1_fixed, y1_fixed; + cairo_fixed_t x2_fixed, y2_fixed; + cairo_fixed_t x3_fixed, y3_fixed; + + _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1); + _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2); + _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3); + + x1_fixed = _cairo_fixed_from_double (x1); + y1_fixed = _cairo_fixed_from_double (y1); + + x2_fixed = _cairo_fixed_from_double (x2); + y2_fixed = _cairo_fixed_from_double (y2); + + x3_fixed = _cairo_fixed_from_double (x3); + y3_fixed = _cairo_fixed_from_double (y3); + + return _cairo_path_fixed_curve_to (cr->path, + x1_fixed, y1_fixed, + x2_fixed, y2_fixed, + x3_fixed, y3_fixed); +} + +static cairo_status_t +_cairo_default_context_arc (void *abstract_cr, + double xc, double yc, double radius, + double angle1, double angle2, + cairo_bool_t forward) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_status_t status; + + /* Do nothing, successfully, if radius is <= 0 */ + if (radius <= 0.0) { + cairo_fixed_t x_fixed, y_fixed; + + _cairo_gstate_user_to_backend (cr->gstate, &xc, &yc); + x_fixed = _cairo_fixed_from_double (xc); + y_fixed = _cairo_fixed_from_double (yc); + status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_default_context_line_to (cr, + xc + radius * cos (angle1), + yc + radius * sin (angle1)); + + if (unlikely (status)) + return status; + + if (forward) + _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2); + else + _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2); + + return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */ +} + +static cairo_status_t +_cairo_default_context_rel_move_to (void *abstract_cr, double dx, double dy) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t dx_fixed, dy_fixed; + + _cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy); + + dx_fixed = _cairo_fixed_from_double (dx); + dy_fixed = _cairo_fixed_from_double (dy); + + return _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed); +} + +static cairo_status_t +_cairo_default_context_rel_line_to (void *abstract_cr, double dx, double dy) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t dx_fixed, dy_fixed; + + _cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy); + + dx_fixed = _cairo_fixed_from_double (dx); + dy_fixed = _cairo_fixed_from_double (dy); + + return _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed); +} + + +static cairo_status_t +_cairo_default_context_rel_curve_to (void *abstract_cr, + double dx1, double dy1, + double dx2, double dy2, + double dx3, double dy3) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t dx1_fixed, dy1_fixed; + cairo_fixed_t dx2_fixed, dy2_fixed; + cairo_fixed_t dx3_fixed, dy3_fixed; + + _cairo_gstate_user_to_backend_distance (cr->gstate, &dx1, &dy1); + _cairo_gstate_user_to_backend_distance (cr->gstate, &dx2, &dy2); + _cairo_gstate_user_to_backend_distance (cr->gstate, &dx3, &dy3); + + dx1_fixed = _cairo_fixed_from_double (dx1); + dy1_fixed = _cairo_fixed_from_double (dy1); + + dx2_fixed = _cairo_fixed_from_double (dx2); + dy2_fixed = _cairo_fixed_from_double (dy2); + + dx3_fixed = _cairo_fixed_from_double (dx3); + dy3_fixed = _cairo_fixed_from_double (dy3); + + return _cairo_path_fixed_rel_curve_to (cr->path, + dx1_fixed, dy1_fixed, + dx2_fixed, dy2_fixed, + dx3_fixed, dy3_fixed); +} + +static cairo_status_t +_cairo_default_context_close_path (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_path_fixed_close_path (cr->path); +} + +static cairo_status_t +_cairo_default_context_rectangle (void *abstract_cr, + double x, double y, + double width, double height) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_status_t status; + + status = _cairo_default_context_move_to (cr, x, y); + if (unlikely (status)) + return status; + + status = _cairo_default_context_rel_line_to (cr, width, 0); + if (unlikely (status)) + return status; + + status = _cairo_default_context_rel_line_to (cr, 0, height); + if (unlikely (status)) + return status; + + status = _cairo_default_context_rel_line_to (cr, -width, 0); + if (unlikely (status)) + return status; + + return _cairo_default_context_close_path (cr); +} + +static void +_cairo_default_context_path_extents (void *abstract_cr, + double *x1, + double *y1, + double *x2, + double *y2) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_path_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); +} + +static cairo_bool_t +_cairo_default_context_has_current_point (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return cr->path->has_current_point; +} + +static cairo_bool_t +_cairo_default_context_get_current_point (void *abstract_cr, + double *x, + double *y) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_fixed_t x_fixed, y_fixed; + + if (_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed)) + { + *x = _cairo_fixed_to_double (x_fixed); + *y = _cairo_fixed_to_double (y_fixed); + _cairo_gstate_backend_to_user (cr->gstate, x, y); + + return TRUE; + } + else + { + return FALSE; + } +} + +static cairo_path_t * +_cairo_default_context_copy_path (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_path_create (cr->path, &cr->base); +} + +static cairo_path_t * +_cairo_default_context_copy_path_flat (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_path_create_flat (cr->path, &cr->base); +} + +static cairo_status_t +_cairo_default_context_append_path (void *abstract_cr, + const cairo_path_t *path) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_path_append_to_context (path, &cr->base); +} + +static cairo_status_t +_cairo_default_context_paint (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_paint (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_paint_with_alpha (void *abstract_cr, + double alpha) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_solid_pattern_t pattern; + cairo_status_t status; + cairo_color_t color; + + if (CAIRO_ALPHA_IS_OPAQUE (alpha)) + return _cairo_gstate_paint (cr->gstate); + + if (CAIRO_ALPHA_IS_ZERO (alpha) && + _cairo_operator_bounded_by_mask (cr->gstate->op)) { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_color_init_rgba (&color, 0., 0., 0., alpha); + _cairo_pattern_init_solid (&pattern, &color); + + status = _cairo_gstate_mask (cr->gstate, &pattern.base); + _cairo_pattern_fini (&pattern.base); + + return status; +} + +static cairo_status_t +_cairo_default_context_mask (void *abstract_cr, + cairo_pattern_t *mask) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_mask (cr->gstate, mask); +} + +static cairo_status_t +_cairo_default_context_stroke_preserve (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_stroke (cr->gstate, cr->path); +} + +static cairo_status_t +_cairo_default_context_stroke (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_status_t status; + + status = _cairo_gstate_stroke (cr->gstate, cr->path); + if (unlikely (status)) + return status; + + return _cairo_default_context_new_path (cr); +} + +static cairo_status_t +_cairo_default_context_in_stroke (void *abstract_cr, + double x, double y, + cairo_bool_t *inside) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_in_stroke (cr->gstate, + cr->path, + x, y, + inside); +} + +static cairo_status_t +_cairo_default_context_stroke_extents (void *abstract_cr, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_stroke_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); +} + +static cairo_status_t +_cairo_default_context_fill_preserve (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_fill (cr->gstate, cr->path); +} + +static cairo_status_t +_cairo_default_context_fill (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_status_t status; + + status = _cairo_gstate_fill (cr->gstate, cr->path); + if (unlikely (status)) + return status; + + return _cairo_default_context_new_path (cr); +} + +static cairo_status_t +_cairo_default_context_in_fill (void *abstract_cr, + double x, double y, + cairo_bool_t *inside) +{ + cairo_default_context_t *cr = abstract_cr; + + *inside = _cairo_gstate_in_fill (cr->gstate, + cr->path, + x, y); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_default_context_fill_extents (void *abstract_cr, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_fill_extents (cr->gstate, + cr->path, + x1, y1, x2, y2); +} + +static cairo_status_t +_cairo_default_context_clip_preserve (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_clip (cr->gstate, cr->path); +} + +static cairo_status_t +_cairo_default_context_clip (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_status_t status; + + status = _cairo_gstate_clip (cr->gstate, cr->path); + if (unlikely (status)) + return status; + + return _cairo_default_context_new_path (cr); +} + +static cairo_status_t +_cairo_default_context_in_clip (void *abstract_cr, + double x, double y, + cairo_bool_t *inside) +{ + cairo_default_context_t *cr = abstract_cr; + + *inside = _cairo_gstate_in_clip (cr->gstate, x, y); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_default_context_reset_clip (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_reset_clip (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_clip_extents (void *abstract_cr, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_default_context_t *cr = abstract_cr; + + if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) { + *x1 = -INFINITY; + *y1 = -INFINITY; + *x2 = +INFINITY; + *y2 = +INFINITY; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_rectangle_list_t * +_cairo_default_context_copy_clip_rectangle_list (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_copy_clip_rectangle_list (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_copy_page (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_copy_page (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_tag_begin (void *abstract_cr, + const char *tag_name, const char *attributes) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_tag_begin (cr->gstate, tag_name, attributes); +} + +static cairo_status_t +_cairo_default_context_tag_end (void *abstract_cr, + const char *tag_name) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_tag_end (cr->gstate, tag_name); +} + +static cairo_status_t +_cairo_default_context_show_page (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_show_page (cr->gstate); +} + +static cairo_status_t +_cairo_default_context_set_font_face (void *abstract_cr, + cairo_font_face_t *font_face) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_font_face (cr->gstate, font_face); +} + +static cairo_font_face_t * +_cairo_default_context_get_font_face (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_font_face_t *font_face; + cairo_status_t status; + + status = _cairo_gstate_get_font_face (cr->gstate, &font_face); + if (unlikely (status)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *) &_cairo_font_face_nil; + } + + return font_face; +} + +static cairo_status_t +_cairo_default_context_font_extents (void *abstract_cr, + cairo_font_extents_t *extents) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_font_extents (cr->gstate, extents); +} + +static cairo_status_t +_cairo_default_context_set_font_size (void *abstract_cr, + double size) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_font_size (cr->gstate, size); +} + +static cairo_status_t +_cairo_default_context_set_font_matrix (void *abstract_cr, + const cairo_matrix_t *matrix) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_font_matrix (cr->gstate, matrix); +} + +static void +_cairo_default_context_get_font_matrix (void *abstract_cr, + cairo_matrix_t *matrix) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_get_font_matrix (cr->gstate, matrix); +} + +static cairo_status_t +_cairo_default_context_set_font_options (void *abstract_cr, + const cairo_font_options_t *options) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_set_font_options (cr->gstate, options); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_default_context_get_font_options (void *abstract_cr, + cairo_font_options_t *options) +{ + cairo_default_context_t *cr = abstract_cr; + + _cairo_gstate_get_font_options (cr->gstate, options); +} + +static cairo_status_t +_cairo_default_context_set_scaled_font (void *abstract_cr, + cairo_scaled_font_t *scaled_font) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_bool_t was_previous; + cairo_status_t status; + + if (scaled_font == cr->gstate->scaled_font) + return CAIRO_STATUS_SUCCESS; + + was_previous = scaled_font == cr->gstate->previous_scaled_font; + + status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); + if (unlikely (status)) + return status; + + status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); + if (unlikely (status)) + return status; + + _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); + + if (was_previous) + cr->gstate->scaled_font = cairo_scaled_font_reference (scaled_font); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_scaled_font_t * +_cairo_default_context_get_scaled_font (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + cairo_scaled_font_t *scaled_font; + cairo_status_t status; + + status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font); + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + + return scaled_font; +} + +static cairo_status_t +_cairo_default_context_glyphs (void *abstract_cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_glyph_text_info_t *info) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_show_text_glyphs (cr->gstate, glyphs, num_glyphs, info); +} + +static cairo_status_t +_cairo_default_context_glyph_path (void *abstract_cr, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_glyph_path (cr->gstate, + glyphs, num_glyphs, + cr->path); +} + +static cairo_status_t +_cairo_default_context_glyph_extents (void *abstract_cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents); +} + +static const cairo_backend_t _cairo_default_context_backend = { + CAIRO_TYPE_DEFAULT, + _cairo_default_context_destroy, + + _cairo_default_context_get_original_target, + _cairo_default_context_get_current_target, + + _cairo_default_context_save, + _cairo_default_context_restore, + + _cairo_default_context_push_group, + _cairo_default_context_pop_group, + + _cairo_default_context_set_source_rgba, + _cairo_default_context_set_source_surface, + _cairo_default_context_set_source, + _cairo_default_context_get_source, + + _cairo_default_context_set_antialias, + _cairo_default_context_set_dash, + _cairo_default_context_set_fill_rule, + _cairo_default_context_set_line_cap, + _cairo_default_context_set_line_join, + _cairo_default_context_set_line_width, + _cairo_default_context_set_miter_limit, + _cairo_default_context_set_opacity, + _cairo_default_context_set_operator, + _cairo_default_context_set_tolerance, + _cairo_default_context_get_antialias, + _cairo_default_context_get_dash, + _cairo_default_context_get_fill_rule, + _cairo_default_context_get_line_cap, + _cairo_default_context_get_line_join, + _cairo_default_context_get_line_width, + _cairo_default_context_get_miter_limit, + _cairo_default_context_get_opacity, + _cairo_default_context_get_operator, + _cairo_default_context_get_tolerance, + + _cairo_default_context_translate, + _cairo_default_context_scale, + _cairo_default_context_rotate, + _cairo_default_context_transform, + _cairo_default_context_set_matrix, + _cairo_default_context_set_identity_matrix, + _cairo_default_context_get_matrix, + + _cairo_default_context_user_to_device, + _cairo_default_context_user_to_device_distance, + _cairo_default_context_device_to_user, + _cairo_default_context_device_to_user_distance, + + _cairo_default_context_user_to_backend, + _cairo_default_context_user_to_backend_distance, + _cairo_default_context_backend_to_user, + _cairo_default_context_backend_to_user_distance, + + _cairo_default_context_new_path, + _cairo_default_context_new_sub_path, + _cairo_default_context_move_to, + _cairo_default_context_rel_move_to, + _cairo_default_context_line_to, + _cairo_default_context_rel_line_to, + _cairo_default_context_curve_to, + _cairo_default_context_rel_curve_to, + NULL, /* arc-to */ + NULL, /* rel-arc-to */ + _cairo_default_context_close_path, + _cairo_default_context_arc, + _cairo_default_context_rectangle, + _cairo_default_context_path_extents, + _cairo_default_context_has_current_point, + _cairo_default_context_get_current_point, + _cairo_default_context_copy_path, + _cairo_default_context_copy_path_flat, + _cairo_default_context_append_path, + + NULL, /* stroke-to-path */ + + _cairo_default_context_clip, + _cairo_default_context_clip_preserve, + _cairo_default_context_in_clip, + _cairo_default_context_clip_extents, + _cairo_default_context_reset_clip, + _cairo_default_context_copy_clip_rectangle_list, + + _cairo_default_context_paint, + _cairo_default_context_paint_with_alpha, + _cairo_default_context_mask, + + _cairo_default_context_stroke, + _cairo_default_context_stroke_preserve, + _cairo_default_context_in_stroke, + _cairo_default_context_stroke_extents, + + _cairo_default_context_fill, + _cairo_default_context_fill_preserve, + _cairo_default_context_in_fill, + _cairo_default_context_fill_extents, + + _cairo_default_context_set_font_face, + _cairo_default_context_get_font_face, + _cairo_default_context_set_font_size, + _cairo_default_context_set_font_matrix, + _cairo_default_context_get_font_matrix, + _cairo_default_context_set_font_options, + _cairo_default_context_get_font_options, + _cairo_default_context_set_scaled_font, + _cairo_default_context_get_scaled_font, + _cairo_default_context_font_extents, + + _cairo_default_context_glyphs, + _cairo_default_context_glyph_path, + _cairo_default_context_glyph_extents, + + _cairo_default_context_copy_page, + _cairo_default_context_show_page, + + _cairo_default_context_tag_begin, + _cairo_default_context_tag_end, +}; + +cairo_status_t +_cairo_default_context_init (cairo_default_context_t *cr, void *target) +{ + _cairo_init (&cr->base, &_cairo_default_context_backend); + _cairo_path_fixed_init (cr->path); + + cr->gstate = &cr->gstate_tail[0]; + cr->gstate_freelist = &cr->gstate_tail[1]; + cr->gstate_tail[1].next = NULL; + + return _cairo_gstate_init (cr->gstate, target); +} + +cairo_t * +_cairo_default_context_create (void *target) +{ + cairo_default_context_t *cr; + cairo_status_t status; + + cr = _freed_pool_get (&context_pool); + if (unlikely (cr == NULL)) { + cr = _cairo_malloc (sizeof (cairo_default_context_t)); + if (unlikely (cr == NULL)) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + status = _cairo_default_context_init (cr, target); + if (unlikely (status)) { + _freed_pool_put (&context_pool, cr); + return _cairo_create_in_error (status); + } + + return &cr->base; +} diff --git a/gfx/cairo/cairo/src/cairo-deflate-stream.c b/gfx/cairo/cairo/src/cairo-deflate-stream.c index ba5f18392b4b..b51a6399cd00 100644 --- a/gfx/cairo/cairo/src/cairo-deflate-stream.c +++ b/gfx/cairo/cairo/src/cairo-deflate-stream.c @@ -35,6 +35,9 @@ */ #include "cairoint.h" + +#if CAIRO_HAS_DEFLATE_STREAM + #include "cairo-error-private.h" #include "cairo-output-stream-private.h" #include @@ -121,7 +124,7 @@ _cairo_deflate_stream_create (cairo_output_stream_t *output) if (output->status) return _cairo_output_stream_create_in_error (output->status); - stream = malloc (sizeof (cairo_deflate_stream_t)); + stream = _cairo_malloc (sizeof (cairo_deflate_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; @@ -149,3 +152,5 @@ _cairo_deflate_stream_create (cairo_output_stream_t *output) return &stream->base; } + +#endif /* CAIRO_HAS_DEFLATE_STREAM */ diff --git a/gfx/cairo/cairo/src/cairo-device.c b/gfx/cairo/cairo/src/cairo-device.c index 15b048477598..965c84c6519f 100644 --- a/gfx/cairo/cairo/src/cairo-device.c +++ b/gfx/cairo/cairo/src/cairo-device.c @@ -92,7 +92,7 @@ * interactions with existing surface API of the device functions for * surfaces of that type. * - */ + **/ static const cairo_device_t _nil_device = { CAIRO_REFERENCE_COUNT_INVALID, @@ -156,6 +156,13 @@ _cairo_device_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_WEIGHT: case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: case CAIRO_STATUS_INVALID_CONTENT: + case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: + case CAIRO_STATUS_DEVICE_FINISHED: + case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_device_t *) &_nil_device; @@ -187,8 +194,8 @@ _cairo_device_init (cairo_device_t *device, * @device from being destroyed until a matching call to * cairo_device_destroy() is made. * - * The number of references to a #cairo_device_t can be get using - * cairo_device_get_reference_count(). + * Use cairo_device_get_reference_count() to get the number of references + * to a #cairo_device_t. * * Return value: the referenced #cairo_device_t. * @@ -254,6 +261,9 @@ cairo_device_flush (cairo_device_t *device) if (device == NULL || device->status) return; + if (device->finished) + return; + if (device->backend->flush != NULL) { status = device->backend->flush (device); if (unlikely (status)) @@ -295,10 +305,14 @@ cairo_device_finish (cairo_device_t *device) cairo_device_flush (device); - device->finished = TRUE; - if (device->backend->finish != NULL) device->backend->finish (device); + + /* We only finish the device after the backend's callback returns because + * the device might still be needed during the callback + * (e.g. for cairo_device_acquire ()). + */ + device->finished = TRUE; } slim_hidden_def (cairo_device_finish); @@ -360,7 +374,7 @@ cairo_device_get_type (cairo_device_t *device) if (device == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) { - return (cairo_device_type_t) -1; + return CAIRO_DEVICE_TYPE_INVALID; } return device->backend->type; @@ -406,7 +420,7 @@ cairo_device_acquire (cairo_device_t *device) return device->status; if (unlikely (device->finished)) - return _cairo_device_set_error (device, CAIRO_STATUS_SURFACE_FINISHED); /* XXX */ + return _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_FINISHED); CAIRO_MUTEX_LOCK (device->mutex); if (device->mutex_depth++ == 0) { @@ -448,11 +462,9 @@ cairo_status_t _cairo_device_set_error (cairo_device_t *device, cairo_status_t status) { - if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED) - return status; + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; - /* Don't overwrite an existing error. This preserves the first - * error, which is the most significant. */ _cairo_status_set_error (&device->status, status); return _cairo_error (status); diff --git a/gfx/cairo/cairo/src/cairo-directfb-surface.c b/gfx/cairo/cairo/src/cairo-directfb-surface.c index fc7509c1a490..0deedf0d5490 100644 --- a/gfx/cairo/cairo/src/cairo-directfb-surface.c +++ b/gfx/cairo/cairo/src/cairo-directfb-surface.c @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2003 University of Southern California + * Copyright © 2012 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 @@ -27,19 +27,23 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is University of Southern - * California. + * The Initial Developer of the Original Code is Chris Wilson * * Contributor(s): - * Michael Emmel - * Claudio Ciccani + * Chris Wilson */ #include "cairoint.h" #include "cairo-directfb.h" #include "cairo-clip-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-pattern-private.h" +#include "cairo-surface-backend-private.h" +#include "cairo-surface-fallback-private.h" #include @@ -49,176 +53,36 @@ #include #include -/* - * Rectangle works fine. - * Bugs 361377, 359553, 359243 in Gnome BTS are caused - * by GDK/DirectFB, not by Cairo/DirectFB. - */ -#define DFB_RECTANGLES 1 +slim_hidden_proto(cairo_directfb_surface_create); -/* - * Composite works fine. - */ -#define DFB_COMPOSITE 1 - -/* - * CompositeTrapezoids works (without antialiasing). - */ -#define DFB_COMPOSITE_TRAPEZOIDS 1 - -/* - * ShowGlyphs works fine. - */ -#define DFB_SHOW_GLYPHS 1 - -#define PIXMAN_invalid (pixman_format_code_t) 0 - - -D_DEBUG_DOMAIN (CairoDFB_Acquire, "CairoDFB/Acquire", "Cairo DirectFB Acquire"); -D_DEBUG_DOMAIN (CairoDFB_Clip, "CairoDFB/Clip", "Cairo DirectFB Clipping"); -D_DEBUG_DOMAIN (CairoDFB_Font, "CairoDFB/Font", "Cairo DirectFB Font Rendering"); -D_DEBUG_DOMAIN (CairoDFB_Render, "CairoDFB/Render", "Cairo DirectFB Rendering"); -D_DEBUG_DOMAIN (CairoDFB_Surface, "CairoDFB/Surface", "Cairo DirectFB Surface"); - -/*****************************************************************************/ - -typedef struct _cairo_directfb_surface { - cairo_surface_t base; - - pixman_format_code_t pixman_format; - cairo_bool_t supported_destination; +typedef struct _cairo_dfb_surface { + cairo_image_surface_t image; IDirectFB *dfb; - IDirectFBSurface *dfbsurface; - IDirectFBSurface *tmpsurface; - pixman_format_code_t tmpformat; + IDirectFBSurface *dfb_surface; - int width; - int height; - - unsigned local : 1; unsigned blit_premultiplied : 1; -} cairo_directfb_surface_t; - - -typedef struct _cairo_directfb_font_cache { - IDirectFB *dfb; - IDirectFBSurface *dfbsurface; - - int width; - int height; - - /* coordinates within the surface - * of the last loaded glyph */ - int x; - int y; -} cairo_directfb_font_cache_t; - -static cairo_surface_backend_t _cairo_directfb_surface_backend; - -/*****************************************************************************/ - -static int _directfb_argb_font = 0; - -/*****************************************************************************/ - -#define RUN_CLIPPED(surface, clip_region, clip, func) {\ - if ((clip_region) != NULL) {\ - int n_clips = cairo_region_num_rectangles (clip_region), n; \ - for (n = 0; n < n_clips; n++) {\ - if (clip) {\ - DFBRegion reg, *cli = (clip); \ - cairo_rectangle_int_t rect; \ - cairo_region_get_rectangle (clip_region, n, &rect); \ - reg.x1 = rect.x; \ - reg.y1 = rect.y; \ - reg.x2 = rect.x + rect.width - 1; \ - reg.y2 = rect.y + rect.height - 1; \ - if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\ - reg.x1 > cli->x2 || reg.y1 > cli->y2)\ - continue;\ - if (reg.x1 < cli->x1)\ - reg.x1 = cli->x1;\ - if (reg.y1 < cli->y1)\ - reg.y1 = cli->y1;\ - if (reg.x2 > cli->x2)\ - reg.x2 = cli->x2;\ - if (reg.y2 > cli->y2)\ - reg.y2 = cli->y2;\ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®);\ - } else {\ - DFBRegion reg; \ - cairo_rectangle_int_t rect; \ - cairo_region_get_rectangle (clip_region, n, &rect); \ - reg.x1 = rect.x; \ - reg.y1 = rect.y; \ - reg.x2 = rect.x + rect.width - 1; \ - reg.y2 = rect.y + rect.height - 1; \ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®); \ - }\ - func;\ - }\ - } else {\ - (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\ - func;\ - }\ -} - -#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) do { \ - double _x = (x); \ - double _y = (y); \ - (ret_x) = (_x * (m).xx + (m).x0); \ - (ret_y) = (_y * (m).yy + (m).y0); \ -} while (0) - -#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) do { \ - double _x = (x); \ - double _y = (y); \ - (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0); \ - (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0); \ -} while (0) - -/* XXX: A1 has a different bits ordering in cairo. - * Probably we should drop it. - */ +} cairo_dfb_surface_t; static cairo_content_t _directfb_format_to_content (DFBSurfacePixelFormat format) { - if (DFB_PIXELFORMAT_HAS_ALPHA (format)) { - if (DFB_COLOR_BITS_PER_PIXEL (format)) - return CAIRO_CONTENT_COLOR_ALPHA; + cairo_content_t content = 0; - return CAIRO_CONTENT_ALPHA; - } + if (DFB_PIXELFORMAT_HAS_ALPHA (format)) + content |= CAIRO_CONTENT_ALPHA; + if (DFB_COLOR_BITS_PER_PIXEL (format)) + content |= CAIRO_CONTENT_COLOR_ALPHA; - return CAIRO_CONTENT_COLOR; -} - -static inline DFBSurfacePixelFormat -_cairo_to_directfb_format (cairo_format_t format) -{ - switch (format) { - case CAIRO_FORMAT_RGB24: - return DSPF_RGB32; - case CAIRO_FORMAT_ARGB32: - return DSPF_ARGB; - case CAIRO_FORMAT_A8: - return DSPF_A8; - case CAIRO_FORMAT_A1: - return DSPF_A1; - default: - break; - } - - return -1; + assert(content); + return content; } static inline pixman_format_code_t _directfb_to_pixman_format (DFBSurfacePixelFormat format) { switch (format) { - case DSPF_UNKNOWN: return PIXMAN_invalid; + case DSPF_UNKNOWN: return 0; case DSPF_ARGB1555: return PIXMAN_a1r5g5b5; case DSPF_RGB16: return PIXMAN_r5g6b5; case DSPF_RGB24: return PIXMAN_r8g8b8; @@ -227,33 +91,144 @@ _directfb_to_pixman_format (DFBSurfacePixelFormat format) case DSPF_A8: return PIXMAN_a8; case DSPF_YUY2: return PIXMAN_yuy2; case DSPF_RGB332: return PIXMAN_r3g3b2; - case DSPF_UYVY: return PIXMAN_invalid; - case DSPF_I420: return PIXMAN_invalid; + case DSPF_UYVY: return 0; + case DSPF_I420: return 0; case DSPF_YV12: return PIXMAN_yv12; - case DSPF_LUT8: return PIXMAN_invalid; - case DSPF_ALUT44: return PIXMAN_invalid; - case DSPF_AiRGB: return PIXMAN_invalid; - case DSPF_A1: return PIXMAN_a1; /* bit reversed, oops */ - case DSPF_NV12: return PIXMAN_invalid; - case DSPF_NV16: return PIXMAN_invalid; - case DSPF_ARGB2554: return PIXMAN_invalid; + case DSPF_LUT8: return 0; + case DSPF_ALUT44: return 0; + case DSPF_AiRGB: return 0; + case DSPF_A1: return 0; /* bit reversed, oops */ + case DSPF_NV12: return 0; + case DSPF_NV16: return 0; + case DSPF_ARGB2554: return 0; case DSPF_ARGB4444: return PIXMAN_a4r4g4b4; - case DSPF_NV21: return PIXMAN_invalid; - case DSPF_AYUV: return PIXMAN_invalid; + case DSPF_NV21: return 0; + case DSPF_AYUV: return 0; case DSPF_A4: return PIXMAN_a4; - case DSPF_ARGB1666: return PIXMAN_invalid; - case DSPF_ARGB6666: return PIXMAN_invalid; - case DSPF_RGB18: return PIXMAN_invalid; - case DSPF_LUT2: return PIXMAN_invalid; + case DSPF_ARGB1666: return 0; + case DSPF_ARGB6666: return 0; + case DSPF_RGB18: return 0; + case DSPF_LUT2: return 0; case DSPF_RGB444: return PIXMAN_x4r4g4b4; case DSPF_RGB555: return PIXMAN_x1r5g5b5; #if DFB_NUM_PIXELFORMATS >= 29 case DSPF_BGR555: return PIXMAN_x1b5g5r5; #endif } - return PIXMAN_invalid; + return 0; } +static cairo_surface_t * +_cairo_dfb_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_dfb_surface_t *other = abstract_src; + DFBSurfacePixelFormat format; + IDirectFBSurface *buffer; + DFBSurfaceDescription dsc; + cairo_surface_t *surface; + + if (width <= 0 || height <= 0) + return _cairo_image_surface_create_with_content (content, width, height); + + switch (content) { + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + format = DSPF_ARGB; + break; + case CAIRO_CONTENT_COLOR: + format = DSPF_RGB32; + break; + case CAIRO_CONTENT_ALPHA: + format = DSPF_A8; + break; + } + + dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT; + dsc.caps = DSCAPS_PREMULTIPLIED; + dsc.width = width; + dsc.height = height; + dsc.pixelformat = format; + + if (other->dfb->CreateSurface (other->dfb, &dsc, &buffer)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_ERROR)); + + surface = cairo_directfb_surface_create (other->dfb, buffer); + buffer->Release (buffer); + + return surface; +} + +static cairo_status_t +_cairo_dfb_surface_finish (void *abstract_surface) +{ + cairo_dfb_surface_t *surface = abstract_surface; + + surface->dfb_surface->Release (surface->dfb_surface); + return _cairo_image_surface_finish (abstract_surface); +} + +static cairo_image_surface_t * +_cairo_dfb_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_dfb_surface_t *surface = abstract_surface; + + if (surface->image.pixman_image == NULL) { + IDirectFBSurface *buffer = surface->dfb_surface; + pixman_image_t *image; + void *data; + int pitch; + + if (buffer->Lock (buffer, DSLF_READ | DSLF_WRITE, &data, &pitch)) + return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + image = pixman_image_create_bits (surface->image.pixman_format, + surface->image.width, + surface->image.height, + data, pitch); + if (image == NULL) { + buffer->Unlock (buffer); + return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + _cairo_image_surface_init (&surface->image, image, surface->image.pixman_format); + } + + return _cairo_image_surface_map_to_image (&surface->image.base, extents); +} + +static cairo_int_status_t +_cairo_dfb_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_dfb_surface_t *surface = abstract_surface; + return _cairo_image_surface_unmap_image (&surface->image.base, image); +} + +static cairo_status_t +_cairo_dfb_surface_flush (void *abstract_surface, + unsigned flags) +{ + cairo_dfb_surface_t *surface = abstract_surface; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->image.pixman_image) { + surface->dfb_surface->Unlock (surface->dfb_surface); + + pixman_image_unref (surface->image.pixman_image); + surface->image.pixman_image = NULL; + surface->image.data = NULL; + } + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 static inline DFBSurfacePixelFormat _directfb_from_pixman_format (pixman_format_code_t format) { @@ -371,808 +346,56 @@ _directfb_get_operator (cairo_operator_t operator, return TRUE; } - -static cairo_status_t -_directfb_buffer_surface_create (IDirectFB *dfb, - DFBSurfacePixelFormat format, - int width, - int height, - IDirectFBSurface **out) -{ - IDirectFBSurface *buffer; - DFBSurfaceDescription dsc; - DFBResult ret; - - dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT; - dsc.caps = DSCAPS_PREMULTIPLIED; - dsc.width = width; - dsc.height = height; - dsc.pixelformat = format; - - ret = dfb->CreateSurface (dfb, &dsc, &buffer); - if (ret) { - DirectFBError ("IDirectFB::CreateSurface()", ret); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - *out = buffer; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_directfb_acquire_surface (cairo_directfb_surface_t *surface, - cairo_rectangle_int_t *intrest_rec, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra, - DFBSurfaceLockFlags lock_flags) -{ - IDirectFBSurface *buffer = NULL; - DFBRectangle source_rect; - cairo_surface_t *image; - pixman_image_t *pixman_image; - pixman_format_code_t pixman_format; - cairo_status_t status; - void *data; - int pitch; - - if (surface->pixman_format == PIXMAN_invalid) { - if (intrest_rec != NULL) { - source_rect.x = intrest_rec->x; - source_rect.y = intrest_rec->y; - source_rect.w = intrest_rec->width; - source_rect.h = intrest_rec->height; - } else { - source_rect.x = 0; - source_rect.y = 0; - surface->dfbsurface->GetSize (surface->dfbsurface, - &source_rect.w, &source_rect.h); - } - - if (surface->tmpsurface != NULL) { - int w, h; - - surface->tmpsurface->GetSize (surface->tmpsurface, &w, &h); - if (w < source_rect.w || h < source_rect.h) { - surface->tmpsurface->Release (surface->tmpsurface); - surface->tmpsurface = NULL; - surface->tmpformat = PIXMAN_invalid; - } - } - - if (surface->tmpsurface == NULL) { - DFBSurfacePixelFormat format; - - D_DEBUG_AT (CairoDFB_Acquire, "Allocating buffer for surface %p.\n", surface); - - format = _cairo_to_directfb_format (_cairo_format_from_content (surface->base.content)); - status = - _directfb_buffer_surface_create (surface->dfb, format, - source_rect.w, source_rect.h, - &surface->tmpsurface); - if (unlikely (status)) - goto ERROR; - - surface->tmpformat = _directfb_to_pixman_format (format); - } - buffer = surface->tmpsurface; - pixman_format = surface->tmpformat; - - -/* surface->dfbsurface->GetCapabilities (surface->dfbsurface, &caps); - DFBSurfaceCapabilities caps; - if (caps & DSCAPS_FLIPPING) { - DFBRegion region = { .x1 = source_rect.x, .y1 = source_rect.y, - .x2 = source_rect.x + source_rect.w - 1, - .y2 = source_rect.y + source_rect.h - 1 }; - surface->dfbsurface->Flip (surface->dfbsurface, ®ion, DSFLIP_BLIT); - } */ - buffer->Blit (buffer, surface->dfbsurface, &source_rect, 0, 0); - } else { - /*might be a subsurface get the offset*/ - surface->dfbsurface->GetVisibleRectangle (surface->dfbsurface, &source_rect); - pixman_format = surface->pixman_format; - buffer = surface->dfbsurface; - } - - if (buffer->Lock (buffer, lock_flags, &data, &pitch)) { - D_DEBUG_AT (CairoDFB_Acquire, "Couldn't lock surface!\n"); - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto ERROR; - } - - pixman_image = pixman_image_create_bits (pixman_format, - source_rect.w, source_rect.h, - data, pitch); - if (pixman_image == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto ERROR; - } - - image = _cairo_image_surface_create_for_pixman_image (pixman_image, - pixman_format); - status = image->status; - if (status) - goto ERROR; - - if (image_rect_out) { - image_rect_out->x = source_rect.x; - image_rect_out->y = source_rect.y; - image_rect_out->width = source_rect.w; - image_rect_out->height = source_rect.h; - } else { - /* lock for read */ - /* might be a subsurface */ - if (buffer == surface->dfbsurface) { - cairo_surface_set_device_offset (image, - source_rect.x, source_rect.y); - } - } - - *image_extra = buffer; - *image_out = (cairo_image_surface_t *) image; - return CAIRO_STATUS_SUCCESS; - -ERROR: - if (buffer) { - buffer->Unlock (buffer); - if (buffer != surface->dfbsurface) - buffer->Release (buffer); - } - return status; -} - -static cairo_surface_t * -_cairo_directfb_surface_create_internal (IDirectFB *dfb, - DFBSurfacePixelFormat format, - cairo_content_t content, - int width, - int height) -{ - cairo_directfb_surface_t *surface; - cairo_status_t status; - - surface = calloc (1, sizeof (cairo_directfb_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface->dfb = dfb; - - if (width < 8 || height < 8) { - IDirectFBSurface *tmp; - DFBRectangle rect = { .x=0, .y=0, .w=width, .h=height }; - - /* Some cards (e.g. Matrox) don't support surfaces smaller than 8x8 */ - status = _directfb_buffer_surface_create (dfb, format, - MAX (width, 8), MAX (height, 8), - &tmp); - if (status) { - free (surface); - return _cairo_surface_create_in_error (status); - } - - tmp->GetSubSurface (tmp, &rect, &surface->dfbsurface); - tmp->Release (tmp); - } else { - status = _directfb_buffer_surface_create (dfb, format, - width, height, - &surface->dfbsurface); - if (status) { - free (surface); - return _cairo_surface_create_in_error (status); - } - } - - _cairo_surface_init (&surface->base, - &_cairo_directfb_surface_backend, - NULL, /* device */ - content); - surface->pixman_format = _directfb_to_pixman_format (format); - surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); - - surface->width = width; - surface->height = height; - surface->local = TRUE; - surface->blit_premultiplied = TRUE; - - return &surface->base; -} - -static cairo_surface_t * -_cairo_directfb_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - cairo_directfb_surface_t *other = abstract_src; - DFBSurfacePixelFormat format; - - D_DEBUG_AT (CairoDFB_Surface, - "%s( src=%p, content=0x%x, width=%d, height=%d).\n", - __FUNCTION__, other, content, width, height); - - width = (width <= 0) ? 1 : width; - height = (height<= 0) ? 1 : height; - - format = _cairo_to_directfb_format (_cairo_format_from_content (content)); - return _cairo_directfb_surface_create_internal (other->dfb, format, - content, width, height); -} - -static cairo_status_t -_cairo_directfb_surface_finish (void *data) -{ - cairo_directfb_surface_t *surface = (cairo_directfb_surface_t *)data; - - D_DEBUG_AT (CairoDFB_Surface, "%s( surface=%p ).\n", __FUNCTION__, surface); - - if (surface->tmpsurface) { - surface->tmpsurface->Release (surface->tmpsurface); - surface->tmpsurface = NULL; - } - - if (surface->dfbsurface) { - surface->dfbsurface->Release (surface->dfbsurface); - surface->dfbsurface = NULL; - } - - if (surface->dfb) - surface->dfb = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_directfb_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p ).\n", __FUNCTION__, surface); - - return _directfb_acquire_surface (surface, NULL, image_out, - NULL, image_extra, DSLF_READ); -} - -static void -_cairo_directfb_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - IDirectFBSurface *buffer = image_extra; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( release=%p ).\n", __FUNCTION__, abstract_surface); - - buffer->Unlock (buffer); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_directfb_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_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p (%dx%d), interest_rect={ %u %u %u %u } ).\n", - __FUNCTION__, surface, surface->width, surface->height, - interest_rect ? interest_rect->x : 0, - interest_rect ? interest_rect->y : 0, - interest_rect ? interest_rect->width : (unsigned) surface->width, - interest_rect ? interest_rect->height : (unsigned) surface->height); - - return _directfb_acquire_surface (surface, interest_rect, image_out, - image_rect_out, image_extra, - DSLF_READ | DSLF_WRITE); -} - -static void -_cairo_directfb_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_directfb_surface_t *surface = abstract_surface; - IDirectFBSurface *buffer = image_extra; - - D_DEBUG_AT (CairoDFB_Acquire, - "%s( surface=%p ).\n", __FUNCTION__, surface); - - buffer->Unlock (buffer); - - if (surface->dfbsurface != buffer) { - DFBRegion region = { - .x1 = interest_rect->x, - .y1 = interest_rect->y, - .x2 = interest_rect->x + interest_rect->width - 1, - .y2 = interest_rect->y + interest_rect->height - 1 - }; - surface->dfbsurface->SetBlittingFlags (surface->dfbsurface, DSBLIT_NOFX); - surface->dfbsurface->SetClip (surface->dfbsurface, ®ion); - surface->dfbsurface->Blit (surface->dfbsurface, - buffer, NULL, - image_rect->x, image_rect->y); - } - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_directfb_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_directfb_surface_t *surface = abstract_surface; - cairo_directfb_surface_t *clone; - - D_DEBUG_AT (CairoDFB_Surface, - "%s( surface=%p, src=%p ).\n", __FUNCTION__, surface, src); - - if (src->backend == surface->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - DFBSurfacePixelFormat format; - DFBResult ret; - pixman_image_t *pixman_image; - void *data; - int pitch; - - format = _directfb_from_pixman_format (image_src->pixman_format); - if (format == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - clone = (cairo_directfb_surface_t *) - _cairo_directfb_surface_create_internal (surface->dfb, format, - image_src->base.content, - width, height); - if (unlikely (clone->base.status)) - return clone->base.status; - - ret = clone->dfbsurface->Lock (clone->dfbsurface, - DSLF_WRITE, (void *)&data, &pitch); - if (ret) { - DirectFBError ("IDirectFBSurface::Lock()", ret); - cairo_surface_destroy (&clone->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image = pixman_image_create_bits (clone->pixman_format, - width, height, - data, pitch); - if (unlikely (pixman_image == NULL)) { - DirectFBError ("IDirectFBSurface::Lock()", ret); - cairo_surface_destroy (&clone->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - image_src->pixman_image, - NULL, - pixman_image, - src_x, src_y, - 0, 0, - 0, 0, - width, height); - - pixman_image_unref (pixman_image); - - clone->dfbsurface->Unlock (clone->dfbsurface); - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &clone->base; - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -#if DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS -static cairo_int_status_t -_directfb_prepare_composite (cairo_directfb_surface_t *dst, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - cairo_operator_t op, - int *src_x, int *src_y, - int *mask_x, int *mask_y, - unsigned int width, - unsigned int height, - cairo_directfb_surface_t **ret_src, - cairo_surface_attributes_t *ret_src_attr) -{ - cairo_directfb_surface_t *src; - cairo_surface_attributes_t src_attr; - cairo_status_t status; - DFBSurfaceBlittingFlags flags; - DFBSurfaceBlendFunction sblend; - DFBSurfaceBlendFunction dblend; - const cairo_color_t *color; - - /* XXX Unbounded operators are not handled correctly */ - if (! _cairo_operator_bounded_by_source (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _directfb_get_operator (op, &sblend, &dblend)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (mask_pattern) { - return CAIRO_INT_STATUS_UNSUPPORTED; - if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) { - const cairo_pattern_t *tmp; - int tmp_x, tmp_y; - - if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID || - sblend == DSBF_INVDESTALPHA) /* Doesn't work correctly */ - return CAIRO_INT_STATUS_UNSUPPORTED; - - D_DEBUG_AT (CairoDFB_Render, "Replacing src pattern by mask pattern.\n"); - - tmp = src_pattern; - tmp_x = *src_x; tmp_y = *src_y; - - src_pattern = mask_pattern; - *src_x = *mask_x; *src_y = *mask_y; - - mask_pattern = tmp; - *mask_x = tmp_x; *mask_y = tmp_y; - - if (sblend == DSBF_ONE) { - sblend = DSBF_SRCALPHA; - /*dblend = DSBF_INVSRCALPHA;*/ - } - } - - color = &((cairo_solid_pattern_t *) mask_pattern)->color; - } else { - color = _cairo_stock_color (CAIRO_STOCK_WHITE); - } - - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - *src_x, *src_y, width, height, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT, - (cairo_surface_t **) &src, - &src_attr); - if (status) - return status; - - if (src->base.backend != &_cairo_directfb_surface_backend || - src->dfb != dst->dfb) - { - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if ((src->base.content & CAIRO_CONTENT_ALPHA) == 0) { - if (sblend == DSBF_SRCALPHA) - sblend = DSBF_ONE; - else if (sblend == DSBF_INVSRCALPHA) - sblend = DSBF_ZERO; - - if (dblend == DSBF_SRCALPHA) - dblend = DSBF_ONE; - else if (dblend == DSBF_INVSRCALPHA) - dblend = DSBF_ZERO; - } - - if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) { - if (sblend == DSBF_DESTALPHA) - sblend = DSBF_ONE; - else if (sblend == DSBF_INVDESTALPHA) - sblend = DSBF_ZERO; - - if (dblend == DSBF_DESTALPHA) - dblend = DSBF_ONE; - else if (dblend == DSBF_INVDESTALPHA) - dblend = DSBF_ZERO; - } - - flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) - ? DSBLIT_NOFX : DSBLIT_BLEND_ALPHACHANNEL; - if (! CAIRO_COLOR_IS_OPAQUE (color)) - flags |= DSBLIT_BLEND_COLORALPHA; - if (! _cairo_color_equal (color, _cairo_stock_color (CAIRO_STOCK_WHITE))) - flags |= DSBLIT_COLORIZE; - - dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags); - - if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA)) { - dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); - dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); - } - - if (flags & (DSBLIT_BLEND_COLORALPHA | DSBLIT_COLORIZE)) { - if (dst->blit_premultiplied) { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red_short >> 8, - color->green_short >> 8, - color->blue_short >> 8, - color->alpha_short >> 8); - } else { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red * 0xff, - color->green * 0xff, - color->blue * 0xff, - color->alpha * 0xff); - } - } - - *ret_src = src; - *ret_src_attr = src_attr; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_directfb_finish_composite (cairo_directfb_surface_t *dst, - const cairo_pattern_t *src_pattern, - cairo_surface_t *src, - cairo_surface_attributes_t *src_attr) -{ - _cairo_pattern_release_surface (src_pattern, src, src_attr); -} -#endif /* DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS */ - -#if DFB_COMPOSITE -static DFBAccelerationMask -_directfb_categorize_operation (cairo_surface_attributes_t *src_attr) -{ - cairo_matrix_t *m = &src_attr->matrix; - - if (m->xy != 0 || m->yx != 0 || m->xx < 0 || m->yy < 0) { - if (src_attr->extend != CAIRO_EXTEND_NONE) - return DFXL_NONE; - - return DFXL_TEXTRIANGLES; - } - - if (m->xx != 1 || m->yy != 1) { - if (src_attr->extend != CAIRO_EXTEND_NONE) - return DFXL_NONE; - - return DFXL_STRETCHBLIT; - } - - switch (src_attr->extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_REPEAT: - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, - NULL, NULL)) - { - return DFXL_BLIT; - } - else - { - return DFXL_STRETCHBLIT; - } - - default: - case CAIRO_EXTEND_REFLECT: - case CAIRO_EXTEND_PAD: - return DFXL_NONE; - } +#define RUN_CLIPPED(surface, clip_region, clip, func) {\ + if ((clip_region) != NULL) {\ + int n_clips = cairo_region_num_rectangles (clip_region), n; \ + for (n = 0; n < n_clips; n++) {\ + if (clip) {\ + DFBRegion reg, *cli = (clip); \ + cairo_rectangle_int_t rect; \ + cairo_region_get_rectangle (clip_region, n, &rect); \ + reg.x1 = rect.x; \ + reg.y1 = rect.y; \ + reg.x2 = rect.x + rect.width - 1; \ + reg.y2 = rect.y + rect.height - 1; \ + if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\ + reg.x1 > cli->x2 || reg.y1 > cli->y2)\ + continue;\ + if (reg.x1 < cli->x1)\ + reg.x1 = cli->x1;\ + if (reg.y1 < cli->y1)\ + reg.y1 = cli->y1;\ + if (reg.x2 > cli->x2)\ + reg.x2 = cli->x2;\ + if (reg.y2 > cli->y2)\ + reg.y2 = cli->y2;\ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®);\ + } else {\ + DFBRegion reg; \ + cairo_rectangle_int_t rect; \ + cairo_region_get_rectangle (clip_region, n, &rect); \ + reg.x1 = rect.x; \ + reg.y1 = rect.y; \ + reg.x2 = rect.x + rect.width - 1; \ + reg.y2 = rect.y + rect.height - 1; \ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, ®); \ + }\ + func;\ + }\ + } else {\ + (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\ + func;\ + }\ } static cairo_int_status_t -_cairo_directfb_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, int src_y, - int mask_x, int mask_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_directfb_surface_t *dst = abstract_dst; - cairo_directfb_surface_t *src; - cairo_surface_attributes_t src_attr; - cairo_bool_t is_integer_translation; - DFBAccelerationMask accel, mask; - cairo_int_status_t status; - int tx, ty; - - D_DEBUG_AT (CairoDFB_Render, - "%s( op=%d, src_pattern=%p, mask_pattern=%p, dst=%p," - " src_x=%d, src_y=%d, mask_x=%d, mask_y=%d, dst_x=%d," - " dst_y=%d, width=%u, height=%u ).\n", - __FUNCTION__, op, src_pattern, mask_pattern, dst, - src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _directfb_prepare_composite (dst, src_pattern, mask_pattern, op, - &src_x, &src_y, &mask_x, &mask_y, - width, height, &src, &src_attr); - if (status) - return status; - - accel = _directfb_categorize_operation (&src_attr); - if (accel == DFXL_NONE) { - _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - dst->dfbsurface->GetAccelerationMask (dst->dfbsurface, - src->dfbsurface, - &mask); - if ((mask & accel) == 0) { - D_DEBUG_AT (CairoDFB_Render, "No acceleration (%08x)!\n", accel); - if (accel != DFXL_BLIT) { - _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - src_x += src_attr.x_offset; - src_y += src_attr.y_offset; - - switch ((int) accel) { - case DFXL_BLIT: - { - DFBRectangle sr; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, - &tx, &ty); - assert (is_integer_translation); - - sr.x = src_x + tx; - sr.y = src_y + ty; - sr.w = width; - sr.h = height; - - if (src_attr.extend == CAIRO_EXTEND_NONE) { - D_DEBUG_AT (CairoDFB_Render, "Running Blit().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->Blit (dst->dfbsurface, - src->dfbsurface, - &sr, dst_x, dst_y)); - } else if (src_attr.extend == CAIRO_EXTEND_REPEAT) { - DFBRegion clip; - - clip.x1 = dst_x; - clip.y1 = dst_y; - clip.x2 = dst_x + width - 1; - clip.y2 = dst_y + height - 1; - - D_DEBUG_AT (CairoDFB_Render, "Running TileBlit().\n"); - - RUN_CLIPPED (dst, clip_region, &clip, - dst->dfbsurface->TileBlit (dst->dfbsurface, - src->dfbsurface, - &sr, dst_x, dst_y)); - } - break; - } - - case DFXL_STRETCHBLIT: - { - DFBRectangle sr, dr; - double x1, y1, x2, y2; - - TRANSFORM_POINT2X (src_attr.matrix, - src_x, src_y, x1, y1); - TRANSFORM_POINT2X (src_attr.matrix, - src_x+width, src_y+height, x2, y2); - - sr.x = floor (x1); - sr.y = floor (y1); - sr.w = ceil (x2) - sr.x; - sr.h = ceil (y2) - sr.y; - - dr.x = dst_x; - dr.y = dst_y; - dr.w = width; - dr.h = height; - - D_DEBUG_AT (CairoDFB_Render, "Running StretchBlit().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->StretchBlit (dst->dfbsurface, - src->dfbsurface, - &sr, &dr)); - break; - } - - case DFXL_TEXTRIANGLES: - { - DFBRegion clip; - DFBVertex v[4]; - float x1, y1, x2, y2; - int w, h; - - status = cairo_matrix_invert (&src_attr.matrix); - /* guaranteed by cairo_pattern_set_matrix (); */ - assert (status == CAIRO_STATUS_SUCCESS); - - x1 = src_x; - y1 = src_y; - x2 = width + x1; - y2 = height + y1; - - src->dfbsurface->GetSize (src->dfbsurface, &w, &h); - - TRANSFORM_POINT3X (src_attr.matrix, x1, y1, v[0].x, v[0].y); - v[0].z = 0; - v[0].w = 1; - v[0].s = x1 / w; - v[0].t = y1 / h; - - TRANSFORM_POINT3X (src_attr.matrix, x2, y1, v[1].x, v[1].y); - v[1].z = 0; - v[1].w = 1; - v[1].s = x2 / w; - v[1].t = y1 / h; - - TRANSFORM_POINT3X (src_attr.matrix, x2, y2, v[2].x, v[2].y); - v[2].z = 0; - v[2].w = 1; - v[2].s = x2 / w; - v[2].t = y2 / h; - - TRANSFORM_POINT3X (src_attr.matrix, x1, y2, v[3].x, v[3].y); - v[3].z = 0; - v[3].w = 1; - v[3].s = x1 / w; - v[3].t = y2 / h; - - clip.x1 = dst_x; - clip.y1 = dst_y; - clip.x2 = dst_x + width - 1; - clip.y2 = dst_y + height - 1; - - D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); - - RUN_CLIPPED (dst, clip_region, &clip, - dst->dfbsurface->TextureTriangles (dst->dfbsurface, - src->dfbsurface, - v, NULL, - 4, DTTF_FAN)); - break; - } - - default: - D_BUG ("Unexpected operation"); - break; - } - - _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr); - - return CAIRO_STATUS_SUCCESS; -} -#endif /* DFB_COMPOSITE */ - -#if DFB_RECTANGLES -static cairo_int_status_t -_cairo_directfb_surface_fill_rectangles (void *abstract_surface, +_cairo_dfb_surface_fill_rectangles (void *abstract_surface, cairo_operator_t op, const cairo_color_t *color, cairo_rectangle_int_t *rects, int n_rects) { - cairo_directfb_surface_t *dst = abstract_surface; + cairo_dfb_surface_t *dst = abstract_surface; DFBSurfaceDrawingFlags flags; DFBSurfaceBlendFunction sblend; DFBSurfaceBlendFunction dblend; @@ -1239,726 +462,84 @@ _cairo_directfb_surface_fill_rectangles (void *abstract_surface } #endif -#if DFB_COMPOSITE_TRAPEZOIDS -static cairo_int_status_t -_cairo_directfb_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_directfb_surface_t *dst = abstract_dst; - cairo_directfb_surface_t *src; - cairo_surface_attributes_t src_attr; - cairo_status_t status; - DFBAccelerationMask accel; - - D_DEBUG_AT (CairoDFB_Render, - "%s( op=%d, pattern=%p, dst=%p, antialias=%d," - " src_x=%d, src_y=%d, dst_x=%d, dst_y=%d," - " width=%u, height=%u, traps=%p, num_traps=%d ).\n", - __FUNCTION__, op, pattern, dst, antialias, - src_x, src_y, dst_x, dst_y, width, height, traps, num_traps); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (antialias != CAIRO_ANTIALIAS_NONE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Textures are not supported yet. */ - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _directfb_prepare_composite (dst, pattern, NULL, op, - &src_x, &src_y, NULL, NULL, - width, height, &src, &src_attr); - if (status) - return status; - - dst->dfbsurface->GetAccelerationMask (dst->dfbsurface, - src->dfbsurface, - &accel); - - status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (accel & DFXL_TEXTRIANGLES) { - DFBVertex vertex[6*num_traps]; - DFBVertex *v = &vertex[0]; - int n = 0; - -#define ADD_TRI_V(V, X, Y) do { \ - (V)->x = (X); (V)->y = (Y); (V)->w = 1; (V)->z = (V)->s = (V)->t = 0; \ -} while (0) -#define ADD_TRI(id, x1, y1, x2, y2, x3, y3) do {\ - const int p = (id)*3;\ - ADD_TRI_V (v+p+0, x1, y1); \ - ADD_TRI_V (v+p+1, x2, y2); \ - ADD_TRI_V (v+p+2, x3, y3); \ -} while (0) - while (num_traps--) { - double lx1, ly1, lx2, ly2; - double rx1, ry1, rx2, ry2; - - lx1 = _cairo_fixed_to_double (traps->left.p1.x); - ly1 = _cairo_fixed_to_double (traps->left.p1.y); - lx2 = _cairo_fixed_to_double (traps->left.p2.x); - ly2 = _cairo_fixed_to_double (traps->left.p2.y); - rx1 = _cairo_fixed_to_double (traps->right.p1.x); - ry1 = _cairo_fixed_to_double (traps->right.p1.y); - rx2 = _cairo_fixed_to_double (traps->right.p2.x); - ry2 = _cairo_fixed_to_double (traps->right.p2.y); - - if (traps->left.p1.y < traps->top) { - double y = _cairo_fixed_to_double (traps->top); - if (lx2 != lx1) - lx1 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1; - ly1 = y; - } - if (traps->left.p2.y > traps->bottom) { - double y = _cairo_fixed_to_double (traps->bottom); - if (lx2 != lx1) - lx2 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1; - ly2 = y; - } - - if (traps->right.p1.y < traps->top) { - double y = _cairo_fixed_to_double (traps->top); - if (rx2 != rx1) - rx1 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1; - ry1 = y; - } - if (traps->right.p2.y > traps->bottom) { - double y = _cairo_fixed_to_double (traps->bottom); - if (rx2 != rx1) - rx2 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1; - ry2 = y; - } - - if (lx1 == rx1 && ly1 == ry1) { - ADD_TRI (0, lx2, ly2, lx1, ly1, rx2, ry2); - v += 3; - n += 3; - } else if (lx2 == rx2 && ly2 == ry2) { - ADD_TRI (0, lx1, ly1, lx2, ly2, rx1, ry1); - v += 3; - n += 3; - } else { - ADD_TRI (0, lx1, ly1, rx1, ry1, lx2, ly2); - ADD_TRI (1, lx2, ly2, rx1, ry1, rx2, ry2); - v += 6; - n += 6; - } - - traps++; - } -#undef ADD_TRI -#undef ADD_TRI_V - - D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->TextureTriangles (dst->dfbsurface, - src->dfbsurface, - vertex, NULL, n, - DTTF_LIST)); - - status = CAIRO_STATUS_SUCCESS; - } - - _directfb_finish_composite (dst, pattern, &src->base, &src_attr); - - return status; -} -#endif /* DFB_COMPOSITE_TRAPEZOIDS */ - -static cairo_bool_t -_cairo_directfb_abstract_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_directfb_surface_t *surface = abstract_surface; - - D_DEBUG_AT (CairoDFB_Surface, - "%s( surface=%p, rectangle=%p ).\n", - __FUNCTION__, surface, rectangle); - - if (!surface->local) { - surface->dfbsurface->GetSize (surface->dfbsurface, - &surface->width, &surface->height); - } - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -#if DFB_SHOW_GLYPHS -static cairo_status_t -_directfb_allocate_font_cache (IDirectFB *dfb, - int width, int height, - cairo_directfb_font_cache_t **out) -{ - cairo_directfb_font_cache_t *cache; - cairo_status_t status; - - cache = calloc (1, sizeof (cairo_directfb_font_cache_t)); - if (cache == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - cache->dfb = dfb; - status = _directfb_buffer_surface_create (dfb, - _directfb_argb_font ? DSPF_ARGB : DSPF_A8, - width, height, - &cache->dfbsurface); - if (status) { - free (cache); - return status; - } - - cache->width = width; - cache->height = height; - *out = cache; - return CAIRO_STATUS_SUCCESS; -} - -static void -_directfb_destroy_font_cache (cairo_directfb_font_cache_t *cache) -{ - cache->dfbsurface->Release (cache->dfbsurface); - free (cache); -} - -/* XXX hook into rtree font cache from drm */ -static cairo_status_t -_directfb_acquire_font_cache (cairo_directfb_surface_t *surface, - cairo_scaled_font_t *scaled_font, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_directfb_font_cache_t **ret_cache, - DFBRectangle *rects, - DFBPoint *points, - int *ret_num) -{ - cairo_status_t status; - cairo_scaled_glyph_t *chars[num_glyphs]; - int num_chars = 0; - cairo_directfb_font_cache_t *cache = NULL; - int n = 0; - int x = 0; - int y = 0; - int w = 8; - int h = 8; - int i; - - D_DEBUG_AT (CairoDFB_Font, "%s( %p [%d] )\n", __FUNCTION__, glyphs, num_glyphs ); - - _cairo_scaled_font_freeze_cache (scaled_font); - - if (scaled_font->surface_private) { - cache = scaled_font->surface_private; - x = cache->x; - y = cache->y; - } - - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - cairo_image_surface_t *img; - - D_DEBUG_AT (CairoDFB_Font, " -> [%2d] = %4lu\n", i, glyphs[i].index ); - - status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (status) { - _cairo_scaled_font_thaw_cache (scaled_font); - return status; - } - - img = scaled_glyph->surface; - switch (img->format) { - case CAIRO_FORMAT_A1: - case CAIRO_FORMAT_A8: - case CAIRO_FORMAT_ARGB32: - break; - case CAIRO_FORMAT_RGB24: - default: - D_DEBUG_AT (CairoDFB_Font, - " -> Unsupported font format %d!\n", img->format); - _cairo_scaled_font_thaw_cache (scaled_font); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - points[n].x = _cairo_lround (glyphs[i].x - img->base.device_transform.x0); - points[n].y = _cairo_lround (glyphs[i].y - img->base.device_transform.y0); - - // D_DEBUG_AT (CairoDFB_Font, " (%4d,%4d) [%2d]\n", points[n].x, points[n].y, n ); - - if (points[n].x >= surface->width || - points[n].y >= surface->height || - points[n].x+img->width <= 0 || - points[n].y+img->height <= 0) - { - continue; - } - - if (scaled_glyph->surface_private == NULL) { - DFBRectangle *rect; - - if (x+img->width > 2048) { - x = 0; - y = h; - h = 0; - } - - rects[n].x = x; - rects[n].y = y; - rects[n].w = img->width; - rects[n].h = img->height; - - x += img->width; - h = MAX (h, img->height); - w = MAX (w, x); - - /* Remember glyph location */ - rect = malloc (sizeof (DFBRectangle)); - if (rect == NULL) { - _cairo_scaled_font_thaw_cache (scaled_font); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - *rect = rects[n]; - - scaled_glyph->surface_private = rect; - chars[num_chars++] = scaled_glyph; - - D_DEBUG_AT (CairoDFB_Font, " -> loading at %4d,%2d <- rect %p, img %p, entry %p\n", - rects[n].x, rects[n].y, rect, scaled_glyph->surface, scaled_glyph); - } else { - rects[n] = *(DFBRectangle *) scaled_glyph->surface_private; - - D_DEBUG_AT (CairoDFB_Font, " -> exists at %4d,%2d\n", rects[n].x, rects[n].y); - } - - n++; - } - - if (n == 0) { - _cairo_scaled_font_thaw_cache (scaled_font); - return CAIRO_INT_STATUS_NOTHING_TO_DO; - } - - h += y; - w = MAX (w, 8); - h = MAX (h, 8); - - /* XXX query maximum surface size */ - if (w > 2048 || h > 2048) { - _cairo_scaled_font_thaw_cache (scaled_font); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (cache) { - if (cache->width < w || cache->height < h) { - cairo_directfb_font_cache_t *new_cache; - - w = MAX (w, cache->width); - h = MAX (h, cache->height); - - D_DEBUG_AT (CairoDFB_Font, " -> Reallocating font cache (%dx%d).\n", w, h); - - status = _directfb_allocate_font_cache (surface->dfb, - w, h, - &new_cache); - if (status) { - _cairo_scaled_font_thaw_cache (scaled_font); - return status; - } - - new_cache->dfbsurface->Blit (new_cache->dfbsurface, - cache->dfbsurface, NULL, 0, 0); - - _directfb_destroy_font_cache (cache); - scaled_font->surface_private = cache = new_cache; - } - } else { - D_DEBUG_AT (CairoDFB_Font, " -> Allocating font cache (%dx%d).\n", w, h); - - status = _directfb_allocate_font_cache (surface->dfb, w, h, &cache); - if (status) { - _cairo_scaled_font_thaw_cache (scaled_font); - return status; - } - - scaled_font->surface_backend = &_cairo_directfb_surface_backend; - scaled_font->surface_private = cache; - } - - if (num_chars) { - unsigned char *data; - int pitch; - - if (cache->dfbsurface->Lock (cache->dfbsurface, - DSLF_WRITE, (void *)&data, &pitch)) - { - _cairo_scaled_font_thaw_cache (scaled_font); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - D_DEBUG_AT (CairoDFB_Font, " => %d chars to load, cache %dx%d\n", num_chars, cache->width, cache->height); - - for (i = 0; i < num_chars; i++) { - cairo_image_surface_t *img = chars[i]->surface; - DFBRectangle *rect = chars[i]->surface_private; - unsigned char *dst = data; - unsigned char *src; - int j; - - D_DEBUG_AT (CairoDFB_Font, " -> loading [%2d] <- rect %p, img %p, entry %p\n", i, rect, img, chars[i]); - - src = img->data; - - D_DEBUG_AT (CairoDFB_Font, " from %p\n", src); - - dst += rect->y * pitch + (_directfb_argb_font ? (rect->x<<2) : rect->x); - - D_DEBUG_AT (CairoDFB_Font, " to %4d,%2d (%p)\n", rect->x, rect->y, dst); - - if (img->format == CAIRO_FORMAT_A1) { - for (h = rect->h; h; h--) { - if (_directfb_argb_font) { - for (j = 0; j < rect->w; j++) - ((uint32_t *) dst)[j] = (src[j>>3] & (1 << (j&7))) ? 0xffffffff : 0; - } else { - for (j = 0; j < rect->w; j++) - dst[j] = (src[j>>3] & (1 << (j&7))) ? 0xff : 0; - } - - dst += pitch; - src += img->stride; - } - } else if (img->format == CAIRO_FORMAT_A8) { - for (h = rect->h; h; h--) { - if (_directfb_argb_font) { - for (j = 0; j < rect->w; j++) - ((uint32_t *) dst)[j] = src[j] * 0x01010101; - } else { - direct_memcpy (dst, src, rect->w); - } - - dst += pitch; - src += img->stride; - } - } else { /* ARGB32 */ - for (h = rect->h; h; h--) { - if (_directfb_argb_font) { - direct_memcpy (dst, src, rect->w<<2); - } else { - for (j = 0; j < rect->w; j++) - dst[j] = ((uint32_t *) src)[j] >> 24; - } - - dst += pitch; - src += img->stride; - } - } - } - - cache->dfbsurface->Unlock (cache->dfbsurface); - } - - _cairo_scaled_font_thaw_cache (scaled_font); - - cache->x = x; - cache->y = y; - - D_DEBUG_AT (CairoDFB_Font, " => cache %d,%d, %p [%d]\n", x, y, cache, n); - - *ret_cache = cache; - *ret_num = n; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_directfb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_directfb_font_cache_t *cache = scaled_font->surface_private; - - D_DEBUG_AT (CairoDFB_Font, - "%s( scaled_font=%p ).\n", __FUNCTION__, scaled_font); - - if (cache != NULL) { - _directfb_destroy_font_cache (cache); - scaled_font->surface_private = NULL; - } -} - -static void -_cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - D_DEBUG_AT (CairoDFB_Font, - "%s( scaled_glyph=%p, scaled_font=%p ).\n", - __FUNCTION__, scaled_glyph, scaled_font); - - if (scaled_glyph->surface_private != NULL) { - free (scaled_glyph->surface_private); - scaled_glyph->surface_private = NULL; - } -} - -static cairo_int_status_t -_cairo_directfb_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_directfb_surface_t *dst = abstract_dst; - cairo_directfb_font_cache_t *cache; - cairo_status_t status; - DFBSurfaceBlittingFlags flags; - DFBSurfaceBlendFunction sblend; - DFBSurfaceBlendFunction dblend; - DFBRectangle rects[num_glyphs]; - DFBPoint points[num_glyphs]; - int num; - const cairo_color_t *color; - cairo_region_t *clip_region = NULL; - - D_DEBUG_AT (CairoDFB_Font, - "%s( dst=%p, op=%d, pattern=%p, glyphs=%p, num_glyphs=%d, scaled_font=%p ).\n", - __FUNCTION__, dst, op, pattern, glyphs, num_glyphs, scaled_font); - - if (! dst->supported_destination) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Fallback if we need to emulate clip regions */ - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - } - - /* XXX Unbounded operators are not handled correctly */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _directfb_get_operator (op, &sblend, &dblend) || - sblend == DSBF_DESTALPHA || sblend == DSBF_INVDESTALPHA) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _directfb_acquire_font_cache (dst, scaled_font, glyphs, num_glyphs, - &cache, &rects[0], &points[0], &num); - if (status) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - status = CAIRO_STATUS_SUCCESS; - return status; - } - - color = &((cairo_solid_pattern_t *) pattern)->color; - - flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE; - if (! CAIRO_COLOR_IS_OPAQUE (color)) - flags |= DSBLIT_BLEND_COLORALPHA; - - if (!_directfb_argb_font) { - if (sblend == DSBF_ONE) { - sblend = DSBF_SRCALPHA; - if (dblend == DSBF_ZERO) - dblend = DSBF_INVSRCALPHA; - } - } - - dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags); - dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); - dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); - if (dst->blit_premultiplied) { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red_short >> 8, - color->green_short >> 8, - color->blue_short >> 8, - color->alpha_short >> 8); - } else { - dst->dfbsurface->SetColor (dst->dfbsurface, - color->red * 0xff, - color->green * 0xff, - color->blue * 0xff, - color->alpha * 0xff); - } - - D_DEBUG_AT (CairoDFB_Font, "Running BatchBlit().\n"); - - RUN_CLIPPED (dst, clip_region, NULL, - dst->dfbsurface->BatchBlit (dst->dfbsurface, - cache->dfbsurface, rects, points, num)); - - return CAIRO_STATUS_SUCCESS; -} -#endif /* DFB_SHOW_GLYPHS */ - - -static cairo_bool_t -_cairo_directfb_surface_is_similar (void *surface_a, void *surface_b) -{ - cairo_directfb_surface_t *a = (cairo_directfb_surface_t *) surface_a; - cairo_directfb_surface_t *b = (cairo_directfb_surface_t *) surface_b; - - return a->dfb == b->dfb; -} - static cairo_surface_backend_t -_cairo_directfb_surface_backend = { - CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/ - _cairo_directfb_surface_create_similar,/*create_similar*/ - _cairo_directfb_surface_finish, /*finish*/ - _cairo_directfb_surface_acquire_source_image,/*acquire_source_image*/ - _cairo_directfb_surface_release_source_image,/*release_source_image*/ - _cairo_directfb_surface_acquire_dest_image,/*acquire_dest_image*/ - _cairo_directfb_surface_release_dest_image,/*release_dest_image*/ - _cairo_directfb_surface_clone_similar,/*clone_similar*/ -#if DFB_COMPOSITE - _cairo_directfb_surface_composite,/*composite*/ -#else - NULL,/*composite*/ -#endif -#if DFB_RECTANGLES - _cairo_directfb_surface_fill_rectangles,/*fill_rectangles*/ -#else - NULL,/*fill_rectangles*/ -#endif -#if DFB_COMPOSITE_TRAPEZOIDS - _cairo_directfb_surface_composite_trapezoids,/*composite_trapezoids*/ -#else - NULL,/*composite_trapezoids*/ -#endif - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_directfb_abstract_surface_get_extents,/* get_extents */ - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ -#if DFB_SHOW_GLYPHS - _cairo_directfb_surface_scaled_font_fini,/* scaled_font_fini */ - _cairo_directfb_surface_scaled_glyph_fini,/* scaled_glyph_fini */ -#else - NULL, - NULL, -#endif - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ -#if DFB_SHOW_GLYPHS - _cairo_directfb_surface_show_glyphs,/* show_glyphs */ -#else - NULL, /* show_glyphs */ -#endif - NULL, /* snapshot */ - _cairo_directfb_surface_is_similar, +_cairo_dfb_surface_backend = { + CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/ + _cairo_dfb_surface_finish, /*finish*/ + _cairo_default_context_create, + + _cairo_dfb_surface_create_similar,/*create_similar*/ + NULL, /* create similar image */ + _cairo_dfb_surface_map_to_image, + _cairo_dfb_surface_unmap_image, + + _cairo_surface_default_source, + _cairo_surface_default_acquire_source_image, + _cairo_surface_default_release_source_image, + NULL, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_image_surface_get_extents, + _cairo_image_surface_get_font_options, + + _cairo_dfb_surface_flush, + NULL, /* mark_dirty_rectangle */ + + _cairo_surface_fallback_paint, + _cairo_surface_fallback_mask, + _cairo_surface_fallback_stroke, + _cairo_surface_fallback_fill, + NULL, /* fill-stroke */ + _cairo_surface_fallback_glyphs, }; - -static void -cairo_directfb_surface_backend_init (IDirectFB *dfb) -{ - static int done = 0; - - if (done) - return; - - if (getenv ("CAIRO_DIRECTFB_NO_ACCEL")) { -#if DFB_RECTANGLES - _cairo_directfb_surface_backend.fill_rectangles = NULL; -#endif -#if DFB_COMPOSITE - _cairo_directfb_surface_backend.composite = NULL; -#endif -#if DFB_COMPOSITE_TRAPEZOIDS - _cairo_directfb_surface_backend.composite_trapezoids = NULL; -#endif -#if DFB_SHOW_GLYPHS - _cairo_directfb_surface_backend.scaled_font_fini = NULL; - _cairo_directfb_surface_backend.scaled_glyph_fini = NULL; - _cairo_directfb_surface_backend.show_glyphs = NULL; -#endif - D_DEBUG_AT (CairoDFB_Surface, "Acceleration disabled.\n"); - } else { - DFBGraphicsDeviceDescription dsc; - - dfb->GetDeviceDescription (dfb, &dsc); - -#if DFB_COMPOSITE - // if (!(dsc.acceleration_mask & DFXL_BLIT)) - // _cairo_directfb_surface_backend.composite = NULL; -#endif - -#if DFB_COMPOSITE_TRAPEZOIDS - // if (!(dsc.acceleration_mask & DFXL_TEXTRIANGLES)) - // _cairo_directfb_surface_backend.composite_trapezoids = NULL; -#endif - } - - if (getenv ("CAIRO_DIRECTFB_ARGB_FONT")) { - _directfb_argb_font = 1; - D_DEBUG_AT (CairoDFB_Surface, "Using ARGB fonts.\n"); - } - - done = 1; -} - cairo_surface_t * cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) { - cairo_directfb_surface_t *surface; + cairo_dfb_surface_t *surface; DFBSurfacePixelFormat format; DFBSurfaceCapabilities caps; + pixman_format_code_t pixman_format; + int width, height; D_ASSERT (dfb != NULL); D_ASSERT (dfbsurface != NULL); - cairo_directfb_surface_backend_init (dfb); + dfbsurface->GetPixelFormat (dfbsurface, &format); + dfbsurface->GetSize (dfbsurface, &width, &height); - surface = calloc (1, sizeof (cairo_directfb_surface_t)); + pixman_format = _directfb_to_pixman_format (format); + if (! pixman_format_supported_destination (pixman_format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + surface = calloc (1, sizeof (cairo_dfb_surface_t)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - dfbsurface->AddRef (dfbsurface); - dfbsurface->GetPixelFormat (dfbsurface, &format); - dfbsurface->GetSize (dfbsurface, &surface->width, &surface->height); + /* XXX dfb -> device */ + _cairo_surface_init (&surface->image.base, + &_cairo_dfb_surface_backend, + NULL, /* device */ + _directfb_format_to_content (format), + FALSE); /* is_vector */ + + surface->image.pixman_format = pixman_format; + surface->image.format = _cairo_format_from_pixman_format (pixman_format); + + surface->image.width = width; + surface->image.height = height; + surface->image.depth = PIXMAN_FORMAT_DEPTH(pixman_format); + surface->dfb = dfb; - surface->dfbsurface = dfbsurface; - surface->pixman_format = _directfb_to_pixman_format (format); - surface->supported_destination = pixman_format_supported_destination (surface->pixman_format); + surface->dfb_surface = dfbsurface; + dfbsurface->AddRef (dfbsurface); dfbsurface->GetCapabilities (dfbsurface, &caps); if (caps & DSCAPS_PREMULTIPLIED) surface->blit_premultiplied = TRUE; - _cairo_surface_init (&surface->base, - &_cairo_directfb_surface_backend, - NULL, /* device */ - _directfb_format_to_content (format)); - - return &surface->base; + return &surface->image.base; } +slim_hidden_def(cairo_directfb_surface_create); diff --git a/gfx/cairo/cairo/src/cairo-eagle-context.c b/gfx/cairo/cairo/src/cairo-eagle-context.c deleted file mode 100644 index 23766a944d1c..000000000000 --- a/gfx/cairo/cairo/src/cairo-eagle-context.c +++ /dev/null @@ -1,181 +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Carl Worth - * Chris Wilson - */ - -#include "cairoint.h" - -#include "cairo-gl-private.h" - -#include /* XXX dummy surface for glewInit() */ -#include - -typedef struct _cairo_eagle_context { - cairo_gl_context_t base; - - EGLDisplay display; - EGLContext context; -} cairo_eagle_context_t; - -typedef struct _cairo_eagle_surface { - cairo_gl_surface_t base; - - EGLSurface eagle; -} cairo_eagle_surface_t; - -static void -_eagle_make_current (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_eagle_context_t *ctx = abstract_ctx; - cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; - - eagleMakeCurrent (ctx->display, surface->eagle, surface->eagle, ctx->context); -} - -static void -_eagle_swap_buffers (void *abstract_ctx, - cairo_gl_surface_t *abstract_surface) -{ - cairo_eagle_context_t *ctx = abstract_ctx; - cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface; - - eagleSwapBuffers (ctx->display, surface->eagle); -} - -static void -_eagle_destroy (void *abstract_ctx) -{ -} - -static cairo_bool_t -_eagle_init (EGLDisplay display, EGLContext context) -{ - const EGLint config_attribs[] = { - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_NONE - }; - const EGLint surface_attribs[] = { - EGL_RENDER_BUFFER, EGL_BACK_BUFFER, - EGL_NONE - }; - EGLConfig config; - EGLSurface dummy; - struct drm_i915_gem_create create; - struct drm_gem_flink flink; - int fd; - GLenum err; - - if (! eagleChooseConfig (display, config_attribs, &config, 1, NULL)) { - fprintf (stderr, "Unable to choose config\n"); - return FALSE; - } - - /* XXX */ - fd = eagleGetDisplayFD (display); - - create.size = 4096; - if (ioctl (fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) { - fprintf (stderr, "gem create failed: %m\n"); - return FALSE; - } - flink.handle = create.handle; - if (ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) { - fprintf (stderr, "gem flink failed: %m\n"); - return FALSE; - } - - dummy = eagleCreateSurfaceForName (display, config, flink.name, - 1, 1, 4, surface_attribs); - if (dummy == NULL) { - fprintf (stderr, "Failed to create dummy surface\n"); - return FALSE; - } - - eagleMakeCurrent (display, dummy, dummy, context); -} - -cairo_gl_context_t * -cairo_eagle_context_create (EGLDisplay display, EGLContext context) -{ - cairo_eagle_context_t *ctx; - cairo_status_t status; - - if (! _eagle_init (display, context)) { - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - } - - ctx = calloc (1, sizeof (cairo_eagle_context_t)); - if (ctx == NULL) - return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); - - ctx->display = display; - ctx->context = context; - - ctx->base.make_current = _eagle_make_current; - ctx->base.swap_buffers = _eagle_swap_buffers; - ctx->base.destroy = _eagle_destroy; - - status = _cairo_gl_context_init (&ctx->base); - if (status) { - free (ctx); - return _cairo_gl_context_create_in_error (status); - } - - return &ctx->base; -} - -cairo_surface_t * -cairo_gl_surface_create_for_eagle (cairo_gl_context_t *ctx, - EGLSurface eagle, - int width, - int height) -{ - cairo_eagle_surface_t *surface; - - if (ctx->status) - return _cairo_surface_create_in_error (ctx->status); - - surface = calloc (1, sizeof (cairo_eagle_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_gl_surface_init (ctx, &surface->base, - CAIRO_CONTENT_COLOR_ALPHA, width, height); - surface->eagle = eagle; - - return &surface->base.base; -} diff --git a/gfx/cairo/cairo/src/cairo-egl-context.c b/gfx/cairo/cairo/src/cairo-egl-context.c new file mode 100644 index 000000000000..bf704c630fb6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-egl-context.c @@ -0,0 +1,317 @@ +/* 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 + * Chris Wilson + */ + +#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/gfx/cairo/cairo/src/cairo-glitz.h b/gfx/cairo/cairo/src/cairo-error-inline.h similarity index 76% rename from gfx/cairo/cairo/src/cairo-glitz.h rename to gfx/cairo/cairo/src/cairo-error-inline.h index 08519dcbd7be..9126c5e61eb9 100644 --- a/gfx/cairo/cairo/src/cairo-glitz.h +++ b/gfx/cairo/cairo/src/cairo-error-inline.h @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -12,7 +13,7 @@ * * 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 + * 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 * @@ -34,24 +35,18 @@ * Carl D. Worth */ -#ifndef CAIRO_GLITZ_H -#define CAIRO_GLITZ_H +#ifndef _CAIRO_ERROR_INLINE_H_ +#define _CAIRO_ERROR_INLINE_H_ -#include "cairo.h" - -#if CAIRO_HAS_GLITZ_SURFACE - -#include +#include "cairo-error-private.h" CAIRO_BEGIN_DECLS -cairo_public cairo_surface_t * -cairo_glitz_surface_create (glitz_surface_t *surface); +static inline cairo_status_t +_cairo_public_status (cairo_int_status_t status) +{ + assert (status <= CAIRO_INT_STATUS_LAST_STATUS); + return (cairo_status_t) status; +} -CAIRO_END_DECLS - -#else /* CAIRO_HAS_GLITZ_SURFACE */ -# error Cairo was not compiled with support for the glitz backend -#endif /* CAIRO_HAS_GLITZ_SURFACE */ - -#endif /* CAIRO_GLITZ_H */ +#endif /* _CAIRO_ERROR_INLINE_H_ */ diff --git a/gfx/cairo/cairo/src/cairo-error-private.h b/gfx/cairo/cairo/src/cairo-error-private.h index fc0c56438846..1ab57ddf846b 100644 --- a/gfx/cairo/cairo/src/cairo-error-private.h +++ b/gfx/cairo/cairo/src/cairo-error-private.h @@ -40,11 +40,82 @@ #include "cairo.h" #include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +#include CAIRO_BEGIN_DECLS +/* _cairo_int_status: internal status + * + * Sure wish C had a real enum type so that this would be distinct + * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 + * offset. We want to keep it fit in int8_t as the compiler may choose + * that for #cairo_status_t + */ +enum _cairo_int_status { + CAIRO_INT_STATUS_SUCCESS = 0, + + CAIRO_INT_STATUS_NO_MEMORY, + CAIRO_INT_STATUS_INVALID_RESTORE, + CAIRO_INT_STATUS_INVALID_POP_GROUP, + CAIRO_INT_STATUS_NO_CURRENT_POINT, + CAIRO_INT_STATUS_INVALID_MATRIX, + CAIRO_INT_STATUS_INVALID_STATUS, + CAIRO_INT_STATUS_NULL_POINTER, + CAIRO_INT_STATUS_INVALID_STRING, + CAIRO_INT_STATUS_INVALID_PATH_DATA, + CAIRO_INT_STATUS_READ_ERROR, + CAIRO_INT_STATUS_WRITE_ERROR, + CAIRO_INT_STATUS_SURFACE_FINISHED, + CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH, + CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH, + CAIRO_INT_STATUS_INVALID_CONTENT, + CAIRO_INT_STATUS_INVALID_FORMAT, + CAIRO_INT_STATUS_INVALID_VISUAL, + CAIRO_INT_STATUS_FILE_NOT_FOUND, + CAIRO_INT_STATUS_INVALID_DASH, + CAIRO_INT_STATUS_INVALID_DSC_COMMENT, + CAIRO_INT_STATUS_INVALID_INDEX, + CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE, + CAIRO_INT_STATUS_TEMP_FILE_ERROR, + CAIRO_INT_STATUS_INVALID_STRIDE, + CAIRO_INT_STATUS_FONT_TYPE_MISMATCH, + CAIRO_INT_STATUS_USER_FONT_IMMUTABLE, + CAIRO_INT_STATUS_USER_FONT_ERROR, + CAIRO_INT_STATUS_NEGATIVE_COUNT, + CAIRO_INT_STATUS_INVALID_CLUSTERS, + CAIRO_INT_STATUS_INVALID_SLANT, + CAIRO_INT_STATUS_INVALID_WEIGHT, + CAIRO_INT_STATUS_INVALID_SIZE, + CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED, + CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH, + CAIRO_INT_STATUS_DEVICE_ERROR, + CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION, + CAIRO_INT_STATUS_DEVICE_FINISHED, + CAIRO_INT_STATUS_JBIG2_GLOBAL_MISSING, + CAIRO_INT_STATUS_PNG_ERROR, + CAIRO_INT_STATUS_FREETYPE_ERROR, + CAIRO_INT_STATUS_WIN32_GDI_ERROR, + CAIRO_INT_STATUS_TAG_ERROR, + + CAIRO_INT_STATUS_LAST_STATUS, + + CAIRO_INT_STATUS_UNSUPPORTED = 100, + CAIRO_INT_STATUS_DEGENERATE, + CAIRO_INT_STATUS_NOTHING_TO_DO, + CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, + CAIRO_INT_STATUS_IMAGE_FALLBACK, + CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, +}; + +typedef enum _cairo_int_status cairo_int_status_t; + #define _cairo_status_is_error(status) \ - (status != CAIRO_STATUS_SUCCESS && status <= CAIRO_STATUS_LAST_STATUS) + (status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS) + +#define _cairo_int_status_is_error(status) \ + (status != CAIRO_INT_STATUS_SUCCESS && status < CAIRO_INT_STATUS_LAST_STATUS) cairo_private cairo_status_t _cairo_error (cairo_status_t status); diff --git a/gfx/cairo/cairo/src/cairo-error.c b/gfx/cairo/cairo/src/cairo-error.c new file mode 100644 index 000000000000..1b9bd76be13a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-error.c @@ -0,0 +1,73 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#include "cairoint.h" +#include "cairo-private.h" + +#include "cairo-compiler-private.h" +#include "cairo-error-private.h" + +#include + +/** + * _cairo_error: + * @status: a status value indicating an error, (eg. not + * %CAIRO_STATUS_SUCCESS) + * + * Checks that status is an error status, but does nothing else. + * + * All assignments of an error status to any user-visible object + * within the cairo application should result in a call to + * _cairo_error(). + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +cairo_status_t +_cairo_error (cairo_status_t status) +{ + CAIRO_ENSURE_UNIQUE; + assert (_cairo_status_is_error (status)); + + return status; +} + +COMPILE_TIME_ASSERT ((int)CAIRO_INT_STATUS_LAST_STATUS == (int)CAIRO_STATUS_LAST_STATUS); diff --git a/gfx/cairo/cairo/src/cairo-fallback-compositor.c b/gfx/cairo/cairo/src/cairo-fallback-compositor.c new file mode 100644 index 000000000000..3f6199fe2bbd --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-fallback-compositor.c @@ -0,0 +1,185 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-offset-private.h" + +/* high-level compositor interface */ + +static cairo_int_status_t +_cairo_fallback_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_image_surface_t *image; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); + + status = _cairo_surface_offset_paint (&image->base, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + extents->clip); + + return _cairo_surface_unmap_image (extents->surface, image); +} + +static cairo_int_status_t +_cairo_fallback_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_image_surface_t *image; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); + + status = _cairo_surface_offset_mask (&image->base, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + &extents->mask_pattern.base, + extents->clip); + + return _cairo_surface_unmap_image (extents->surface, image); +} + +static cairo_int_status_t +_cairo_fallback_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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_image_surface_t *image; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); + + status = _cairo_surface_offset_stroke (&image->base, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + path, style, + ctm, ctm_inverse, + tolerance, + antialias, + extents->clip); + + return _cairo_surface_unmap_image (extents->surface, image); +} + +static cairo_int_status_t +_cairo_fallback_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_image_surface_t *image; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); + + status = _cairo_surface_offset_fill (&image->base, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + path, + fill_rule, tolerance, antialias, + extents->clip); + + return _cairo_surface_unmap_image (extents->surface, image); +} + +static cairo_int_status_t +_cairo_fallback_compositor_glyphs (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + cairo_image_surface_t *image; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded); + + status = _cairo_surface_offset_glyphs (&image->base, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + scaled_font, glyphs, num_glyphs, + extents->clip); + + return _cairo_surface_unmap_image (extents->surface, image); +} + +const cairo_compositor_t _cairo_fallback_compositor = { + &__cairo_no_compositor, + + _cairo_fallback_compositor_paint, + _cairo_fallback_compositor_mask, + _cairo_fallback_compositor_stroke, + _cairo_fallback_compositor_fill, + _cairo_fallback_compositor_glyphs, +}; diff --git a/gfx/cairo/cairo/src/cairo-features-uninstalled.pc.in b/gfx/cairo/cairo/src/cairo-features-uninstalled.pc.in new file mode 100644 index 000000000000..b9cd9d3ad2b3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-features-uninstalled.pc.in @@ -0,0 +1,7 @@ +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/gfx/cairo/cairo/src/cairo-features-win32.h b/gfx/cairo/cairo/src/cairo-features-win32.h deleted file mode 100644 index ef202fc14758..000000000000 --- a/gfx/cairo/cairo/src/cairo-features-win32.h +++ /dev/null @@ -1,16 +0,0 @@ -/* Generated by configure. Do not edit. */ -#ifndef CAIRO_FEATURES_H -#define CAIRO_FEATURES_H - -#define HAVE_WINDOWS_H 1 - -#define CAIRO_HAS_WIN32_SURFACE 1 -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_PNG_FUNCTIONS 1 -#define CAIRO_HAS_PS_SURFACE 1 -#define CAIRO_HAS_PDF_SURFACE 1 -#define CAIRO_HAS_SVG_SURFACE 1 -#define CAIRO_HAS_IMAGE_SURFACE 1 -#define CAIRO_HAS_USER_FONT 1 - -#endif diff --git a/gfx/cairo/cairo/src/cairo-features.pc.in b/gfx/cairo/cairo/src/cairo-features.pc.in new file mode 100644 index 000000000000..9a4b657c8da4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-features.pc.in @@ -0,0 +1,12 @@ +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/gfx/cairo/cairo/src/cairo-fixed-private.h b/gfx/cairo/cairo/src/cairo-fixed-private.h index d34ac5ed32ca..5f9ce684c3f8 100644 --- a/gfx/cairo/cairo/src/cairo-fixed-private.h +++ b/gfx/cairo/cairo/src/cairo-fixed-private.h @@ -40,26 +40,28 @@ #include "cairo-fixed-type-private.h" #include "cairo-wideint-private.h" +#include "cairoint.h" /* Implementation */ #if (CAIRO_FIXED_BITS != 32) # error CAIRO_FIXED_BITS must be 32, and the type must be a 32-bit type. -# error To remove this limitation, you will have to fix the tesselator. +# error To remove this limitation, you will have to fix the tessellator. #endif #define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS)) #define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS)) -#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS)) #define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1)) +#define CAIRO_FIXED_ERROR_DOUBLE (1. / (2 * CAIRO_FIXED_ONE_DOUBLE)) + #define CAIRO_FIXED_FRAC_MASK ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS))) #define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK) static inline cairo_fixed_t _cairo_fixed_from_int (int i) { - return (cairo_fixed_t)((cairo_fixed_unsigned_t)i << CAIRO_FIXED_FRAC_BITS); + return i << CAIRO_FIXED_FRAC_BITS; } /* This is the "magic number" approach to converting a double into fixed @@ -152,12 +154,6 @@ _cairo_fixed_to_double (cairo_fixed_t f) return ((double) f) / CAIRO_FIXED_ONE_DOUBLE; } -static inline float -_cairo_fixed_to_float (cairo_fixed_t f) -{ - return ((float) f) / CAIRO_FIXED_ONE_FLOAT; -} - static inline int _cairo_fixed_is_integer (cairo_fixed_t f) { @@ -170,6 +166,12 @@ _cairo_fixed_floor (cairo_fixed_t f) return f & ~CAIRO_FIXED_FRAC_MASK; } +static inline cairo_fixed_t +_cairo_fixed_ceil (cairo_fixed_t f) +{ + return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK); +} + static inline cairo_fixed_t _cairo_fixed_round (cairo_fixed_t f) { @@ -221,7 +223,7 @@ _cairo_fixed_integer_ceil (cairo_fixed_t f) if (f > 0) return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1; else - return - (-f >> CAIRO_FIXED_FRAC_BITS); + return - ((cairo_fixed_t)(-(cairo_fixed_unsigned_t)f) >> CAIRO_FIXED_FRAC_BITS); } /* A bunch of explicit 16.16 operators; we need these @@ -310,7 +312,7 @@ _cairo_fixed_mul_div_floor (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) return _cairo_int64_32_div (_cairo_int32x32_64_mul (a, b), c); } - +/* compute y from x so that (x,y), p1, and p2 are collinear */ static inline cairo_fixed_t _cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1, const cairo_point_t *p2, @@ -331,6 +333,7 @@ _cairo_edge_compute_intersection_y_for_x (const cairo_point_t *p1, return y; } +/* compute x from y so that (x,y), p1, and p2 are collinear */ static inline cairo_fixed_t _cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, const cairo_point_t *p2, @@ -351,6 +354,40 @@ _cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, return x; } +/* Intersect two segments based on the algorithm described at + * http://paulbourke.net/geometry/pointlineplane/. This implementation + * uses floating point math. */ +static inline cairo_bool_t +_slow_segment_intersection (const cairo_point_t *seg1_p1, + const cairo_point_t *seg1_p2, + const cairo_point_t *seg2_p1, + const cairo_point_t *seg2_p2, + cairo_point_t *intersection) +{ + double denominator, u_a, u_b; + double seg1_dx, seg1_dy, seg2_dx, seg2_dy, seg_start_dx, seg_start_dy; + + seg1_dx = _cairo_fixed_to_double (seg1_p2->x - seg1_p1->x); + seg1_dy = _cairo_fixed_to_double (seg1_p2->y - seg1_p1->y); + seg2_dx = _cairo_fixed_to_double (seg2_p2->x - seg2_p1->x); + seg2_dy = _cairo_fixed_to_double (seg2_p2->y - seg2_p1->y); + denominator = (seg2_dy * seg1_dx) - (seg2_dx * seg1_dy); + if (denominator == 0) + return FALSE; + + seg_start_dx = _cairo_fixed_to_double (seg1_p1->x - seg2_p1->x); + seg_start_dy = _cairo_fixed_to_double (seg1_p1->y - seg2_p1->y); + u_a = ((seg2_dx * seg_start_dy) - (seg2_dy * seg_start_dx)) / denominator; + u_b = ((seg1_dx * seg_start_dy) - (seg1_dy * seg_start_dx)) / denominator; + + if (u_a <= 0 || u_a >= 1 || u_b <= 0 || u_b >= 1) + return FALSE; + + intersection->x = seg1_p1->x + _cairo_fixed_from_double ((u_a * seg1_dx)); + intersection->y = seg1_p1->y + _cairo_fixed_from_double ((u_a * seg1_dy)); + return TRUE; +} + #else # error Please define multiplication and other operands for your fixed-point type size #endif diff --git a/gfx/cairo/cairo/src/cairo-fixed-type-private.h b/gfx/cairo/cairo/src/cairo-fixed-type-private.h index 2bbd5f786210..e9f26f615395 100644 --- a/gfx/cairo/cairo/src/cairo-fixed-type-private.h +++ b/gfx/cairo/cairo/src/cairo-fixed-type-private.h @@ -50,7 +50,7 @@ typedef cairo_int128_t cairo_fixed_64_64_t; typedef cairo_int128_t cairo_fixed_96_32_t; /* Eventually, we should allow changing this, but I think - * there are some assumptions in the tesselator about the + * there are some assumptions in the tessellator about the * size of a fixed type. For now, it must be 32. */ #define CAIRO_FIXED_BITS 32 diff --git a/gfx/cairo/cairo/src/cairo-font-face-twin.c b/gfx/cairo/cairo/src/cairo-font-face-twin.c index 98c6dd8a90a9..a0855c9f6011 100644 --- a/gfx/cairo/cairo/src/cairo-font-face-twin.c +++ b/gfx/cairo/cairo/src/cairo-font-face-twin.c @@ -283,16 +283,14 @@ face_props_parse (twin_face_properties_t *props, parse_field (props, start, end - start); } -static cairo_status_t -twin_font_face_create_properties (cairo_font_face_t *twin_face, - twin_face_properties_t **props_out) +static twin_face_properties_t * +twin_font_face_create_properties (cairo_font_face_t *twin_face) { twin_face_properties_t *props; - cairo_status_t status; - props = malloc (sizeof (twin_face_properties_t)); + props = _cairo_malloc (sizeof (twin_face_properties_t)); if (unlikely (props == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return NULL; props->stretch = TWIN_STRETCH_NORMAL; props->slant = CAIRO_FONT_SLANT_NORMAL; @@ -300,30 +298,25 @@ twin_font_face_create_properties (cairo_font_face_t *twin_face, props->monospace = FALSE; props->smallcaps = FALSE; - status = cairo_font_face_set_user_data (twin_face, + if (unlikely (cairo_font_face_set_user_data (twin_face, &twin_properties_key, - props, free); - if (unlikely (status)) { + props, free))) { free (props); - return status; + return NULL; } - if (props_out) - *props_out = props; - - return CAIRO_STATUS_SUCCESS; + return props; } static cairo_status_t twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, cairo_toy_font_face_t *toy_face) { - cairo_status_t status; twin_face_properties_t *props; - status = twin_font_face_create_properties (twin_face, &props); - if (unlikely (status)) - return status; + props = twin_font_face_create_properties (twin_face); + if (unlikely (props == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); props->slant = toy_face->slant; props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ? @@ -419,7 +412,7 @@ twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, cairo_status_t status; twin_scaled_properties_t *props; - props = malloc (sizeof (twin_scaled_properties_t)); + props = _cairo_malloc (sizeof (twin_scaled_properties_t)); if (unlikely (props == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -729,11 +722,9 @@ cairo_font_face_t * _cairo_font_face_twin_create_fallback (void) { cairo_font_face_t *twin_font_face; - cairo_status_t status; twin_font_face = _cairo_font_face_twin_create_internal (); - status = twin_font_face_create_properties (twin_font_face, NULL); - if (status) { + if (! twin_font_face_create_properties (twin_font_face)) { cairo_font_face_destroy (twin_font_face); return (cairo_font_face_t *) &_cairo_font_face_nil; } diff --git a/gfx/cairo/cairo/src/cairo-font-face.c b/gfx/cairo/cairo/src/cairo-font-face.c index 554c65b299ca..e10a6eac2f2c 100644 --- a/gfx/cairo/cairo/src/cairo-font-face.c +++ b/gfx/cairo/cairo/src/cairo-font-face.c @@ -49,14 +49,14 @@ * * #cairo_font_face_t represents a particular font at a particular weight, * slant, and other characteristic but no size, transformation, or size. - * + * * Font faces are created using font-backend-specific * constructors, typically of the form - * cairo_backend_font_face_create(), or implicitly - * using the toy text API by way of + * cairo_backend_font_face_create(), + * or implicitly using the toy text API by way of * cairo_select_font_face(). The resulting face can be accessed using * cairo_get_font_face(). - */ + **/ /* #cairo_font_face_t */ @@ -67,6 +67,13 @@ const cairo_font_face_t _cairo_font_face_nil = { { 0, 0, 0, NULL }, /* user_data */ NULL }; +const cairo_font_face_t _cairo_font_face_nil_file_not_found = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_FILE_NOT_FOUND, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; cairo_status_t _cairo_font_face_set_error (cairo_font_face_t *font_face, @@ -104,16 +111,18 @@ _cairo_font_face_init (cairo_font_face_t *font_face, * @font_face from being destroyed until a matching call to * cairo_font_face_destroy() is made. * - * The number of references to a #cairo_font_face_t can be get using - * cairo_font_face_get_reference_count(). + * Use cairo_font_face_get_reference_count() to get the number of + * references to a #cairo_font_face_t. * * Return value: the referenced #cairo_font_face_t. + * + * Since: 1.0 **/ cairo_font_face_t * cairo_font_face_reference (cairo_font_face_t *font_face) { if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return font_face; /* We would normally assert that we have a reference here but we @@ -126,6 +135,28 @@ cairo_font_face_reference (cairo_font_face_t *font_face) } slim_hidden_def (cairo_font_face_reference); +static inline cairo_bool_t +__put(cairo_reference_count_t *v) +{ + int c, old; + + c = CAIRO_REFERENCE_COUNT_GET_VALUE(v); + while (c != 1 && (old = _cairo_atomic_int_cmpxchg_return_old(&v->ref_count, c, c - 1)) != c) + c = old; + + return c != 1; +} + +cairo_bool_t +_cairo_font_face_destroy (void *abstract_face) +{ +#if 0 /* Nothing needs to be done, we can just drop the last reference */ + cairo_font_face_t *font_face = abstract_face; + return _cairo_reference_count_dec_and_test (&font_face->ref_count); +#endif + return TRUE; +} + /** * cairo_font_face_destroy: * @font_face: a #cairo_font_face_t @@ -133,37 +164,30 @@ slim_hidden_def (cairo_font_face_reference); * Decreases the reference count on @font_face by one. If the result * is zero, then @font_face and all associated resources are freed. * See cairo_font_face_reference(). + * + * Since: 1.0 **/ void cairo_font_face_destroy (cairo_font_face_t *font_face) { if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return; assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)); - if (! _cairo_reference_count_dec_and_test (&font_face->ref_count)) - return; - - if (font_face->backend->destroy) - font_face->backend->destroy (font_face); - /* We allow resurrection to deal with some memory management for the * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t * need to effectively mutually reference each other */ - if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)) + if (__put (&font_face->ref_count)) return; - if (font_face->backend->lock) - font_face->backend->lock (font_face); + if (! font_face->backend->destroy (font_face)) + return; _cairo_user_data_array_fini (&font_face->user_data); - if (font_face->backend->unlock) - font_face->backend->unlock (font_face); - free (font_face); } slim_hidden_def (cairo_font_face_destroy); @@ -203,7 +227,7 @@ unsigned int cairo_font_face_get_reference_count (cairo_font_face_t *font_face) { if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return 0; return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count); @@ -218,6 +242,8 @@ cairo_font_face_get_reference_count (cairo_font_face_t *font_face) * * Return value: %CAIRO_STATUS_SUCCESS or another error such as * %CAIRO_STATUS_NO_MEMORY. + * + * Since: 1.0 **/ cairo_status_t cairo_font_face_status (cairo_font_face_t *font_face) @@ -236,19 +262,15 @@ cairo_font_face_status (cairo_font_face_t *font_face) * function returns %NULL. * * Return value: the user data previously attached or %NULL. + * + * Since: 1.0 **/ void * cairo_font_face_get_user_data (cairo_font_face_t *font_face, const cairo_user_data_key_t *key) { - void *result; - if (font_face->backend->lock) - font_face->backend->lock (font_face); - result = _cairo_user_data_array_get_data (&font_face->user_data, - key); - if (font_face->backend->unlock) - font_face->backend->unlock (font_face); - return result; + return _cairo_user_data_array_get_data (&font_face->user_data, + key); } slim_hidden_def (cairo_font_face_get_user_data); @@ -267,6 +289,8 @@ slim_hidden_def (cairo_font_face_get_user_data); * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. + * + * Since: 1.0 **/ cairo_status_t cairo_font_face_set_user_data (cairo_font_face_t *font_face, @@ -277,14 +301,8 @@ cairo_font_face_set_user_data (cairo_font_face_t *font_face, if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) return font_face->status; - cairo_status_t status; - if (font_face->backend->lock) - font_face->backend->lock (font_face); - status = _cairo_user_data_array_set_data (&font_face->user_data, - key, user_data, destroy); - if (font_face->backend->unlock) - font_face->backend->unlock (font_face); - return status; + return _cairo_user_data_array_set_data (&font_face->user_data, + key, user_data, destroy); } slim_hidden_def (cairo_font_face_set_user_data); @@ -317,10 +335,11 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); - if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count)) + if (__put (&unscaled_font->ref_count)) return; - unscaled_font->backend->destroy (unscaled_font); + if (! unscaled_font->backend->destroy (unscaled_font)) + return; free (unscaled_font); } diff --git a/gfx/cairo/cairo/src/cairo-font-options.c b/gfx/cairo/cairo/src/cairo-font-options.c index 2b12771f8d39..9aef14073862 100644 --- a/gfx/cairo/cairo/src/cairo-font-options.c +++ b/gfx/cairo/cairo/src/cairo-font-options.c @@ -47,7 +47,7 @@ * time the font options implied by a surface are just right and do not * need any changes, but for pixel-based targets tweaking font options * may result in superior output on a particular display. - */ + **/ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_ANTIALIAS_DEFAULT, @@ -55,7 +55,8 @@ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_LCD_FILTER_DEFAULT, CAIRO_HINT_STYLE_DEFAULT, CAIRO_HINT_METRICS_DEFAULT, - CAIRO_ROUND_GLYPH_POS_DEFAULT + CAIRO_ROUND_GLYPH_POS_DEFAULT, + NULL }; /** @@ -73,6 +74,7 @@ _cairo_font_options_init_default (cairo_font_options_t *options) options->hint_style = CAIRO_HINT_STYLE_DEFAULT; options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT; options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT; + options->variations = NULL; } void @@ -85,6 +87,7 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, options->hint_style = other->hint_style; options->hint_metrics = other->hint_metrics; options->round_glyph_positions = other->round_glyph_positions; + options->variations = other->variations ? strdup (other->variations) : NULL; } /** @@ -98,13 +101,15 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_font_options_status(). + * + * Since: 1.0 **/ cairo_font_options_t * cairo_font_options_create (void) { cairo_font_options_t *options; - options = malloc (sizeof (cairo_font_options_t)); + options = _cairo_malloc (sizeof (cairo_font_options_t)); if (!options) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_options_t *) &_cairo_font_options_nil; @@ -127,6 +132,8 @@ cairo_font_options_create (void) * valid pointer; if memory cannot be allocated, then a special * error object is returned where all operations on the object do nothing. * You can check for this with cairo_font_options_status(). + * + * Since: 1.0 **/ cairo_font_options_t * cairo_font_options_copy (const cairo_font_options_t *original) @@ -136,7 +143,7 @@ cairo_font_options_copy (const cairo_font_options_t *original) if (cairo_font_options_status ((cairo_font_options_t *) original)) return (cairo_font_options_t *) &_cairo_font_options_nil; - options = malloc (sizeof (cairo_font_options_t)); + options = _cairo_malloc (sizeof (cairo_font_options_t)); if (!options) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_options_t *) &_cairo_font_options_nil; @@ -147,12 +154,20 @@ cairo_font_options_copy (const cairo_font_options_t *original) return options; } +void +_cairo_font_options_fini (cairo_font_options_t *options) +{ + free (options->variations); +} + /** * cairo_font_options_destroy: * @options: a #cairo_font_options_t * * Destroys a #cairo_font_options_t object created with * cairo_font_options_create() or cairo_font_options_copy(). + * + * Since: 1.0 **/ void cairo_font_options_destroy (cairo_font_options_t *options) @@ -160,6 +175,7 @@ cairo_font_options_destroy (cairo_font_options_t *options) if (cairo_font_options_status (options)) return; + _cairo_font_options_fini (options); free (options); } @@ -171,6 +187,8 @@ cairo_font_options_destroy (cairo_font_options_t *options) * font options object * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.0 **/ cairo_status_t cairo_font_options_status (cairo_font_options_t *options) @@ -192,7 +210,9 @@ slim_hidden_def (cairo_font_options_status); * Merges non-default options from @other into @options, replacing * existing values. This operation can be thought of as somewhat * similar to compositing @other onto @options with the operation - * of %CAIRO_OPERATION_OVER. + * of %CAIRO_OPERATOR_OVER. + * + * Since: 1.0 **/ void cairo_font_options_merge (cairo_font_options_t *options, @@ -216,6 +236,24 @@ cairo_font_options_merge (cairo_font_options_t *options, options->hint_metrics = other->hint_metrics; if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) options->round_glyph_positions = other->round_glyph_positions; + + if (other->variations) { + if (options->variations) { + char *p; + + /* 'merge' variations by concatenating - later entries win */ + p = malloc (strlen (other->variations) + strlen (options->variations) + 2); + p[0] = 0; + strcat (p, options->variations); + strcat (p, ","); + strcat (p, other->variations); + free (options->variations); + options->variations = p; + } + else { + options->variations = strdup (other->variations); + } + } } slim_hidden_def (cairo_font_options_merge); @@ -229,6 +267,8 @@ slim_hidden_def (cairo_font_options_merge); * Return value: %TRUE if all fields of the two font options objects match. * Note that this function will return %FALSE if either object is in * error. + * + * Since: 1.0 **/ cairo_bool_t cairo_font_options_equal (const cairo_font_options_t *options, @@ -247,7 +287,10 @@ cairo_font_options_equal (const cairo_font_options_t *options, options->lcd_filter == other->lcd_filter && options->hint_style == other->hint_style && options->hint_metrics == other->hint_metrics && - options->round_glyph_positions == other->round_glyph_positions); + options->round_glyph_positions == other->round_glyph_positions && + ((options->variations == NULL && other->variations == NULL) || + (options->variations != NULL && other->variations != NULL && + strcmp (options->variations, other->variations) == 0))); } slim_hidden_def (cairo_font_options_equal); @@ -262,18 +305,25 @@ slim_hidden_def (cairo_font_options_equal); * Return value: the hash value for the font options object. * The return value can be cast to a 32-bit type if a * 32-bit hash value is needed. + * + * Since: 1.0 **/ unsigned long cairo_font_options_hash (const cairo_font_options_t *options) { + unsigned long hash = 0; + if (cairo_font_options_status ((cairo_font_options_t *) options)) options = &_cairo_font_options_nil; /* force default values */ + if (options->variations) + hash = _cairo_string_hash (options->variations, strlen (options->variations)); + return ((options->antialias) | (options->subpixel_order << 4) | (options->lcd_filter << 8) | (options->hint_style << 12) | - (options->hint_metrics << 16)); + (options->hint_metrics << 16)) ^ hash; } slim_hidden_def (cairo_font_options_hash); @@ -284,6 +334,8 @@ slim_hidden_def (cairo_font_options_hash); * * Sets the antialiasing mode for the font options object. This * specifies the type of antialiasing to do when rendering text. + * + * Since: 1.0 **/ void cairo_font_options_set_antialias (cairo_font_options_t *options, @@ -303,6 +355,8 @@ slim_hidden_def (cairo_font_options_set_antialias); * Gets the antialiasing mode for the font options object. * * Return value: the antialiasing mode + * + * Since: 1.0 **/ cairo_antialias_t cairo_font_options_get_antialias (const cairo_font_options_t *options) @@ -323,6 +377,8 @@ cairo_font_options_get_antialias (const cairo_font_options_t *options) * the display device when rendering with an antialiasing mode of * %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for * #cairo_subpixel_order_t for full details. + * + * Since: 1.0 **/ void cairo_font_options_set_subpixel_order (cairo_font_options_t *options, @@ -343,6 +399,8 @@ slim_hidden_def (cairo_font_options_set_subpixel_order); * See the documentation for #cairo_subpixel_order_t for full details. * * Return value: the subpixel order for the font options object + * + * Since: 1.0 **/ cairo_subpixel_order_t cairo_font_options_get_subpixel_order (const cairo_font_options_t *options) @@ -362,11 +420,9 @@ cairo_font_options_get_subpixel_order (const cairo_font_options_t *options) * specifies how pixels are filtered when rendered with an antialiasing * mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for * #cairo_lcd_filter_t for full details. - * - * Since: 1.8 **/ void -cairo_font_options_set_lcd_filter (cairo_font_options_t *options, +_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, cairo_lcd_filter_t lcd_filter) { if (cairo_font_options_status (options)) @@ -374,7 +430,6 @@ cairo_font_options_set_lcd_filter (cairo_font_options_t *options, options->lcd_filter = lcd_filter; } -slim_hidden_def (cairo_font_options_set_lcd_filter); /** * _cairo_font_options_get_lcd_filter: @@ -384,8 +439,6 @@ slim_hidden_def (cairo_font_options_set_lcd_filter); * See the documentation for #cairo_lcd_filter_t for full details. * * Return value: the LCD filter for the font options object - * - * Since: 1.8 **/ cairo_lcd_filter_t _cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) @@ -403,8 +456,6 @@ _cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) * * Sets the rounding options for the font options object. If rounding is set, a * glyph's position will be rounded to integer values. - * - * Since: 1.12 **/ void _cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, @@ -423,8 +474,6 @@ _cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, * Gets the glyph position rounding option for the font options object. * * Return value: The round glyph posistions flag for the font options object. - * - * Since: 1.12 **/ cairo_round_glyph_positions_t _cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options) @@ -444,6 +493,8 @@ _cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *optio * This controls whether to fit font outlines to the pixel grid, * and if so, whether to optimize for fidelity or contrast. * See the documentation for #cairo_hint_style_t for full details. + * + * Since: 1.0 **/ void cairo_font_options_set_hint_style (cairo_font_options_t *options, @@ -464,6 +515,8 @@ slim_hidden_def (cairo_font_options_set_hint_style); * See the documentation for #cairo_hint_style_t for full details. * * Return value: the hint style for the font options object + * + * Since: 1.0 **/ cairo_hint_style_t cairo_font_options_get_hint_style (const cairo_font_options_t *options) @@ -483,6 +536,8 @@ cairo_font_options_get_hint_style (const cairo_font_options_t *options) * controls whether metrics are quantized to integer values in * device units. * See the documentation for #cairo_hint_metrics_t for full details. + * + * Since: 1.0 **/ void cairo_font_options_set_hint_metrics (cairo_font_options_t *options, @@ -503,6 +558,8 @@ slim_hidden_def (cairo_font_options_set_hint_metrics); * See the documentation for #cairo_hint_metrics_t for full details. * * Return value: the metrics hinting mode for the font options object + * + * Since: 1.0 **/ cairo_hint_metrics_t cairo_font_options_get_hint_metrics (const cairo_font_options_t *options) @@ -512,3 +569,54 @@ cairo_font_options_get_hint_metrics (const cairo_font_options_t *options) return options->hint_metrics; } + +/** + * cairo_font_options_set_variations: + * @options: a #cairo_font_options_t + * @variations: the new font variations, or %NULL + * + * Sets the OpenType font variations for the font options object. + * Font variations are specified as a string with a format that + * is similar to the CSS font-variation-settings. The string contains + * a comma-separated list of axis assignments, which each assignment + * consists of a 4-character axis name and a value, separated by + * whitespace and optional equals sign. + * + * Examples: + * + * wght=200,wdth=140.5 + * + * wght 200 , wdth 140.5 + * + * Since: 1.16 + **/ +void +cairo_font_options_set_variations (cairo_font_options_t *options, + const char *variations) +{ + char *tmp = variations ? strdup (variations) : NULL; + free (options->variations); + options->variations = tmp; +} + +/** + * cairo_font_options_get_variations: + * @options: a #cairo_font_options_t + * + * Gets the OpenType font variations for the font options object. + * See cairo_font_options_set_variations() for details about the + * string format. + * + * Return value: the font variations for the font options object. The + * returned string belongs to the @options and must not be modified. + * It is valid until either the font options object is destroyed or + * the font variations in this object is modified with + * cairo_font_options_set_variations(). + * + * Since: 1.16 + **/ +const char * +cairo_font_options_get_variations (cairo_font_options_t *options) +{ + return options->variations; +} diff --git a/gfx/cairo/cairo/src/cairo-freed-pool-private.h b/gfx/cairo/cairo/src/cairo-freed-pool-private.h index c73e593c0297..8a7af523db30 100644 --- a/gfx/cairo/cairo/src/cairo-freed-pool-private.h +++ b/gfx/cairo/cairo/src/cairo-freed-pool-private.h @@ -40,11 +40,15 @@ #include "cairoint.h" #include "cairo-atomic-private.h" -#if HAS_ATOMIC_OPS +CAIRO_BEGIN_DECLS + +#define DISABLE_FREED_POOLS 0 + +#if HAS_ATOMIC_OPS && ! DISABLE_FREED_POOLS /* Keep a stash of recently freed clip_paths, since we need to * reallocate them frequently. */ -#define MAX_FREED_POOL_SIZE 4 +#define MAX_FREED_POOL_SIZE 16 typedef struct { void *pool[MAX_FREED_POOL_SIZE]; cairo_atomic_int_t top; @@ -118,6 +122,10 @@ _freed_pool_reset (freed_pool_t *pool); #else +/* A warning about an unused freed-pool in a build without atomics + * enabled usually indicates a missing _freed_pool_reset() in the + * static reset function */ + typedef int freed_pool_t; #define _freed_pool_get(pool) NULL @@ -126,4 +134,6 @@ typedef int freed_pool_t; #endif +CAIRO_END_DECLS + #endif /* CAIRO_FREED_POOL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-freelist-private.h b/gfx/cairo/cairo/src/cairo-freelist-private.h index 703181b567c3..f85f6891412d 100644 --- a/gfx/cairo/cairo/src/cairo-freelist-private.h +++ b/gfx/cairo/cairo/src/cairo-freelist-private.h @@ -111,7 +111,7 @@ _cairo_freepool_alloc (cairo_freepool_t *freepool) cairo_freelist_node_t *node; node = freepool->first_free_node; - if (unlikely (node == NULL)) + if (node == NULL) return _cairo_freepool_alloc_from_pool (freepool); VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); @@ -133,7 +133,7 @@ _cairo_freepool_free (cairo_freepool_t *freepool, void *ptr) node->next = freepool->first_free_node; freepool->first_free_node = node; - VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize)); + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); } #endif /* CAIRO_FREELIST_H */ diff --git a/gfx/cairo/cairo/src/cairo-freelist.c b/gfx/cairo/cairo/src/cairo-freelist.c index d596eab81dcb..7f2f3b30b811 100644 --- a/gfx/cairo/cairo/src/cairo-freelist.c +++ b/gfx/cairo/cairo/src/cairo-freelist.c @@ -61,7 +61,7 @@ _cairo_freelist_alloc (cairo_freelist_t *freelist) return node; } - return malloc (freelist->nodesize); + return _cairo_malloc (freelist->nodesize); } void * @@ -80,7 +80,7 @@ _cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode) if (node) { node->next = freelist->first_free_node; freelist->first_free_node = node; - VG (VALGRIND_MAKE_MEM_NOACCESS (node, freelist->nodesize)); + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize)); } } @@ -97,7 +97,7 @@ _cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize) freepool->embedded_pool.rem = sizeof (freepool->embedded_data); freepool->embedded_pool.data = freepool->embedded_data; - VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data, sizeof (freepool->embedded_data))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (freepool->embedded_data, sizeof (freepool->embedded_data))); } void @@ -119,7 +119,7 @@ _cairo_freepool_fini (cairo_freepool_t *freepool) pool = next; } - VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (freepool, sizeof (freepool))); } void * @@ -139,7 +139,7 @@ _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool) else poolsize = (128 * freepool->nodesize + 8191) & -8192; - pool = malloc (sizeof (cairo_freelist_pool_t) + poolsize); + pool = _cairo_malloc (sizeof (cairo_freelist_pool_t) + poolsize); if (unlikely (pool == NULL)) return pool; @@ -152,7 +152,7 @@ _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool) pool->rem = poolsize - freepool->nodesize; pool->data = (uint8_t *) (pool + 1) + freepool->nodesize; - VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, pool->rem)); + VG (VALGRIND_MAKE_MEM_UNDEFINED (pool->data, pool->rem)); return pool + 1; } diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c index 257af94d4e57..d2221edec233 100644 --- a/gfx/cairo/cairo/src/cairo-ft-font.c +++ b/gfx/cairo/cairo/src/cairo-ft-font.c @@ -38,11 +38,14 @@ * Carl Worth */ -#define _BSD_SOURCE /* for strdup() */ +#define _DEFAULT_SOURCE /* for strdup() */ #include "cairoint.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" #include "cairo-ft-private.h" +#include "cairo-pattern-private.h" +#include "cairo-pixman-private.h" #include @@ -54,6 +57,7 @@ #include FT_IMAGE_H #include FT_BITMAP_H #include FT_TRUETYPE_TABLES_H +#include FT_XFREE86_H #include FT_MULTIPLE_MASTERS_H #if HAVE_FT_GLYPHSLOT_EMBOLDEN #include FT_SYNTHESIS_H @@ -63,11 +67,10 @@ #include FT_LCD_FILTER_H #endif -#define _GNU_SOURCE /* for RTLD_DEFAULT */ -#include - -#ifndef RTLD_DEFAULT -#define RTLD_DEFAULT ((void *) 0) +#if HAVE_UNISTD_H +#include +#else +#define access(p, m) 0 #endif /* Fontconfig version older than 2.6 didn't have these options */ @@ -90,10 +93,6 @@ #define FT_LCD_FILTER_LEGACY 16 #endif -typedef FT_Error (*setLcdFilterFunc)(FT_Library, int); -static setLcdFilterFunc setLcdFilter; - -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) #define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) @@ -101,27 +100,6 @@ static setLcdFilterFunc setLcdFilter; /* This is the max number of FT_face objects we keep open at once */ #define MAX_OPEN_FACES 10 -/* This is the maximum font size we allow to be passed to FT_Set_Char_Size - */ -#define MAX_FONT_SIZE 2000 - -extern void mozilla_AddRefSharedFTFace(void* aContext); -extern void mozilla_ReleaseSharedFTFace(void* aContext, void* aOwner); -/* Returns true if the face's state has been modifide by another owner. */ -extern int mozilla_LockSharedFTFace(void* aContext, void* aOwner); -extern void mozilla_UnlockSharedFTFace(void* aContext); -extern FT_Error mozilla_LoadFTGlyph(FT_Face aFace, uint32_t aGlyphIndex, int32_t aFlags); -extern void mozilla_LockFTLibrary(FT_Library aFTLibrary); -extern void mozilla_UnlockFTLibrary(FT_Library aFTLibrary); - -#define CAIRO_FT_LOCK(unscaled) \ - ((unscaled)->face_context \ - ? (void)mozilla_LockSharedFTFace((unscaled)->face_context, NULL) \ - : (void)CAIRO_MUTEX_LOCK((unscaled)->mutex)) -#define CAIRO_FT_UNLOCK(unscaled) \ - ((unscaled)->face_context \ - ? mozilla_UnlockSharedFTFace((unscaled)->face_context) \ - : (void)CAIRO_MUTEX_UNLOCK((unscaled)->mutex)) /** * SECTION:cairo-ft @@ -131,14 +109,16 @@ extern void mozilla_UnlockFTLibrary(FT_Library aFTLibrary); * * The FreeType font backend is primarily used to render text on GNU/Linux * systems, but can be used on other platforms too. - */ + **/ /** * CAIRO_HAS_FT_FONT: * * Defined if the FreeType font backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.0 + **/ /** * CAIRO_HAS_FC_FONT: @@ -146,7 +126,9 @@ extern void mozilla_UnlockFTLibrary(FT_Library aFTLibrary); * Defined if the Fontconfig-specific functions of the FreeType font backend * are available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.10 + **/ /* * The simple 2x2 matrix is converted into separate scale and shape @@ -172,7 +154,6 @@ struct _cairo_ft_unscaled_font { cairo_bool_t from_face; /* was the FT_Face provided by user? */ FT_Face face; /* provided or cached face */ - void *face_context; /* only set if from_face is false */ char *filename; @@ -187,6 +168,10 @@ struct _cairo_ft_unscaled_font { cairo_matrix_t current_shape; FT_Matrix Current_Shape; + unsigned int have_color_set : 1; + unsigned int have_color : 1; /* true if the font contains color glyphs */ + FT_Fixed *variations; /* variation settings that FT_Face came */ + cairo_mutex_t mutex; int lock_count; @@ -201,11 +186,26 @@ static void _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); typedef struct _cairo_ft_options { - cairo_font_options_t base; - int load_flags; /* flags for FT_Load_Glyph */ + cairo_font_options_t base; + unsigned int load_flags; /* flags for FT_Load_Glyph */ unsigned int synth_flags; } cairo_ft_options_t; +static void +_cairo_ft_options_init_copy (cairo_ft_options_t *options, + const cairo_ft_options_t *other) +{ + _cairo_font_options_init_copy (&options->base, &other->base); + options->load_flags = other->load_flags; + options->synth_flags = other->synth_flags; +} + +static void +_cairo_ft_options_fini (cairo_ft_options_t *options) +{ + _cairo_font_options_fini (&options->base); +} + struct _cairo_ft_font_face { cairo_font_face_t base; @@ -235,6 +235,20 @@ _cairo_ft_resolve_pattern (FcPattern *pattern, #endif +static cairo_status_t +_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; + } +} + /* * We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t. * The hash table itself isn't limited in size. However, we limit the @@ -253,6 +267,16 @@ typedef struct _cairo_ft_unscaled_font_map { static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL; +static FT_Face +_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled); + +static void +_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); + +static cairo_bool_t +_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); + + static void _font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map, cairo_ft_unscaled_font_t *unscaled) @@ -276,7 +300,7 @@ _cairo_ft_unscaled_font_map_create (void) * detect some other call path. */ assert (cairo_ft_unscaled_font_map == NULL); - font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t)); + font_map = _cairo_malloc (sizeof (cairo_ft_unscaled_font_map_t)); if (unlikely (font_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -312,11 +336,8 @@ _cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure) _cairo_hash_table_remove (font_map->hash_table, &unscaled->base.hash_entry); - if (unscaled->from_face) { - mozilla_ReleaseSharedFTFace (unscaled->face_context, unscaled); - } else { + if (! unscaled->from_face) _font_map_release_face_lock_held (font_map, unscaled); - } _cairo_ft_unscaled_font_fini (unscaled); free (unscaled); @@ -349,6 +370,8 @@ _cairo_ft_unscaled_font_map_destroy (void) static cairo_ft_unscaled_font_map_t * _cairo_ft_unscaled_font_map_lock (void) { + CAIRO_MUTEX_INITIALIZE (); + CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); if (unlikely (cairo_ft_unscaled_font_map == NULL)) { @@ -372,8 +395,7 @@ _cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key, cairo_bool_t from_face, char *filename, int id, - FT_Face face, - void *face_context) + FT_Face face) { unsigned long hash; @@ -381,7 +403,6 @@ _cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key, key->filename = filename; key->id = id; key->face = face; - key->face_context = face_context; hash = _cairo_hash_string (filename); /* the constants are just arbitrary primes */ @@ -417,27 +438,50 @@ _cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled, cairo_bool_t from_face, const char *filename, int id, - FT_Face face, - void *face_context) + FT_Face face) { _cairo_unscaled_font_init (&unscaled->base, &cairo_ft_unscaled_font_backend); + unscaled->variations = NULL; + if (from_face) { unscaled->from_face = TRUE; - _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face, face_context); + _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, id, face); + + + unscaled->have_color = FT_HAS_COLOR (face) != 0; + unscaled->have_color_set = TRUE; + +#ifdef HAVE_FT_GET_VAR_DESIGN_COORDINATES + { + FT_MM_Var *ft_mm_var; + if (0 == FT_Get_MM_Var (face, &ft_mm_var)) + { + unscaled->variations = calloc (ft_mm_var->num_axis, sizeof (FT_Fixed)); + if (unscaled->variations) + FT_Get_Var_Design_Coordinates (face, ft_mm_var->num_axis, unscaled->variations); +#if HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (face->glyph->library, ft_mm_var); +#else + free (ft_mm_var); +#endif + } + } +#endif } else { char *filename_copy; unscaled->from_face = FALSE; unscaled->face = NULL; - unscaled->face_context = NULL; filename_copy = strdup (filename); if (unlikely (filename_copy == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL, NULL); + _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL); + + unscaled->have_color_set = FALSE; } unscaled->have_scale = FALSE; @@ -465,10 +509,10 @@ _cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled) { assert (unscaled->face == NULL); - if (unscaled->filename) { - free (unscaled->filename); - unscaled->filename = NULL; - } + free (unscaled->filename); + unscaled->filename = NULL; + + free (unscaled->variations); CAIRO_MUTEX_FINI (unscaled->mutex); } @@ -482,7 +526,7 @@ _cairo_ft_unscaled_font_keys_equal (const void *key_a, if (unscaled_a->id == unscaled_b->id && unscaled_a->from_face == unscaled_b->from_face) - { + { if (unscaled_a->from_face) return unscaled_a->face == unscaled_b->face; @@ -505,7 +549,6 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, char *filename, int id, FT_Face font_face, - void *face_context, cairo_ft_unscaled_font_t **out) { cairo_ft_unscaled_font_t key, *unscaled; @@ -516,7 +559,7 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, if (unlikely (font_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face, face_context); + _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face); /* Return existing unscaled font if it exists in the hash table. */ unscaled = _cairo_hash_table_lookup (font_map->hash_table, @@ -527,13 +570,13 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, } /* Otherwise create it and insert into hash table. */ - unscaled = malloc (sizeof (cairo_ft_unscaled_font_t)); + unscaled = _cairo_malloc (sizeof (cairo_ft_unscaled_font_t)); if (unlikely (unscaled == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNWIND_FONT_MAP_LOCK; } - status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face, face_context); + status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face); if (unlikely (status)) goto UNWIND_UNSCALED_MALLOC; @@ -543,8 +586,6 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, if (unlikely (status)) goto UNWIND_UNSCALED_FONT_INIT; - mozilla_AddRefSharedFTFace (face_context); - DONE: _cairo_ft_unscaled_font_map_unlock (); *out = unscaled; @@ -580,12 +621,15 @@ _cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, if (ret == FcResultOutOfMemory) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (ret == FcResultMatch) { - /* If FC_INDEX is not set, we just use 0 */ - ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id); - if (ret == FcResultOutOfMemory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (access (filename, R_OK) == 0) { + /* If FC_INDEX is not set, we just use 0 */ + ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id); + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; + goto DONE; + } else + return _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); } /* The pattern contains neither a face nor a filename, resolve it later. */ @@ -594,36 +638,32 @@ _cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, DONE: return _cairo_ft_unscaled_font_create_internal (font_face != NULL, - filename, id, font_face, NULL, + filename, id, font_face, out); } #endif static cairo_status_t _cairo_ft_unscaled_font_create_from_face (FT_Face face, - void *face_context, cairo_ft_unscaled_font_t **out) { - return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, face_context, out); + return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, face->face_index, face, out); } -static void +static cairo_bool_t _cairo_ft_unscaled_font_destroy (void *abstract_font) { cairo_ft_unscaled_font_t *unscaled = abstract_font; cairo_ft_unscaled_font_map_t *font_map; - if (unscaled == NULL) - return; - font_map = _cairo_ft_unscaled_font_map_lock (); /* All created objects must have been mapped in the font map. */ assert (font_map != NULL); - if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) { + if (! _cairo_reference_count_dec_and_test (&unscaled->base.ref_count)) { /* somebody recreated the font whilst we waited for the lock */ _cairo_ft_unscaled_font_map_unlock (); - return; + return FALSE; } _cairo_hash_table_remove (font_map->hash_table, @@ -635,21 +675,17 @@ _cairo_ft_unscaled_font_destroy (void *abstract_font) */ if (unscaled->faces && unscaled->faces->unscaled == NULL) { assert (unscaled->faces->next == NULL); - // Guard user-data destruction with the unscaled font mutex. - CAIRO_FT_LOCK (unscaled); cairo_font_face_destroy (&unscaled->faces->base); - CAIRO_FT_UNLOCK (unscaled); } - mozilla_ReleaseSharedFTFace (unscaled->face_context, unscaled); } else { _font_map_release_face_lock_held (font_map, unscaled); } unscaled->face = NULL; - unscaled->face_context = NULL; _cairo_ft_unscaled_font_map_unlock (); _cairo_ft_unscaled_font_fini (unscaled); + return TRUE; } static cairo_bool_t @@ -666,20 +702,14 @@ _has_unlocked_face (const void *entry) * This differs from _cairo_ft_scaled_font_lock_face in that it doesn't * set the scale on the face, but just returns it at the last scale. */ -cairo_warn FT_Face +static cairo_warn FT_Face _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) { cairo_ft_unscaled_font_map_t *font_map; FT_Face face = NULL; FT_Error error; - if (unscaled->face_context) { - if (!mozilla_LockSharedFTFace(unscaled->face_context, unscaled)) { - unscaled->have_scale = FALSE; - } - } else { - CAIRO_FT_LOCK(unscaled); - } + CAIRO_MUTEX_LOCK (unscaled->mutex); unscaled->lock_count++; if (unscaled->face) @@ -702,35 +732,28 @@ _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) if (entry == NULL) break; - /* Must use try-lock here to avoid deadlock on multiple threads trying to - * acquire the font map lock inside the unscaled font mutexes. In the worst - * case, this may just cause a spin in the extremely unlikely circumstance - * that all open faces are currently locked. - */ - if (CAIRO_MUTEX_TRY_LOCK (entry->mutex)) - { - /* Verify the lock count is still actually 0 inside the mutex before - * trying to free the entry. - */ - if (_has_unlocked_face (entry)) - _font_map_release_face_lock_held (font_map, entry); - CAIRO_MUTEX_UNLOCK (entry->mutex); - } + _font_map_release_face_lock_held (font_map, entry); } } _cairo_ft_unscaled_font_map_unlock (); - error = FT_New_Face (font_map->ft_library, unscaled->filename, unscaled->id, &face); + error = FT_New_Face (font_map->ft_library, + unscaled->filename, + unscaled->id, + &face); if (error) { unscaled->lock_count--; - CAIRO_FT_UNLOCK (unscaled); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + CAIRO_MUTEX_UNLOCK (unscaled->mutex); + _cairo_error_throw (_ft_to_cairo_error (error)); return NULL; } unscaled->face = face; + unscaled->have_color = FT_HAS_COLOR (face) != 0; + unscaled->have_color_set = TRUE; + font_map->num_open_faces++; return face; @@ -739,14 +762,14 @@ _cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled) /* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face */ -void +static void _cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled) { assert (unscaled->lock_count > 0); unscaled->lock_count--; - CAIRO_FT_UNLOCK (unscaled); + CAIRO_MUTEX_UNLOCK (unscaled->mutex); } @@ -785,7 +808,6 @@ _compute_transform (cairo_ft_font_transform_t *sf, double min_distance = DBL_MAX; cairo_bool_t magnify = TRUE; int i; - int best_i = 0; double best_x_size = 0; double best_y_size = 0; @@ -804,7 +826,6 @@ _compute_transform (cairo_ft_font_transform_t *sf, if ((magnify && distance >= 0) || fabs (distance) <= min_distance) { magnify = distance < 0; min_distance = fabs (distance); - best_i = i; best_x_size = x_size; best_y_size = y_size; } @@ -838,7 +859,6 @@ _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, cairo_ft_font_transform_t sf; FT_Matrix mat; FT_Error error; - double x_scale, y_scale; assert (unscaled->face != NULL); @@ -877,14 +897,12 @@ _cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, FT_Set_Transform(unscaled->face, &mat, NULL); - x_scale = MIN(sf.x_scale, MAX_FONT_SIZE); - y_scale = MIN(sf.y_scale, MAX_FONT_SIZE); error = FT_Set_Char_Size (unscaled->face, - x_scale * 64.0 + .5, - y_scale * 64.0 + .5, + sf.x_scale * 64.0 + .5, + sf.y_scale * 64.0 + .5, 0, 0); if (error) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_error (_ft_to_cairo_error (error)); return CAIRO_STATUS_SUCCESS; } @@ -965,6 +983,13 @@ _compute_xrender_bitmap_size(FT_Bitmap *target, pitch = width * 4; break; +#ifdef FT_LOAD_COLOR + case FT_PIXEL_MODE_BGRA: + /* each pixel is replicated into a 32-bit ARGB value */ + pitch = width * 4; + break; +#endif + default: /* unsupported source format */ return -1; } @@ -1122,7 +1147,7 @@ _fill_xrender_bitmap(FT_Bitmap *target, } break; - default: /* FT_PIXEL_MODE_LCD_V */ + case FT_PIXEL_MODE_LCD_V: /* convert vertical RGB into ARGB32 */ if (!bgr) { @@ -1159,6 +1184,17 @@ _fill_xrender_bitmap(FT_Bitmap *target, } } } + break; + +#ifdef FT_LOAD_COLOR + case FT_PIXEL_MODE_BGRA: + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) + memcpy (dstLine, srcLine, width * 4); + break; +#endif + + default: + assert (0); } } @@ -1172,9 +1208,10 @@ _get_bitmap_surface (FT_Bitmap *bitmap, cairo_font_options_t *font_options, cairo_image_surface_t **surface) { - int width, height, stride; + unsigned int width, height; unsigned char *data; int format = CAIRO_FORMAT_A8; + int stride; cairo_image_surface_t *image; cairo_bool_t component_alpha = FALSE; @@ -1203,31 +1240,23 @@ _get_bitmap_surface (FT_Bitmap *bitmap, } else { int i; unsigned char *source, *dest; - int copy_len = MIN (stride, bitmap->pitch); - int pad_len = stride - bitmap->pitch; source = bitmap->buffer; dest = data; for (i = height; i; i--) { - memcpy (dest, source, copy_len); + memcpy (dest, source, bitmap->pitch); + memset (dest + bitmap->pitch, '\0', stride - bitmap->pitch); + source += bitmap->pitch; dest += stride; } - /* do we really care about zeroing any extra row padding in dest? */ - if (pad_len > 0) { - dest = data + copy_len; - for (i = height; i; i--) { - memset (dest, '\0', pad_len); - dest += stride; - } - } } } #ifndef WORDS_BIGENDIAN { - uint8_t *d = data; - int count = stride * height; + uint8_t *d = data; + int count = stride * height; while (count--) { *d = CAIRO_BITSWAP8 (*d); @@ -1283,6 +1312,15 @@ _get_bitmap_surface (FT_Bitmap *bitmap, memcpy (data, bitmap->buffer, stride * height); } + + if (!_cairo_is_little_endian ()) + { + /* Byteswap. */ + unsigned int i, count = height * width; + uint32_t *p = (uint32_t *) data; + for (i = 0; i < count; i++) + p[i] = be32_to_cpu (p[i]); + } format = CAIRO_FORMAT_ARGB32; break; #endif @@ -1296,6 +1334,7 @@ _get_bitmap_surface (FT_Bitmap *bitmap, FT_Bitmap tmp; FT_Int align; + FT_Error error; format = CAIRO_FORMAT_A8; @@ -1303,8 +1342,9 @@ _get_bitmap_surface (FT_Bitmap *bitmap, FT_Bitmap_New( &tmp ); - if (FT_Bitmap_Convert( library, bitmap, &tmp, align )) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + error = FT_Bitmap_Convert( library, bitmap, &tmp, align ); + if (error) + return _cairo_error (_ft_to_cairo_error (error)); FT_Bitmap_Done( library, bitmap ); *bitmap = tmp; @@ -1329,11 +1369,12 @@ _get_bitmap_surface (FT_Bitmap *bitmap, memcpy (data, bitmap->buffer, stride * height); break; } + /* fall through */ /* These could be triggered by very rare types of TrueType fonts */ default: if (own_buffer) free (bitmap->buffer); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); } /* XXX */ @@ -1384,7 +1425,7 @@ _render_glyph_outline (FT_Face face, FT_BBox cbox; unsigned int width, height; cairo_status_t status; - FT_Error fterror; + FT_Error error; FT_Library library = glyphslot->library; FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; @@ -1394,6 +1435,7 @@ _render_glyph_outline (FT_Face face, break; case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: switch (font_options->subpixel_order) { case CAIRO_SUBPIXEL_ORDER_DEFAULT: case CAIRO_SUBPIXEL_ORDER_RGB: @@ -1427,6 +1469,8 @@ _render_glyph_outline (FT_Face face, case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: render_mode = FT_RENDER_MODE_NORMAL; } @@ -1461,69 +1505,54 @@ _render_glyph_outline (FT_Face face, (*surface) = (cairo_image_surface_t *) cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); + pixman_image_set_component_alpha ((*surface)->pixman_image, TRUE); if ((*surface)->base.status) return (*surface)->base.status; } else { int bitmap_size; - static int initialized_setLcdFilter = 0; switch (render_mode) { case FT_RENDER_MODE_LCD: - if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) { + if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) rgba = FC_RGBA_BGR; - } else { + else rgba = FC_RGBA_RGB; - } - break; - case FT_RENDER_MODE_LCD_V: - if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) { - rgba = FC_RGBA_VBGR; - } else { - rgba = FC_RGBA_VRGB; - } break; + + case FT_RENDER_MODE_LCD_V: + if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR) + rgba = FC_RGBA_VBGR; + else + rgba = FC_RGBA_VRGB; + break; + case FT_RENDER_MODE_MONO: case FT_RENDER_MODE_LIGHT: case FT_RENDER_MODE_NORMAL: case FT_RENDER_MODE_MAX: default: break; - } + } - if (!initialized_setLcdFilter) { - initialized_setLcdFilter = 1; -#ifdef HAVE_FT_LIBRARY_SETLCDFILTER - setLcdFilter = &FT_Library_SetLcdFilter; -#else - setLcdFilter = (setLcdFilterFunc) dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter"); +#if HAVE_FT_LIBRARY_SETLCDFILTER + FT_Library_SetLcdFilter (library, lcd_filter); #endif - } - if (setLcdFilter && - (render_mode == FT_RENDER_MODE_LCD || - render_mode == FT_RENDER_MODE_LCD_V)) { - mozilla_LockFTLibrary (library); - setLcdFilter (library, lcd_filter); - } + error = FT_Render_Glyph (face->glyph, render_mode); - fterror = FT_Render_Glyph (face->glyph, render_mode); +#if HAVE_FT_LIBRARY_SETLCDFILTER + FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE); +#endif - if (setLcdFilter && - (render_mode == FT_RENDER_MODE_LCD || - render_mode == FT_RENDER_MODE_LCD_V)) { - setLcdFilter (library, FT_LCD_FILTER_NONE); - mozilla_UnlockFTLibrary (library); - } - - if (fterror != 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (error) + return _cairo_error (_ft_to_cairo_error (error)); bitmap_size = _compute_xrender_bitmap_size (&bitmap, face->glyph, render_mode); if (bitmap_size < 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); bitmap.buffer = calloc (1, bitmap_size); if (bitmap.buffer == NULL) @@ -1668,7 +1697,7 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, if (unlikely (status)) return status; - if (cairo_image_surface_get_format (*surface) == CAIRO_FORMAT_ARGB32 && + if ((*surface)->format == CAIRO_FORMAT_ARGB32 && !pixman_image_get_component_alpha ((*surface)->pixman_image)) image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); else @@ -1701,6 +1730,10 @@ _transform_glyph_bitmap (cairo_matrix_t * shape, old_image = (*surface); (*surface) = (cairo_image_surface_t *)image; + + /* Note: we converted subpixel-rendered RGBA images to grayscale, + * so, no need to copy component alpha to new image. */ + cairo_surface_destroy (&old_image->base); cairo_surface_set_device_offset (&(*surface)->base, @@ -1739,6 +1772,7 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) #ifdef FC_HINT_STYLE int hintstyle; #endif + char *variations; _cairo_font_options_init_default (&ft_options.base); ft_options.load_flags = FT_LOAD_DEFAULT; @@ -1880,6 +1914,13 @@ _get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) if (embolden) ft_options.synth_flags |= CAIRO_FT_SYNTHESIZE_BOLD; +#ifndef FC_FONT_VARIATIONS +#define FC_FONT_VARIATIONS "fontvariations" +#endif + if (FcPatternGetString (pattern, FC_FONT_VARIATIONS, 0, (FcChar8 **) &variations) == FcResultMatch) { + ft_options.base.variations = strdup (variations); + } + *ret = ft_options; } #endif @@ -1901,16 +1942,13 @@ _cairo_ft_options_merge (cairo_ft_options_t *options, options->base.antialias == CAIRO_ANTIALIAS_NONE) { options->base.antialias = CAIRO_ANTIALIAS_NONE; options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; - } else if (options->base.antialias != CAIRO_ANTIALIAS_GRAY) { - /* The surface supports subpixel aa, so let the font face options - * choose whether to use subpixel aa. If the surface has - * CAIRO_ANTIALIAS_GRAY (e.g. PS, PDF, SVG, translucent part of a - * CONTENT_COLOR_ALPHA surface), then don't accept subpixel aa. */ - if (other->base.antialias != CAIRO_ANTIALIAS_DEFAULT) - options->base.antialias = other->base.antialias; - /* If the surface knows the subpixel order then use that. */ - if (options->base.subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) - options->base.subpixel_order = other->base.subpixel_order; + } + + if (other->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL && + (options->base.antialias == CAIRO_ANTIALIAS_DEFAULT || + options->base.antialias == CAIRO_ANTIALIAS_GRAY)) { + options->base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; + options->base.subpixel_order = other->base.subpixel_order; } if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT) @@ -1960,6 +1998,24 @@ _cairo_ft_options_merge (cairo_ft_options_t *options, } } + if (other->base.variations) { + if (options->base.variations) { + char *p; + + /* 'merge' variations by concatenating - later entries win */ + p = malloc (strlen (other->base.variations) + strlen (options->base.variations) + 2); + p[0] = 0; + strcat (p, other->base.variations); + strcat (p, ","); + strcat (p, options->base.variations); + free (options->base.variations); + options->base.variations = p; + } + else { + options->base.variations = strdup (other->base.variations); + } + } + options->load_flags = load_flags | load_target; options->synth_flags = other->synth_flags; } @@ -1985,7 +2041,7 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_font_face, if (unlikely (face == NULL)) /* backend error */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); - scaled_font = malloc (sizeof (cairo_ft_scaled_font_t)); + scaled_font = _cairo_malloc (sizeof (cairo_ft_scaled_font_t)); if (unlikely (scaled_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; @@ -2262,6 +2318,163 @@ _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font, } } +static void +cairo_ft_apply_variations (FT_Face face, + cairo_ft_scaled_font_t *scaled_font) +{ + FT_MM_Var *ft_mm_var; + FT_Error ret; + unsigned int instance_id = scaled_font->unscaled->id >> 16; + + ret = FT_Get_MM_Var (face, &ft_mm_var); + if (ret == 0) { + FT_Fixed *current_coords; + FT_Fixed *coords; + unsigned int i; + const char *p; + + coords = malloc (sizeof (FT_Fixed) * ft_mm_var->num_axis); + /* FIXME check coords. */ + + if (scaled_font->unscaled->variations) + { + memcpy (coords, scaled_font->unscaled->variations, ft_mm_var->num_axis * sizeof (*coords)); + } + else if (instance_id && instance_id <= ft_mm_var->num_namedstyles) + { + FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1]; + memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords)); + } + else + for (i = 0; i < ft_mm_var->num_axis; i++) + coords[i] = ft_mm_var->axis[i].def; + + p = scaled_font->ft_options.base.variations; + while (p && *p) { + const char *start; + const char *end, *end2; + FT_ULong tag; + double value; + + while (_cairo_isspace (*p)) p++; + + start = p; + end = strchr (p, ','); + if (end && (end - p < 6)) + goto skip; + + tag = FT_MAKE_TAG(p[0], p[1], p[2], p[3]); + + p += 4; + while (_cairo_isspace (*p)) p++; + if (*p == '=') p++; + + if (p - start < 5) + goto skip; + + value = _cairo_strtod (p, (char **) &end2); + + while (end2 && _cairo_isspace (*end2)) end2++; + + if (end2 && (*end2 != ',' && *end2 != '\0')) + goto skip; + + for (i = 0; i < ft_mm_var->num_axis; i++) { + if (ft_mm_var->axis[i].tag == tag) { + coords[i] = (FT_Fixed)(value*65536); + break; + } + } + +skip: + p = end ? end + 1 : NULL; + } + + current_coords = malloc (sizeof (FT_Fixed) * ft_mm_var->num_axis); +#ifdef HAVE_FT_GET_VAR_DESIGN_COORDINATES + ret = FT_Get_Var_Design_Coordinates (face, ft_mm_var->num_axis, current_coords); + if (ret == 0) { + for (i = 0; i < ft_mm_var->num_axis; i++) { + if (coords[i] != current_coords[i]) + break; + } + if (i == ft_mm_var->num_axis) + goto done; + } +#endif + + FT_Set_Var_Design_Coordinates (face, ft_mm_var->num_axis, coords); +done: + free (coords); + free (current_coords); +#if HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (face->glyph->library, ft_mm_var); +#else + free (ft_mm_var); +#endif + } +} + +static cairo_int_status_t +_cairo_ft_scaled_glyph_load_glyph (cairo_ft_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + FT_Face face, + int load_flags, + cairo_bool_t use_em_size, + cairo_bool_t vertical_layout) +{ + FT_Error error; + cairo_status_t status; + + if (use_em_size) { + cairo_matrix_t em_size; + cairo_matrix_init_scale (&em_size, face->units_per_EM, face->units_per_EM); + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, &em_size); + } else { + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, + &scaled_font->base.scale); + } + if (unlikely (status)) + return status; + + cairo_ft_apply_variations (face, scaled_font); + + error = FT_Load_Glyph (face, + _cairo_scaled_glyph_index(scaled_glyph), + load_flags); + /* XXX ignoring all other errors for now. They are not fatal, typically + * just a glyph-not-found. */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* + * synthesize glyphs if requested + */ +#if HAVE_FT_GLYPHSLOT_EMBOLDEN + if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) + FT_GlyphSlot_Embolden (face->glyph); +#endif + +#if HAVE_FT_GLYPHSLOT_OBLIQUE + if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE) + FT_GlyphSlot_Oblique (face->glyph); +#endif + + if (vertical_layout) + _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, face->glyph); + + if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Pos xshift, yshift; + + xshift = _cairo_scaled_glyph_xphase (scaled_glyph) << 4; + yshift = _cairo_scaled_glyph_yphase (scaled_glyph) << 4; + + FT_Outline_Translate (&face->glyph->outline, xshift, -yshift); + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_ft_scaled_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, @@ -2271,29 +2484,26 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, cairo_ft_scaled_font_t *scaled_font = abstract_font; cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; FT_GlyphSlot glyph; - FT_Face face = NULL; - FT_Error error; + 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_t status = CAIRO_STATUS_SUCCESS; + cairo_bool_t scaled_glyph_loaded = FALSE; face = _cairo_ft_unscaled_font_lock_face (unscaled); if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, - &scaled_font->base.scale); - if (unlikely (status)) - goto FAIL; - /* Ignore global advance unconditionally */ load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 && - (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0) + (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) == 0) { load_flags |= FT_LOAD_NO_BITMAP; + } /* * Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as @@ -2305,43 +2515,26 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, } #ifdef FT_LOAD_COLOR - /* Color-glyph support: - * - * This flags needs plumbing through fontconfig (does it?), and - * maybe we should cache color and grayscale bitmaps separately - * such that users of the font (ie. the surface) can choose which - * version to use based on target content type. - */ - load_flags |= FT_LOAD_COLOR; #endif - error = mozilla_LoadFTGlyph (scaled_font->unscaled->face, - _cairo_scaled_glyph_index(scaled_glyph), - load_flags); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - glyph = face->glyph; - -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - /* - * embolden glyphs if requested - */ - if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) - FT_GlyphSlot_Embolden (glyph); -#endif - - if (vertical_layout) - _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) { cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF; + + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags, + !hint_metrics, + vertical_layout); + if (unlikely (status)) + goto FAIL; + + glyph = face->glyph; + scaled_glyph_loaded = hint_metrics; + /* * Compute font-space metrics */ @@ -2436,9 +2629,24 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, &fs_metrics); } - if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) { +LOAD: + if (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) { cairo_image_surface_t *surface; + 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; + scaled_glyph_loaded = TRUE; + } + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { status = _render_glyph_outline (face, &scaled_font->ft_options.base, &surface); @@ -2450,16 +2658,37 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, { status = _transform_glyph_bitmap (&unscaled->current_shape, &surface); - if (unlikely (status)) - cairo_surface_destroy (&surface->base); - } + if (unlikely (status)) + cairo_surface_destroy (&surface->base); + } } if (unlikely (status)) goto FAIL; - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - surface); + if (pixman_image_get_format (surface->pixman_image) == PIXMAN_a8r8g8b8 && + !pixman_image_get_component_alpha (surface->pixman_image)) { + _cairo_scaled_glyph_set_color_surface (scaled_glyph, + &scaled_font->base, + surface); + } else { + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + surface); + } + } + + if (((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) && + ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0)) { + /* + * A kludge -- load again, without color. + * No need to load the metrics again, though + */ + scaled_glyph_loaded = FALSE; + info &= ~CAIRO_SCALED_GLYPH_INFO_METRICS; +#ifdef FT_LOAD_COLOR + load_flags &= ~FT_LOAD_COLOR; +#endif + goto LOAD; } if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { @@ -2469,27 +2698,24 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, * 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) != 0) { - error = mozilla_LoadFTGlyph (face, - _cairo_scaled_glyph_index(scaled_glyph), - load_flags | FT_LOAD_NO_BITMAP); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } -#if HAVE_FT_GLYPHSLOT_EMBOLDEN - /* - * embolden glyphs if requested - */ - if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD) - FT_GlyphSlot_Embolden (glyph); -#endif - if (vertical_layout) - _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph); - + 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; + } + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, &path); @@ -2544,6 +2770,11 @@ _cairo_ft_load_truetype_table (void *abstract_font, FT_Face face; cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + /* We don't support the FreeType feature of loading a table + * without specifying the size since this may overflow our + * buffer. */ + assert (length != NULL); + if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2552,9 +2783,13 @@ _cairo_ft_load_truetype_table (void *abstract_font, if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - if (FT_IS_SFNT (face) && - FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0) - status = CAIRO_STATUS_SUCCESS; + if (FT_IS_SFNT (face)) { + if (buffer == NULL) + *length = 0; + + if (FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0) + status = CAIRO_STATUS_SUCCESS; + } _cairo_ft_unscaled_font_unlock_face (unscaled); #endif @@ -2592,41 +2827,275 @@ _cairo_ft_index_to_ucs4(void *abstract_font, return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_ft_is_synthetic (void *abstract_font, + cairo_bool_t *is_synthetic) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + FT_Error error; + + if (scaled_font->ft_options.synth_flags != 0) { + *is_synthetic = TRUE; + return status; + } + + *is_synthetic = FALSE; + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *mm_var = NULL; + FT_Fixed *coords = NULL; + int num_axis; + + /* If this is an MM or variable font we can't assume the current outlines + * are the same as the font tables */ + *is_synthetic = TRUE; + + error = FT_Get_MM_Var (face, &mm_var); + if (error) { + status = _cairo_error (_ft_to_cairo_error (error)); + goto cleanup; + } + + num_axis = mm_var->num_axis; + coords = _cairo_malloc_ab (num_axis, sizeof(FT_Fixed)); + if (!coords) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } + +#if FREETYPE_MAJOR > 2 || ( FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 8) + /* If FT_Get_Var_Blend_Coordinates() is available, we can check if the + * current design coordinates are the default coordinates. In this case + * the current outlines match the font tables. + */ + { + int i; + + FT_Get_Var_Blend_Coordinates (face, num_axis, coords); + *is_synthetic = FALSE; + for (i = 0; i < num_axis; i++) { + if (coords[i]) { + *is_synthetic = TRUE; + break; + } + } + } +#endif + + cleanup: + free (coords); +#if HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (face->glyph->library, mm_var); +#else + free (mm_var); +#endif + } + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + return status; +} + +static cairo_int_status_t +_cairo_index_to_glyph_name (void *abstract_font, + char **glyph_names, + int num_glyph_names, + unsigned long glyph_index, + unsigned long *glyph_array_index) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + char buffer[256]; /* PLRM specifies max name length of 127 */ + FT_Error error; + int i; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + error = FT_Get_Glyph_Name (face, glyph_index, buffer, sizeof buffer); + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + if (error != FT_Err_Ok) { + /* propagate fatal errors from FreeType */ + if (error == FT_Err_Out_Of_Memory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* FT first numbers the glyphs in the order they are read from the + * Type 1 font. Then if .notdef is not the first glyph, the first + * glyph is swapped with .notdef to ensure that .notdef is at + * glyph index 0. + * + * As all but two glyphs in glyph_names already have the same + * index as the FT glyph index, we first check if + * glyph_names[glyph_index] is the name we are looking for. If not + * we fall back to searching the entire array. + */ + + if ((long)glyph_index < num_glyph_names && + strcmp (glyph_names[glyph_index], buffer) == 0) + { + *glyph_array_index = glyph_index; + + return CAIRO_STATUS_SUCCESS; + } + + for (i = 0; i < num_glyph_names; i++) { + if (strcmp (glyph_names[i], buffer) == 0) { + *glyph_array_index = i; + + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_bool_t +_ft_is_type1 (FT_Face face) +{ +#if HAVE_FT_GET_X11_FONT_FORMAT + const char *font_format = FT_Get_X11_Font_Format (face); + if (font_format && + (strcmp (font_format, "Type 1") == 0 || + strcmp (font_format, "CFF") == 0)) + { + return TRUE; + } +#endif + + return FALSE; +} + +static cairo_int_status_t +_cairo_ft_load_type1_data (void *abstract_font, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + unsigned long available_length; + unsigned long ret; + + assert (length != NULL); + + if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + +#if HAVE_FT_LOAD_SFNT_TABLE + if (FT_IS_SFNT (face)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto unlock; + } +#endif + + if (! _ft_is_type1 (face)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto unlock; + } + + available_length = MAX (face->stream->size - offset, 0); + if (!buffer) { + *length = available_length; + } else { + if (*length > available_length) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + } else if (face->stream->read != NULL) { + /* Note that read() may be implemented as a macro, thanks POSIX!, so we + * need to wrap the following usage in parentheses in order to + * disambiguate it for the pre-processor - using the verbose function + * pointer dereference for clarity. + */ + ret = (* face->stream->read) (face->stream, + offset, + buffer, + *length); + if (ret != *length) + status = _cairo_error (CAIRO_STATUS_READ_ERROR); + } else { + memcpy (buffer, face->stream->base + offset, *length); + } + } + + unlock: + _cairo_ft_unscaled_font_unlock_face (unscaled); + + return status; +} + +static cairo_bool_t +_cairo_ft_has_color_glyphs (void *scaled) +{ + cairo_ft_unscaled_font_t *unscaled = ((cairo_ft_scaled_font_t *)scaled)->unscaled; + + if (!unscaled->have_color_set) { + FT_Face face; + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (unlikely (face == NULL)) + return FALSE; + _cairo_ft_unscaled_font_unlock_face (unscaled); + } + + return unscaled->have_color; +} + static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { CAIRO_FONT_TYPE_FT, _cairo_ft_scaled_font_fini, _cairo_ft_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_ft_ucs4_to_index, - NULL, /* show_glyphs */ _cairo_ft_load_truetype_table, - _cairo_ft_index_to_ucs4 + _cairo_ft_index_to_ucs4, + _cairo_ft_is_synthetic, + _cairo_index_to_glyph_name, + _cairo_ft_load_type1_data, + _cairo_ft_has_color_glyphs }; /* #cairo_ft_font_face_t */ #if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, - cairo_font_face_t **out); +static cairo_font_face_t * +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern); static cairo_status_t -_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, - cairo_font_face_t **font_face) +_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face_out) { + cairo_font_face_t *font_face = (cairo_font_face_t *) &_cairo_font_face_nil; FcPattern *pattern; int fcslant; int fcweight; - cairo_status_t status = CAIRO_STATUS_SUCCESS; pattern = FcPatternCreate (); - if (!pattern) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (!pattern) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return font_face->status; + } if (!FcPatternAddString (pattern, FC_FAMILY, (unsigned char *) toy_face->family)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FREE_PATTERN; } @@ -2645,7 +3114,7 @@ _cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, } if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FREE_PATTERN; } @@ -2661,20 +3130,21 @@ _cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, } if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FREE_PATTERN; } - status = _cairo_ft_font_face_create_for_pattern (pattern, font_face); + font_face = _cairo_ft_font_face_create_for_pattern (pattern); FREE_PATTERN: FcPatternDestroy (pattern); - return status; + *font_face_out = font_face; + return font_face->status; } #endif -static void +static cairo_bool_t _cairo_ft_font_face_destroy (void *abstract_face) { cairo_ft_font_face_t *font_face = abstract_face; @@ -2694,23 +3164,19 @@ _cairo_ft_font_face_destroy (void *abstract_face) * font_face <------- unscaled */ - if (font_face->unscaled) + if (font_face->unscaled && + font_face->unscaled->from_face && + font_face->next == NULL && + font_face->unscaled->faces == font_face && + CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1) { - CAIRO_FT_LOCK (font_face->unscaled); + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; - if (font_face->unscaled->from_face && - font_face->next == NULL && - font_face->unscaled->faces == font_face && - CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1) - { - cairo_font_face_reference (&font_face->base); - - CAIRO_FT_UNLOCK (font_face->unscaled); - _cairo_unscaled_font_destroy (&font_face->unscaled->base); - font_face->unscaled = NULL; - return; - } + return FALSE; + } + if (font_face->unscaled) { cairo_ft_font_face_t *tmp_face = NULL; cairo_ft_font_face_t *last_face = NULL; @@ -2729,20 +3195,20 @@ _cairo_ft_font_face_destroy (void *abstract_face) last_face = tmp_face; } - // Ensure user-data destruction happens within the unscaled font mutex. - _cairo_user_data_array_fini (&font_face->base.user_data); - - CAIRO_FT_UNLOCK (font_face->unscaled); _cairo_unscaled_font_destroy (&font_face->unscaled->base); font_face->unscaled = NULL; } + _cairo_ft_options_fini (&font_face->ft_options); + #if CAIRO_HAS_FC_FONT if (font_face->pattern) { FcPatternDestroy (font_face->pattern); cairo_font_face_destroy (font_face->resolved_font_face); } #endif + + return TRUE; } static cairo_font_face_t * @@ -2802,24 +3268,6 @@ _cairo_ft_font_face_get_implementation (void *abstract_face, return abstract_face; } -static void -_cairo_ft_font_face_lock (void *abstract_face) -{ - cairo_ft_font_face_t *font_face = abstract_face; - if (font_face->unscaled) { - CAIRO_FT_LOCK (font_face->unscaled); - } -} - -static void -_cairo_ft_font_face_unlock (void *abstract_face) -{ - cairo_ft_font_face_t *font_face = abstract_face; - if (font_face->unscaled) { - CAIRO_FT_UNLOCK (font_face->unscaled); - } -} - const cairo_font_face_backend_t _cairo_ft_font_face_backend = { CAIRO_FONT_TYPE_FT, #if CAIRO_HAS_FC_FONT @@ -2829,29 +3277,32 @@ const cairo_font_face_backend_t _cairo_ft_font_face_backend = { #endif _cairo_ft_font_face_destroy, _cairo_ft_font_face_scaled_font_create, - _cairo_ft_font_face_get_implementation, - _cairo_ft_font_face_lock, - _cairo_ft_font_face_unlock + _cairo_ft_font_face_get_implementation }; #if CAIRO_HAS_FC_FONT -static cairo_status_t -_cairo_ft_font_face_create_for_pattern (FcPattern *pattern, - cairo_font_face_t **out) +static cairo_font_face_t * +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern) { cairo_ft_font_face_t *font_face; - font_face = malloc (sizeof (cairo_ft_font_face_t)); - if (unlikely (font_face == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + font_face = _cairo_malloc (sizeof (cairo_ft_font_face_t)); + if (unlikely (font_face == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *) &_cairo_font_face_nil; + } font_face->unscaled = NULL; + + _get_pattern_ft_options (pattern, &font_face->ft_options); + font_face->next = NULL; font_face->pattern = FcPatternDuplicate (pattern); if (unlikely (font_face->pattern == NULL)) { free (font_face); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *) &_cairo_font_face_nil; } font_face->resolved_font_face = NULL; @@ -2859,8 +3310,7 @@ _cairo_ft_font_face_create_for_pattern (FcPattern *pattern, _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); - *out = &font_face->base; - return CAIRO_STATUS_SUCCESS; + return &font_face->base; } #endif @@ -2870,8 +3320,6 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, { cairo_ft_font_face_t *font_face, **prev_font_face; - CAIRO_FT_LOCK (unscaled); - /* Looked for an existing matching font face */ for (font_face = unscaled->faces, prev_font_face = &unscaled->faces; font_face; @@ -2893,19 +3341,15 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, * from owner to ownee. */ font_face->unscaled = unscaled; _cairo_unscaled_font_reference (&unscaled->base); - } else { - cairo_font_face_reference (&font_face->base); - } - - CAIRO_FT_UNLOCK (unscaled); - return &font_face->base; + return &font_face->base; + } else + return cairo_font_face_reference (&font_face->base); } } /* No match found, create a new one */ - font_face = malloc (sizeof (cairo_ft_font_face_t)); + font_face = _cairo_malloc (sizeof (cairo_ft_font_face_t)); if (unlikely (!font_face)) { - CAIRO_FT_UNLOCK (unscaled); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; } @@ -2913,13 +3357,12 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, font_face->unscaled = unscaled; _cairo_unscaled_font_reference (&unscaled->base); - font_face->ft_options = *ft_options; + _cairo_ft_options_init_copy (&font_face->ft_options, ft_options); if (unscaled->faces && unscaled->faces->unscaled == NULL) { /* This "zombie" font_face (from _cairo_ft_font_face_destroy) * is no longer needed. */ assert (unscaled->from_face && unscaled->faces->next == NULL); - // The unscaled font mutex is held, so destroying user data is safe inside here. cairo_font_face_destroy (&unscaled->faces->base); unscaled->faces = NULL; } @@ -2933,7 +3376,6 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); - CAIRO_FT_UNLOCK (unscaled); return &font_face->base; } @@ -3074,6 +3516,8 @@ _cairo_ft_font_options_substitute (const cairo_font_options_t *options, * so you should call this function after calling FcConfigSubstitute() (the * user's settings should override options based on the surface type), but * before calling FcDefaultSubstitute(). + * + * Since: 1.0 **/ void cairo_ft_font_options_substitute (const cairo_font_options_t *options, @@ -3133,28 +3577,39 @@ _cairo_ft_resolve_pattern (FcPattern *pattern, FcDefaultSubstitute (pattern); - resolved = FcFontMatch (NULL, pattern, &result); - if (!resolved) { - /* We failed to find any font. Substitute twin so that the user can - * see something (and hopefully recognise that the font is missing) - * and not just receive a NO_MEMORY error during rendering. - */ - font_face = _cairo_font_face_twin_create_fallback (); + status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); + if (unlikely (status)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; goto FREE_PATTERN; } - status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled); - if (unlikely (status || unscaled == NULL)) { - font_face = (cairo_font_face_t *)&_cairo_font_face_nil; - goto FREE_RESOLVED; - } + if (unscaled == NULL) { + resolved = FcFontMatch (NULL, pattern, &result); + if (!resolved) { + /* We failed to find any font. Substitute twin so that the user can + * see something (and hopefully recognise that the font is missing) + * and not just receive a NO_MEMORY error during rendering. + */ + font_face = _cairo_font_face_twin_create_fallback (); + goto FREE_PATTERN; + } + + status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled); + if (unlikely (status || unscaled == NULL)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_RESOLVED; + } + } else + resolved = pattern; _get_pattern_ft_options (resolved, &ft_options); font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_ft_options_fini (&ft_options); _cairo_unscaled_font_destroy (&unscaled->base); FREE_RESOLVED: - FcPatternDestroy (resolved); + if (resolved != pattern) + FcPatternDestroy (resolved); FREE_PATTERN: FcPatternDestroy (pattern); @@ -3193,11 +3648,13 @@ FREE_PATTERN: * If the FC_FT_FACE element of @pattern is set, the user is responsible * for making sure that the referenced FT_Face remains valid for the life * time of the returned #cairo_font_face_t. See - * cairo_ft_font_face_create_for_ft_face() for an exmaple of how to couple + * cairo_ft_font_face_create_for_ft_face() for an example of how to couple * the life time of the FT_Face to that of the cairo font-face. * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.0 **/ cairo_font_face_t * cairo_ft_font_face_create_for_pattern (FcPattern *pattern) @@ -3207,23 +3664,22 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern) cairo_ft_options_t ft_options; cairo_status_t status; - status = _cairo_ft_unscaled_font_create_for_pattern (pattern, - &unscaled); - if (unlikely (status)) + status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); + if (unlikely (status)) { + if (status == CAIRO_STATUS_FILE_NOT_FOUND) + return (cairo_font_face_t *) &_cairo_font_face_nil_file_not_found; + else return (cairo_font_face_t *) &_cairo_font_face_nil; + } if (unlikely (unscaled == NULL)) { /* Store the pattern. We will resolve it and create unscaled * font when creating scaled fonts */ - status = _cairo_ft_font_face_create_for_pattern (pattern, - &font_face); - if (unlikely (status)) - return (cairo_font_face_t *) &_cairo_font_face_nil; - - return font_face; + return _cairo_ft_font_face_create_for_pattern (pattern); } _get_pattern_ft_options (pattern, &ft_options); font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_ft_options_fini (&ft_options); _cairo_unscaled_font_destroy (&unscaled->base); return font_face; @@ -3275,25 +3731,24 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern) * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.0 **/ cairo_font_face_t * cairo_ft_font_face_create_for_ft_face (FT_Face face, - int load_flags, - unsigned int synth_flags, - void *face_context) + int load_flags) { cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; cairo_status_t status; - status = _cairo_ft_unscaled_font_create_from_face (face, face_context, - &unscaled); + status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled); if (unlikely (status)) return (cairo_font_face_t *)&_cairo_font_face_nil; ft_options.load_flags = load_flags; - ft_options.synth_flags = synth_flags; + ft_options.synth_flags = 0; _cairo_font_options_init_default (&ft_options.base); font_face = _cairo_ft_font_face_create (unscaled, &ft_options); @@ -3302,6 +3757,74 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, return font_face; } +/** + * cairo_ft_font_face_set_synthesize: + * @font_face: The #cairo_ft_font_face_t object to modify + * @synth_flags: the set of synthesis options to enable + * + * FreeType provides the ability to synthesize different glyphs from a base + * font, which is useful if you lack those glyphs from a true bold or oblique + * font. See also #cairo_ft_synthesize_t. + * + * Since: 1.12 + **/ +void +cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face, + unsigned int synth_flags) +{ + cairo_ft_font_face_t *ft; + + if (font_face->backend->type != CAIRO_FONT_TYPE_FT) + return; + + ft = (cairo_ft_font_face_t *) font_face; + ft->ft_options.synth_flags |= synth_flags; +} + +/** + * cairo_ft_font_face_unset_synthesize: + * @font_face: The #cairo_ft_font_face_t object to modify + * @synth_flags: the set of synthesis options to disable + * + * See cairo_ft_font_face_set_synthesize(). + * + * Since: 1.12 + **/ +void +cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face, + unsigned int synth_flags) +{ + cairo_ft_font_face_t *ft; + + if (font_face->backend->type != CAIRO_FONT_TYPE_FT) + return; + + ft = (cairo_ft_font_face_t *) font_face; + ft->ft_options.synth_flags &= ~synth_flags; +} + +/** + * cairo_ft_font_face_get_synthesize: + * @font_face: The #cairo_ft_font_face_t object to query + * + * See #cairo_ft_synthesize_t. + * + * Returns: the current set of synthesis options. + * + * Since: 1.12 + **/ +unsigned int +cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face) +{ + cairo_ft_font_face_t *ft; + + if (font_face->backend->type != CAIRO_FONT_TYPE_FT) + return 0; + + ft = (cairo_ft_font_face_t *) font_face; + return ft->ft_options.synth_flags; +} + /** * cairo_ft_scaled_font_lock_face: * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an @@ -3310,7 +3833,8 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, * cairo_ft_font_face_create_for_ft_face()). * * cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType - * backend font and scales it appropriately for the font. You must + * backend font and scales it appropriately for the font and applies OpenType + * font variations if applicable. You must * release the face with cairo_ft_scaled_font_unlock_face() * when you are done using it. Since the #FT_Face object can be * shared between multiple #cairo_scaled_font_t objects, you must not @@ -3331,6 +3855,8 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, * Return value: The #FT_Face object for @font, scaled appropriately, * or %NULL if @scaled_font is in an error state (see * cairo_scaled_font_status()) or there is insufficient memory. + * + * Since: 1.0 **/ FT_Face cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) @@ -3361,13 +3887,15 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) return NULL; } + cairo_ft_apply_variations (face, scaled_font); + /* Note: We deliberately release the unscaled font's mutex here, * so that we are not holding a lock across two separate calls to * cairo function, (which would give the application some * opportunity for creating deadlock. This is obviously unsafe, * but as documented, the user must add manual locking when using * this function. */ - CAIRO_FT_UNLOCK(scaled_font->unscaled); + CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex); return face; } @@ -3380,6 +3908,8 @@ cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font) * cairo_ft_font_face_create_for_ft_face()). * * Releases a face obtained with cairo_ft_scaled_font_lock_face(). + * + * Since: 1.0 **/ void cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) @@ -3398,24 +3928,12 @@ cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font) * cairo_ft_scaled_font_lock_face, so we have to acquire it again * as _cairo_ft_unscaled_font_unlock_face expects it to be held * when we call into it. */ - CAIRO_FT_LOCK(scaled_font->unscaled); + CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex); _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); } -/* We expose our unscaled font implementation internally for the the - * PDF backend, which needs to keep track of the the different - * fonts-on-disk used by a document, so it can embed them. - */ -cairo_unscaled_font_t * -_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *abstract_font) -{ - cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; - - return &scaled_font->unscaled->base; -} - -cairo_bool_t +static cairo_bool_t _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) { cairo_ft_scaled_font_t *ft_scaled_font; diff --git a/gfx/cairo/cairo/src/cairo-ft-private.h b/gfx/cairo/cairo/src/cairo-ft-private.h index ff6ad4e655fa..0dc8114726f5 100644 --- a/gfx/cairo/cairo/src/cairo-ft-private.h +++ b/gfx/cairo/cairo/src/cairo-ft-private.h @@ -37,8 +37,8 @@ #ifndef CAIRO_FT_PRIVATE_H #define CAIRO_FT_PRIVATE_H -#include "cairo-ft.h" #include "cairoint.h" +#include "cairo-ft.h" #if CAIRO_HAS_FT_FONT @@ -52,18 +52,6 @@ _cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font); /* These functions are needed by the PDF backend, which needs to keep track of the * the different fonts-on-disk used by a document, so it can embed them */ -cairo_private cairo_unscaled_font_t * -_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *scaled_font); - -cairo_private FT_Face -_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled); - -cairo_private void -_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); - -cairo_private cairo_bool_t -_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); - cairo_private unsigned int _cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); diff --git a/gfx/cairo/cairo/src/cairo-ft.h b/gfx/cairo/cairo/src/cairo-ft.h index c8897eb4e51c..29c43c9655bc 100644 --- a/gfx/cairo/cairo/src/cairo-ft.h +++ b/gfx/cairo/cairo/src/cairo-ft.h @@ -52,16 +52,45 @@ CAIRO_BEGIN_DECLS +cairo_public cairo_font_face_t * +cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags); + +/** + * cairo_ft_synthesize_t: + * @CAIRO_FT_SYNTHESIZE_BOLD: Embolden the glyphs (redraw with a pixel offset) + * @CAIRO_FT_SYNTHESIZE_OBLIQUE: Slant the glyph outline by 12 degrees to the + * right. + * + * A set of synthesis options to control how FreeType renders the glyphs + * for a particular font face. + * + * Individual synthesis features of a #cairo_ft_font_face_t can be set + * using cairo_ft_font_face_set_synthesize(), or disabled using + * cairo_ft_font_face_unset_synthesize(). The currently enabled set of + * synthesis options can be queried with cairo_ft_font_face_get_synthesize(). + * + * Note: that when synthesizing glyphs, the font metrics returned will only + * be estimates. + * + * Since: 1.12 + **/ typedef enum { CAIRO_FT_SYNTHESIZE_BOLD = 1 << 0, CAIRO_FT_SYNTHESIZE_OBLIQUE = 1 << 1 } cairo_ft_synthesize_t; -cairo_public cairo_font_face_t * -cairo_ft_font_face_create_for_ft_face (FT_Face face, - int load_flags, - unsigned int synth_flags, - void *face_context); +cairo_public void +cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face, + unsigned int synth_flags); + +cairo_public void +cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face, + unsigned int synth_flags); + +cairo_public unsigned int +cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face); + cairo_public FT_Face cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font); @@ -72,7 +101,7 @@ cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); #if CAIRO_HAS_FC_FONT cairo_public cairo_font_face_t * -cairo_ft_font_face_create_for_pattern (FcPattern *pattern); +cairo_ft_font_face_create_for_pattern (FcPattern *pattern); cairo_public void cairo_ft_font_options_substitute (const cairo_font_options_t *options, diff --git a/gfx/cairo/cairo/src/cairo-gl-composite.c b/gfx/cairo/cairo/src/cairo-gl-composite.c new file mode 100644 index 000000000000..5477ef0d8ac4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-composite.c @@ -0,0 +1,1364 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + * Alexandros Frantzis + * Henry Song + * Martin Robinson + */ + +#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/gfx/cairo/cairo/src/cairo-gl-device.c b/gfx/cairo/cairo/src/cairo-gl-device.c new file mode 100644 index 000000000000..6f4c852a454b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-device.c @@ -0,0 +1,851 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + * Alexandros Frantzis + */ + +#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/gfx/cairo/cairo/src/cairo-gl-dispatch-private.h b/gfx/cairo/cairo/src/cairo-gl-dispatch-private.h new file mode 100644 index 000000000000..cabf76f0d6b5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-dispatch-private.h @@ -0,0 +1,129 @@ +/* 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 + */ + +#ifndef CAIRO_GL_DISPATCH_PRIVATE_H +#define CAIRO_GL_DISPATCH_PRIVATE_H + +#include "cairo-gl-private.h" +#include + +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/gfx/cairo/cairo/src/cairo-gl-dispatch.c b/gfx/cairo/cairo/src/cairo-gl-dispatch.c new file mode 100644 index 000000000000..a49199dbb0a9 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-dispatch.c @@ -0,0 +1,273 @@ +/* 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 + */ + +#include "cairoint.h" +#include "cairo-gl-private.h" +#include "cairo-gl-dispatch-private.h" +#if CAIRO_HAS_DLSYM +#include +#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/gfx/cairo/cairo/src/cairo-gl-ext-def-private.h b/gfx/cairo/cairo/src/cairo-gl-ext-def-private.h new file mode 100644 index 000000000000..a261947bef07 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-ext-def-private.h @@ -0,0 +1,143 @@ +/* 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 + */ + +#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/gfx/cairo/cairo/src/cairo-gl-glyphs.c b/gfx/cairo/cairo/src/cairo-gl-glyphs.c index 4736e190e0de..5923af44147e 100644 --- a/gfx/cairo/cairo/src/cairo-gl-glyphs.c +++ b/gfx/cairo/cairo/src/cairo-gl-glyphs.c @@ -40,7 +40,10 @@ #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 @@ -48,22 +51,61 @@ #define GLYPH_CACHE_MIN_SIZE 4 #define GLYPH_CACHE_MAX_SIZE 128 -typedef struct _cairo_gl_glyph_private { +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_private_t; +} cairo_gl_glyph_t; -static cairo_status_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_surface_t *cache_surface; - cairo_gl_glyph_private_t *glyph_private; + cairo_gl_glyph_t *glyph_private; cairo_rtree_node_t *node = NULL; - cairo_status_t status; + cairo_int_status_t status; int width, height; width = glyph_surface->width; @@ -78,32 +120,34 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, /* search for an unlocked slot */ if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_rtree_evict_random (&cache->rtree, - width, height, &node); - if (status == CAIRO_STATUS_SUCCESS) { + width, height, &node); + if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_rtree_node_insert (&cache->rtree, - node, width, height, &node); + node, width, height, &node); } } if (status) return status; - cache_surface = (cairo_gl_surface_t *) cache->pattern.surface; - /* 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); + 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; - scaled_glyph->surface_private = node; - node->owner = &scaled_glyph->surface_private; - - glyph_private = (cairo_gl_glyph_private_t *) node; + 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; @@ -111,177 +155,119 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, 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 /= cache_surface->width; - glyph_private->p1.y /= cache_surface->height; - glyph_private->p2.x /= cache_surface->width; - glyph_private->p2.y /= cache_surface->height; + 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_private_t * +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->surface_private); + 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_out) { cairo_gl_glyph_cache_t *cache; cairo_content_t content; switch (format) { + 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; + content = CAIRO_CONTENT_COLOR_ALPHA; break; case CAIRO_FORMAT_A8: case CAIRO_FORMAT_A1: cache = &ctx->glyph_cache[1]; - content = CAIRO_CONTENT_ALPHA; + content = CAIRO_CONTENT_ALPHA; break; + default: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); } - if (unlikely (cache->pattern.surface == NULL)) { - cairo_surface_t *surface; - surface = cairo_gl_surface_create (&ctx->base, - content, - GLYPH_CACHE_WIDTH, - GLYPH_CACHE_HEIGHT); - if (unlikely (surface->status)) { - cairo_status_t status = surface->status; - cairo_surface_destroy (surface); - return status; - } - _cairo_surface_release_device_reference (surface); - _cairo_pattern_init_for_surface (&cache->pattern, surface); - cairo_surface_destroy (surface); - cache->pattern.base.has_component_alpha = (content == CAIRO_CONTENT_COLOR_ALPHA); + 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 void -_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) -{ - _cairo_rtree_unpin (&cache->rtree); -} - -static cairo_bool_t -_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_device_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &_cairo_gl_surface_backend) || - (font_private != NULL && font_private != surface->base.device)) - { - return FALSE; - } - - return TRUE; -} - -void -_cairo_gl_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_list_del (&scaled_font->link); -} - -void -_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_gl_glyph_private_t *glyph_private; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private != NULL) { - glyph_private->node.owner = NULL; - if (! glyph_private->node.pinned) { - /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ - _cairo_rtree_node_remove (&glyph_private->cache->rtree, - &glyph_private->node); - } - } -} - static cairo_status_t -_render_glyphs (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_rectangle_int_t *glyph_extents, - cairo_scaled_font_t *scaled_font, - cairo_bool_t *has_component_alpha, - cairo_region_t *clip_region, - int *remaining_glyphs) +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_status_t status; + 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; - _cairo_scaled_font_freeze_cache (scaled_font); - - status = _cairo_gl_composite_init (&setup, op, dst, - TRUE, glyph_extents); - + status = _cairo_gl_composite_init (&setup, op, dst, TRUE); if (unlikely (status)) goto FINISH; - if (! _cairo_gl_surface_owns_font (dst, scaled_font)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - 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)); + } - status = _cairo_gl_composite_set_source (&setup, source, - glyph_extents->x, glyph_extents->y, - dst_x, dst_y, - glyph_extents->width, - glyph_extents->height); - if (unlikely (status)) - goto FINISH; + _cairo_gl_composite_set_clip (&setup, clip); - if (scaled_font->surface_private == NULL) { - scaled_font->surface_private = ctx; - scaled_font->surface_backend = &_cairo_gl_surface_backend; - cairo_list_add (&scaled_font->link, &ctx->fonts); - } - - _cairo_gl_composite_set_clip_region (&setup, clip_region); - - for (i = 0; i < num_glyphs; i++) { + for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; - cairo_gl_glyph_private_t *glyph; + cairo_gl_glyph_t *glyph; double x_offset, y_offset; double x1, x2, y1, y2; - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, + status = _cairo_scaled_glyph_lookup (info->font, + info->glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) @@ -292,133 +278,135 @@ _render_glyphs (cairo_gl_surface_t *dst, { continue; } - if (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || - scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FINISH; - } - 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; + &cache); + if (unlikely (status)) + goto FINISH; last_format = scaled_glyph->surface->format; - status = _cairo_gl_composite_set_mask (&setup, - &cache->pattern.base, - 0, 0, 0, 0, 0, 0); - if (unlikely (status)) - goto FINISH; + _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); + *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; - *has_component_alpha |= cache->pattern.base.has_component_alpha; - - /* XXX: _cairo_gl_composite_begin() acquires the context a - * second time. Need to refactor this loop so this doesn't happen. - */ - status = _cairo_gl_composite_begin (&setup, &ctx); - - status = _cairo_gl_context_release (ctx, status); + /* 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->surface_private == NULL) { - status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + if (scaled_glyph->dev_private_key != cache) { + cairo_scaled_glyph_private_t *priv; - 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); + 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 (unlikely (_cairo_status_is_error (status))) - goto FINISH; + 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 (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); + 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); - _cairo_gl_composite_emit_glyph (ctx, - x1, y1, x2, y2, - glyph->p1.x, glyph->p1.y, - glyph->p2.x, glyph->p2.y); + 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: - _cairo_scaled_font_thaw_cache (scaled_font); - status = _cairo_gl_context_release (ctx, status); _cairo_gl_composite_fini (&setup); - - *remaining_glyphs = num_glyphs - i; return status; } static cairo_int_status_t -_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_rectangle_int_t *glyph_extents, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) +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; - int i; + + 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, - glyph_extents->width, - glyph_extents->height); + CAIRO_CONTENT_COLOR_ALPHA, + info->extents.width, + info->extents.height); if (unlikely (mask->status)) - return mask->status; + return mask->status; - for (i = 0; i < num_glyphs; i++) { - glyphs[i].x -= glyph_extents->x; - glyphs[i].y -= glyph_extents->y; - } - - status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0, - CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - glyphs, num_glyphs, glyph_extents, - scaled_font, &has_component_alpha, - NULL, remaining_glyphs); + 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; + 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, - -glyph_extents->x, -glyph_extents->y); + 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, &mask_pattern.base, clip); + &source_pattern.base, + &mask_pattern.base, + clip); + + _cairo_clip_destroy (clip); + _cairo_pattern_fini (&mask_pattern.base); - } else { - for (i = 0; i < num_glyphs; i++) { - glyphs[i].x += glyph_extents->x; - glyphs[i].y += glyph_extents->y; - } - *remaining_glyphs = num_glyphs; + _cairo_pattern_fini (&source_pattern.base); } cairo_surface_destroy (mask); @@ -427,158 +415,72 @@ _cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, } 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, - cairo_clip_t *clip, - int *remaining_glyphs) +_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_gl_surface_t *dst = abstract_dst; - cairo_rectangle_int_t surface_extents; - cairo_rectangle_int_t extents; - cairo_region_t *clip_region = NULL; - cairo_bool_t overlap, use_mask = FALSE; - cairo_bool_t has_component_alpha; - cairo_status_t status; - int i; - - if (! _cairo_gl_operator_is_supported (op)) + if (! _cairo_gl_operator_is_supported (extents->op)) return UNSUPPORTED ("unsupported operator"); - if (! _cairo_operator_bounded_by_mask (op)) - use_mask |= TRUE; + /* XXX use individual masks for large glyphs? */ + if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE) + return UNSUPPORTED ("glyphs too large"); - /* If any of the glyphs are component alpha, we have to go through a mask, - * since only _cairo_gl_surface_composite() currently supports component - * alpha. + 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 (!use_mask && op != CAIRO_OPERATOR_OVER) { - for (i = 0; i < num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (!_cairo_status_is_error (status) && - scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32) - { - use_mask = TRUE; - break; - } - } + 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; } - /* For CLEAR, cairo's rendering equation (quoting Owen's description in: - * http://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 (op == CAIRO_OPERATOR_CLEAR) { - source = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; + 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); } - /* For SOURCE, cairo's rendering equation is: - * (mask IN clip) ? src OP dest : dest - * or more simply: - * (mask IN clip) ? src : dest. - * - * If we just used the Render equation, we would get: - * (src IN mask IN clip) OP dest - * or: - * (src IN mask IN clip) bounded by extents of the drawing. - * - * The trick is that for GL blending, we only get our 4 source values - * into the blender, and since we need all 4 components of source, we - * can't also get the mask IN clip into the blender. But if we did - * two passes we could make it work: - * dest = (mask IN clip) DEST_OUT dest - * dest = src IN mask IN clip ADD dest - * - * But for now, composite via an intermediate mask. - */ - if (op == CAIRO_OPERATOR_SOURCE) - use_mask |= TRUE; +} - /* XXX we don't need ownership of the font as we use a global - * glyph cache -- but we do need scaled_glyph eviction notification. :-( - */ - if (! _cairo_gl_surface_owns_font (dst, scaled_font)) - return UNSUPPORTED ("do not control font"); - - /* If the glyphs overlap, we need to build an intermediate mask rather - * then perform the compositing directly. - */ - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, num_glyphs, - &extents, - &overlap); - if (unlikely (status)) - return status; - - use_mask |= overlap; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - /* the empty clip should never be propagated this far */ - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (unlikely (_cairo_status_is_error (status))) - return status; - - use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_rectangle_intersect (&extents, - _cairo_clip_get_extents (clip))) - goto EMPTY; - } - - surface_extents.x = surface_extents.y = 0; - surface_extents.width = dst->width; - surface_extents.height = dst->height; - if (! _cairo_rectangle_intersect (&extents, &surface_extents)) - goto EMPTY; - - if (use_mask) { - return _cairo_gl_surface_show_glyphs_via_mask (dst, op, - source, - glyphs, num_glyphs, - &extents, - scaled_font, - clip, - remaining_glyphs); - } - - return _render_glyphs (dst, extents.x, extents.y, - op, source, - glyphs, num_glyphs, &extents, - scaled_font, &has_component_alpha, - clip_region, remaining_glyphs); - -EMPTY: - *remaining_glyphs = 0; - if (! _cairo_operator_bounded_by_mask (op)) - return _cairo_surface_paint (&dst->base, op, source, clip); - else - return CAIRO_STATUS_SUCCESS; +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 @@ -588,7 +490,8 @@ _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT, GLYPH_CACHE_MIN_SIZE, - sizeof (cairo_gl_glyph_private_t)); + sizeof (cairo_gl_glyph_t), + _cairo_gl_node_destroy); } void @@ -596,10 +499,5 @@ _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, cairo_gl_glyph_cache_t *cache) { _cairo_rtree_fini (&cache->rtree); - - if (cache->pattern.surface) { - _cairo_pattern_fini (&cache->pattern.base); - cache->pattern.surface = NULL; - } + cairo_surface_destroy (&cache->surface->base); } - diff --git a/gfx/cairo/cairo/src/cairo-gl-gradient-private.h b/gfx/cairo/cairo/src/cairo-gl-gradient-private.h new file mode 100644 index 000000000000..0d9f41f54b49 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-gradient-private.h @@ -0,0 +1,96 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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 +#include +#elif CAIRO_HAS_GLESV2_SURFACE +#include +#include +#elif CAIRO_HAS_GL_SURFACE +#include +#include +#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/gfx/cairo/cairo/src/cairo-gl-gradient.c b/gfx/cairo/cairo/src/cairo-gl-gradient.c new file mode 100644 index 000000000000..1bbd8dd0e6a6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-gradient.c @@ -0,0 +1,339 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#include "cairoint.h" +#include +#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 unsigned long +_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, + unsigned long 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) +{ + unsigned long 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/gfx/cairo/cairo/src/cairo-gl-info.c b/gfx/cairo/cairo/src/cairo-gl-info.c new file mode 100644 index 000000000000..53f5b17209fb --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-info.c @@ -0,0 +1,145 @@ +/* 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 + * Heiko Lewin + */ + +#include "cairoint.h" +#include "cairo-gl-private.h" + +#include + +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); +} + + +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 + + 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/gfx/cairo/cairo/src/cairo-gl-msaa-compositor.c b/gfx/cairo/cairo/src/cairo-gl-msaa-compositor.c new file mode 100644 index 000000000000..7a83dd219421 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-msaa-compositor.c @@ -0,0 +1,956 @@ +/* -*- 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 + * Martin Robinson + */ + +#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/gfx/cairo/cairo/src/cairo-gl-operand.c b/gfx/cairo/cairo/src/cairo-gl-operand.c new file mode 100644 index 000000000000..a754bde2f318 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-operand.c @@ -0,0 +1,793 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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/gfx/cairo/cairo/src/cairo-gl-private.h b/gfx/cairo/cairo/src/cairo-gl-private.h index 54f226f425a6..f02a58763e93 100644 --- a/gfx/cairo/cairo/src/cairo-gl-private.h +++ b/gfx/cairo/cairo/src/cairo-gl-private.h @@ -3,6 +3,7 @@ * 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 @@ -37,28 +38,40 @@ * Chris Wilson * Eric Anholt * T. Zachary Laine + * Alexandros Frantzis */ #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 -#include - -#include "cairo-gl.h" - +#if CAIRO_HAS_GLESV3_SURFACE +#include +#include +#elif CAIRO_HAS_GLESV2_SURFACE +#include +#include +#elif CAIRO_HAS_GL_SURFACE #include -#define GL_GLEXT_PROTOTYPES #include +#endif + +#include "cairo-gl-ext-def-private.h" #define DEBUG_GL 0 @@ -73,27 +86,119 @@ #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 */ -#define CAIRO_GL_VBO_SIZE 16384 +/* 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 { +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; /* GL framebuffer object holding depth */ + 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_gl_surface_t; + cairo_bool_t needs_update; + + cairo_region_t *clip_region; +}; typedef struct cairo_gl_glyph_cache { cairo_rtree_t rtree; - cairo_surface_pattern_t pattern; + cairo_gl_surface_t *surface; } cairo_gl_glyph_cache_t; typedef enum cairo_gl_tex { @@ -102,22 +207,16 @@ typedef enum cairo_gl_tex { CAIRO_GL_TEX_TEMP = 2 } cairo_gl_tex_t; -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, - CAIRO_GL_OPERAND_SPANS, - - CAIRO_GL_OPERAND_COUNT -} cairo_gl_operand_type_t; - -typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_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 { @@ -131,60 +230,130 @@ typedef enum cairo_gl_shader_in { typedef enum cairo_gl_var_type { CAIRO_GL_VAR_NONE, CAIRO_GL_VAR_TEXCOORDS, - CAIRO_GL_VAR_COVERAGE + CAIRO_GL_VAR_TEXGEN, } cairo_gl_var_type_t; -#define cairo_gl_var_type_hash(src,mask,dest) ((mask) << 2 | (src << 1) | (dest)) -#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_COVERAGE << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS) +typedef enum cairo_gl_primitive_type { + CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES, + CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS +} cairo_gl_primitive_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_surface_attributes_t attributes; - } texture; - struct { - GLfloat color[4]; - } constant; - struct { - cairo_gl_gradient_t *gradient; - cairo_matrix_t m; - float segment_x; - float segment_y; - cairo_extend_t extend; - } linear; - struct { - cairo_gl_gradient_t *gradient; - cairo_matrix_t m; - float circle_1_x; - float circle_1_y; - float radius_0; - float radius_1; - cairo_extend_t extend; - } radial; - }; - unsigned int vertex_offset; -} cairo_gl_operand_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; - GLuint dummy_tex; + const cairo_compositor_t *compositor; + GLuint texture_load_pbo; - GLuint vbo; GLint max_framebuffer_size; GLint max_texture_size; GLint max_textures; GLenum tex_target; - const cairo_gl_shader_impl_t *shader_impl; + GLint num_samples; + cairo_bool_t supports_msaa; + char *vb; - GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1]; + 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; @@ -199,11 +368,27 @@ struct _cairo_gl_context { cairo_gl_shader_t *current_shader; cairo_gl_operand_t operands[2]; + cairo_bool_t spans; - char *vb; + 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); @@ -220,9 +405,17 @@ typedef struct _cairo_gl_composite { 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; -cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend; +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) @@ -261,18 +454,22 @@ _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); + 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)->shader_impl != NULL; + 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_EXT; + return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE; } static cairo_always_inline cairo_status_t cairo_warn @@ -312,7 +509,28 @@ _cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status) } cairo_private void -_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface); +_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, @@ -321,78 +539,95 @@ _cairo_gl_context_activate (cairo_gl_context_t *ctx, 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, - const cairo_rectangle_int_t *rect); + 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, - int src_x, int src_y, - int dst_x, int dst_y, - int width, int height); + 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, - int src_x, int src_y, - int dst_x, int dst_y, - int width, int height); + 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_spans (cairo_gl_composite_t *setup); +_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 void -_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx, - GLfloat x1, - GLfloat y1, - GLfloat x2, - GLfloat y2, - uint8_t alpha); - -cairo_private 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_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 (pixman_format_code_t pixman_format, +_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_private void -_cairo_gl_surface_scaled_font_fini ( cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); + 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); @@ -408,18 +643,9 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, + const cairo_clip_t *clip, int *remaining_glyphs); -static inline int -_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y) -{ - if (surface->fb) - return y; - else - return (surface->height - 1) - y; -} - cairo_private cairo_status_t _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx); @@ -429,48 +655,49 @@ _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 == NULL; + return ctx->vb_offset == 0; } cairo_private cairo_status_t _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, - cairo_gl_operand_type_t source, - cairo_gl_operand_type_t mask, + 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, - const char *name, + GLint location, float value); cairo_private void _cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, - const char *name, + GLint location, float value0, float value1); cairo_private void _cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, - const char *name, + GLint location, float value0, float value1, float value2); cairo_private void _cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, - const char *name, + GLint location, float value0, float value1, float value2, float value3); cairo_private void _cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, - const char *name, - cairo_matrix_t* m); + GLint location, + const cairo_matrix_t* m); cairo_private void -_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx, - const char *name, - GLuint tex_unit); +_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, @@ -479,6 +706,159 @@ _cairo_gl_set_shader (cairo_gl_context_t *ctx, 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); diff --git a/gfx/cairo/cairo/src/cairo-gl-shaders.c b/gfx/cairo/cairo/src/cairo-gl-shaders.c index d7773f567781..b70c177f2228 100644 --- a/gfx/cairo/cairo/src/cairo-gl-shaders.c +++ b/gfx/cairo/cairo/src/cairo-gl-shaders.c @@ -3,6 +3,7 @@ * 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 @@ -35,6 +36,8 @@ * Benjamin Otte * Eric Anholt * T. Zachary Laine + * Alexandros Frantzis + * H. Lewin */ #include "cairoint.h" @@ -42,431 +45,85 @@ #include "cairo-error-private.h" #include "cairo-output-stream-private.h" -typedef struct cairo_gl_shader_impl { - void - (*compile_shader) (GLuint *shader, GLenum type, const char *text); - - void - (*link_shader) (GLuint *program, GLuint vert, GLuint frag); - - void - (*destroy_shader) (GLuint shader); - - void - (*destroy_program) (GLuint program); - - void - (*bind_float) (cairo_gl_shader_t *shader, - const char *name, - float value); - - void - (*bind_vec2) (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1); - - void - (*bind_vec3) (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2); - - void - (*bind_vec4) (cairo_gl_shader_t *shader, - const char *name, - float value0, float value1, - float value2, float value3); - - void - (*bind_matrix) (cairo_gl_shader_t *shader, - const char *name, - cairo_matrix_t* m); - - void - (*bind_texture) (cairo_gl_shader_t *shader, - const char *name, - cairo_gl_tex_t tex_unit); - - void - (*use) (cairo_gl_shader_t *shader); -} shader_impl_t; - static cairo_status_t -_cairo_gl_shader_compile (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - const char *fragment_text); - -/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions - API. */ -static void -compile_shader_arb (GLuint *shader, GLenum type, const char *text) -{ - const char* strings[1] = { text }; - GLint gl_status; - - *shader = glCreateShaderObjectARB (type); - glShaderSourceARB (*shader, 1, strings, 0); - glCompileShaderARB (*shader); - glGetObjectParameterivARB (*shader, GL_OBJECT_COMPILE_STATUS_ARB, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetObjectParameterivARB (*shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetInfoLogARB (*shader, log_size, &chars, log); - printf ("OpenGL shader compilation failed. Shader:\n" - "%s\n" - "OpenGL compilation log:\n" - "%s\n", - text, log); - - free (log); - } else { - printf ("OpenGL shader compilation failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -link_shader_arb (GLuint *program, GLuint vert, GLuint frag) -{ - GLint gl_status; - - *program = glCreateProgramObjectARB (); - glAttachObjectARB (*program, vert); - glAttachObjectARB (*program, frag); - glLinkProgramARB (*program); - glGetObjectParameterivARB (*program, GL_OBJECT_LINK_STATUS_ARB, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetObjectParameterivARB (*program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetInfoLogARB (*program, log_size, &chars, log); - printf ("OpenGL shader link failed:\n%s\n", log); - - free (log); - } else { - printf ("OpenGL shader link failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -destroy_shader_arb (GLuint shader) -{ - glDeleteObjectARB (shader); -} - -static void -destroy_program_arb (GLuint shader) -{ - glDeleteObjectARB (shader); -} - -static void -bind_float_arb (cairo_gl_shader_t *shader, - const char *name, - float value) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform1fARB (location, value); -} - -static void -bind_vec2_arb (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform2fARB (location, value0, value1); -} - -static void -bind_vec3_arb (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform3fARB (location, value0, value1, value2); -} - -static void -bind_vec4_arb (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2, - float value3) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform4fARB (location, value0, value1, value2, value3); -} - -static void -bind_matrix_arb (cairo_gl_shader_t *shader, - const char *name, - cairo_matrix_t* m) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - float gl_m[9] = { - m->xx, m->xy, m->x0, - m->yx, m->yy, m->y0, - 0, 0, 1 - }; - assert (location != -1); - glUniformMatrix3fvARB (location, 1, GL_TRUE, gl_m); -} - -static void -bind_texture_arb (cairo_gl_shader_t *shader, - const char *name, - cairo_gl_tex_t tex_unit) -{ - GLint location = glGetUniformLocationARB (shader->program, name); - assert (location != -1); - glUniform1iARB (location, tex_unit); -} - -static void -use_program_arb (cairo_gl_shader_t *shader) -{ - if (shader) - glUseProgramObjectARB (shader->program); - else - glUseProgramObjectARB (0); -} - -/* OpenGL Core 2.0 API. */ -static void -compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text) -{ - const char* strings[1] = { text }; - GLint gl_status; - - *shader = glCreateShader (type); - glShaderSource (*shader, 1, strings, 0); - glCompileShader (*shader); - glGetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetShaderInfoLog (*shader, log_size, &chars, log); - printf ("OpenGL shader compilation failed. Shader:\n" - "%s\n" - "OpenGL compilation log:\n" - "%s\n", - text, log); - - free (log); - } else { - printf ("OpenGL shader compilation failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -link_shader_core_2_0 (GLuint *program, GLuint vert, GLuint frag) -{ - GLint gl_status; - - *program = glCreateProgram (); - glAttachShader (*program, vert); - glAttachShader (*program, frag); - glLinkProgram (*program); - glGetProgramiv (*program, GL_LINK_STATUS, &gl_status); - if (gl_status == GL_FALSE) { - GLint log_size; - glGetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size); - if (0 < log_size) { - char *log = _cairo_malloc (log_size); - GLint chars; - - log[log_size - 1] = '\0'; - glGetProgramInfoLog (*program, log_size, &chars, log); - printf ("OpenGL shader link failed:\n%s\n", log); - - free (log); - } else { - printf ("OpenGL shader link failed.\n"); - } - - ASSERT_NOT_REACHED; - } -} - -static void -destroy_shader_core_2_0 (GLuint shader) -{ - glDeleteShader (shader); -} - -static void -destroy_program_core_2_0 (GLuint shader) -{ - glDeleteProgram (shader); -} - -static void -bind_float_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform1f (location, value); -} - -static void -bind_vec2_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform2f (location, value0, value1); -} - -static void -bind_vec3_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform3f (location, value0, value1, value2); -} - -static void -bind_vec4_core_2_0 (cairo_gl_shader_t *shader, - const char *name, - float value0, - float value1, - float value2, - float value3) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform4f (location, value0, value1, value2, value3); -} - -static void -bind_matrix_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_matrix_t* m) -{ - GLint location = glGetUniformLocation (shader->program, name); - float gl_m[16] = { - m->xx, m->xy, m->x0, - m->yx, m->yy, m->y0, - 0, 0, 1 - }; - assert (location != -1); - glUniformMatrix3fv (location, 1, GL_TRUE, gl_m); -} - -static void -bind_texture_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_gl_tex_t tex_unit) -{ - GLint location = glGetUniformLocation (shader->program, name); - assert (location != -1); - glUniform1i (location, tex_unit); -} - -static void -use_program_core_2_0 (cairo_gl_shader_t *shader) -{ - if (shader) - glUseProgram (shader->program); - else - glUseProgram (0); -} - -static const cairo_gl_shader_impl_t shader_impl_core_2_0 = { - compile_shader_core_2_0, - link_shader_core_2_0, - destroy_shader_core_2_0, - destroy_program_core_2_0, - bind_float_core_2_0, - bind_vec2_core_2_0, - bind_vec3_core_2_0, - bind_vec4_core_2_0, - bind_matrix_core_2_0, - bind_texture_core_2_0, - use_program_core_2_0, -}; - -static const cairo_gl_shader_impl_t shader_impl_arb = { - compile_shader_arb, - link_shader_arb, - destroy_shader_arb, - destroy_program_arb, - bind_float_arb, - bind_vec2_arb, - bind_vec3_arb, - bind_vec4_arb, - bind_matrix_arb, - bind_texture_arb, - use_program_arb, -}; +_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 (const void *key_a, const void *key_b) +_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->src == b->src && - a->mask == b->mask && - a->dest == b->dest && - a->in == b->in; + 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 (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in); + return (((uint32_t)entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage) ^ entry->vertex; } static void @@ -476,7 +133,7 @@ _cairo_gl_shader_cache_destroy (void *data) _cairo_gl_shader_fini (entry->ctx, &entry->shader); if (entry->ctx->current_shader == &entry->shader) - entry->ctx->current_shader = NULL; + entry->ctx->current_shader = NULL; free (entry); } @@ -491,6 +148,9 @@ 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" @@ -498,37 +158,38 @@ _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) "}\n"; cairo_status_t status; - /* XXX multiple device support? */ - if (GLEW_VERSION_2_0) { - ctx->shader_impl = &shader_impl_core_2_0; - } else if (GLEW_ARB_shader_objects && - GLEW_ARB_fragment_shader && - GLEW_ARB_vertex_program) { - ctx->shader_impl = &shader_impl_arb; + 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->shader_impl = NULL; + 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, - _cairo_gl_shader_cache_equal, - NULL, - _cairo_gl_shader_cache_destroy, - CAIRO_GL_MAX_SHADERS_PER_CONTEXT); + 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; - if (ctx->shader_impl != NULL) { - _cairo_gl_shader_init (&ctx->fill_rectangles_shader); - status = _cairo_gl_shader_compile (ctx, - &ctx->fill_rectangles_shader, - CAIRO_GL_VAR_NONE, - CAIRO_GL_VAR_NONE, - fill_fs_source); - 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; } @@ -538,11 +199,12 @@ _cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx) { int i; - for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) { + for (i = 0; i < CAIRO_GL_VAR_TYPE_MAX; i++) { if (ctx->vertex_shaders[i]) - ctx->shader_impl->destroy_shader (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); } @@ -551,100 +213,513 @@ _cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader) { if (shader->fragment_shader) - ctx->shader_impl->destroy_shader (shader->fragment_shader); + ctx->dispatch.DeleteShader (shader->fragment_shader); if (shader->program) - ctx->shader_impl->destroy_program (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_type_t type) +cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand) { - switch (type) { + switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; + 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: - return CAIRO_GL_VAR_NONE; + 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 CAIRO_GL_VAR_TEXCOORDS; - case CAIRO_GL_OPERAND_SPANS: - return CAIRO_GL_VAR_COVERAGE; + 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) + cairo_gl_var_type_t type, + cairo_gl_tex_t name) { switch (type) { default: - ASSERT_NOT_REACHED; + ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: - break; + break; case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n", - operand_names[name]); - break; - case CAIRO_GL_VAR_COVERAGE: - _cairo_output_stream_printf (stream, - "varying float %s_coverage;\n", - operand_names[name]); - break; + _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) + cairo_gl_var_type_t type, + cairo_gl_tex_t name) { switch (type) { default: - ASSERT_NOT_REACHED; + ASSERT_NOT_REACHED; case CAIRO_GL_VAR_NONE: - break; + break; case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - " %s_texcoords = gl_MultiTexCoord%d.xy;\n", - operand_names[name], name); - break; - case CAIRO_GL_VAR_COVERAGE: - _cairo_output_stream_printf (stream, - " %s_coverage = gl_Color.a;\n", - operand_names[name]); - break; + _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_gl_var_type_t dest, + 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 int length; + 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 = ftransform();\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); @@ -658,193 +733,159 @@ cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, } static void -cairo_gl_shader_emit_color (cairo_output_stream_t *stream, - GLuint tex_target, - cairo_gl_operand_type_t type, - cairo_gl_tex_t name) +compile_shader (cairo_gl_context_t *ctx, + GLuint *shader, + GLenum type, + const char *source) { - const char *namestr = operand_names[name]; - const char *rectstr = (tex_target == GL_TEXTURE_RECTANGLE_EXT ? "Rect" : ""); + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + GLint success, log_size, num_chars; + char *log; - switch (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" - "varying vec2 %s_texcoords;\n" - "vec4 get_%s()\n" - "{\n" - " return texture2D%s(%s_sampler, %s_texcoords);\n" - "}\n", - rectstr, namestr, namestr, namestr, rectstr, namestr, namestr); - break; - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - _cairo_output_stream_printf (stream, - "uniform sampler1D %s_sampler;\n" - "uniform mat3 %s_matrix;\n" - "uniform vec2 %s_segment;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" - " float t = dot (pos, %s_segment) / dot (%s_segment, %s_segment);\n" - " return texture1D (%s_sampler, t);\n" - "}\n", - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr); - break; - case CAIRO_GL_OPERAND_RADIAL_GRADIENT: - _cairo_output_stream_printf (stream, - "uniform sampler1D %s_sampler;\n" - "uniform mat3 %s_matrix;\n" - "uniform vec2 %s_circle_1;\n" - "uniform float %s_radius_0;\n" - "uniform float %s_radius_1;\n" - "\n" - "vec4 get_%s()\n" - "{\n" - " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" - " \n" - " float dr = %s_radius_1 - %s_radius_0;\n" - " float dot_circle_1 = dot (%s_circle_1, %s_circle_1);\n" - " float dot_pos_circle_1 = dot (pos, %s_circle_1);\n" - " \n" - " float A = dot_circle_1 - dr * dr;\n" - " float B = -2.0 * (dot_pos_circle_1 + %s_radius_0 * dr);\n" - " float C = dot (pos, pos) - %s_radius_0 * %s_radius_0;\n" - " float det = B * B - 4.0 * A * C;\n" - " det = max (det, 0.0);\n" - " \n" - " float sqrt_det = sqrt (det);\n" - " sqrt_det *= sign(A);\n" - " \n" - " float t = (-B + sqrt_det) / (2.0 * A);\n" - " return texture1D (%s_sampler, t);\n" - "}\n", - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr, namestr, - namestr); - break; - case CAIRO_GL_OPERAND_SPANS: - _cairo_output_stream_printf (stream, - "varying float %s_coverage;\n" - "vec4 get_%s()\n" - "{\n" - " return vec4(0, 0, 0, %s_coverage);\n" - "}\n", - namestr, namestr, namestr); - break; + *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_get_fragment_source (GLuint tex_target, - cairo_gl_shader_in_t in, - cairo_gl_operand_type_t src, - cairo_gl_operand_type_t mask, - cairo_gl_operand_type_t dest, - char **out) -{ - cairo_output_stream_t *stream = _cairo_memory_stream_create (); - unsigned char *source; - unsigned int length; - cairo_status_t status; - - cairo_gl_shader_emit_color (stream, tex_target, src, CAIRO_GL_TEX_SOURCE); - cairo_gl_shader_emit_color (stream, tex_target, mask, CAIRO_GL_TEX_MASK); - - _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;\n"); - break; - case CAIRO_GL_SHADER_IN_CA_SOURCE: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source() * get_mask();\n"); - break; - case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: - _cairo_output_stream_printf (stream, - " gl_FragColor = get_source().a * get_mask();\n"); - 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 cairo_status_t -_cairo_gl_shader_compile (cairo_gl_context_t *ctx, - cairo_gl_shader_t *shader, - cairo_gl_var_type_t src, - cairo_gl_var_type_t mask, - const char *fragment_text) +_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; - - if (ctx->shader_impl == NULL) - return CAIRO_STATUS_SUCCESS; + int i; assert (shader->program == 0); - vertex_shader = cairo_gl_var_type_hash (src, mask, CAIRO_GL_VAR_NONE); + 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; + if (unlikely (status)) + goto FAILURE; - ctx->shader_impl->compile_shader (&ctx->vertex_shaders[vertex_shader], - GL_VERTEX_SHADER, - source); - free (source); + compile_shader (ctx, &ctx->vertex_shaders[vertex_shader], + GL_VERTEX_SHADER, source); + free (source); } - ctx->shader_impl->compile_shader (&shader->fragment_shader, - GL_FRAGMENT_SHADER, - fragment_text); + compile_shader (ctx, &shader->fragment_shader, + GL_FRAGMENT_SHADER, fragment_text); - ctx->shader_impl->link_shader (&shader->program, - ctx->vertex_shaders[vertex_shader], - shader->fragment_shader); + 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; @@ -856,125 +897,193 @@ _cairo_gl_shader_compile (cairo_gl_context_t *ctx, 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, - const char *name, + GLint location, float value) { - ctx->shader_impl->bind_float (ctx->current_shader, name, 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, - const char *name, + GLint location, float value0, float value1) { - ctx->shader_impl->bind_vec2 (ctx->current_shader, name, value0, 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, - const char *name, + GLint location, float value0, float value1, float value2) { - ctx->shader_impl->bind_vec3 (ctx->current_shader, name, value0, value1, 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, - const char *name, + GLint location, float value0, float value1, float value2, float value3) { - ctx->shader_impl->bind_vec4 (ctx->current_shader, name, value0, value1, value2, 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, - const char *name, cairo_matrix_t* m) + GLint location, + const cairo_matrix_t* m) { - ctx->shader_impl->bind_matrix (ctx->current_shader, name, 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_texture (cairo_gl_context_t *ctx, - const char *name, GLuint tex_unit) +_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, + GLint location, GLfloat* gl_m) { - ctx->shader_impl->bind_texture (ctx->current_shader, name, tex_unit); + 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->shader_impl == NULL) + if (ctx->current_shader == shader) return; - if (ctx->current_shader == shader) - return; - - ctx->shader_impl->use (shader); + 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_type_t source, - cairo_gl_operand_type_t mask, - cairo_gl_shader_in_t in, - cairo_gl_shader_t **shader) + 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; - if (ctx->shader_impl == NULL) { - *shader = NULL; - return CAIRO_STATUS_SUCCESS; - } + lookup.ctx = ctx; - lookup.src = source; - lookup.mask = mask; + 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; + assert (entry->shader.program); + *shader = &entry->shader; return CAIRO_STATUS_SUCCESS; } - status = cairo_gl_shader_get_fragment_source (ctx->tex_target, + 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 = malloc (sizeof (cairo_shader_cache_entry_t)); + entry = _cairo_malloc (sizeof (cairo_shader_cache_entry_t)); if (unlikely (entry == NULL)) { - free (fs_source); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + 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 (ctx, - &entry->shader, - cairo_gl_operand_get_var_type (source), - cairo_gl_operand_get_var_type (mask), - fs_source); + 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)) { @@ -982,6 +1091,13 @@ _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, 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); diff --git a/gfx/cairo/cairo/src/cairo-gl-source.c b/gfx/cairo/cairo/src/cairo-gl-source.c new file mode 100644 index 000000000000..7e0ee4a826fa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-source.c @@ -0,0 +1,113 @@ +/* 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 + */ + +#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/gfx/cairo/cairo/src/cairo-gl-spans-compositor.c b/gfx/cairo/cairo/src/cairo-gl-spans-compositor.c new file mode 100644 index 000000000000..0a4538a047c5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-spans-compositor.c @@ -0,0 +1,556 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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/gfx/cairo/cairo/src/cairo-gl-surface-legacy.c b/gfx/cairo/cairo/src/cairo-gl-surface-legacy.c new file mode 100644 index 000000000000..87dca2f03ecb --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-surface-legacy.c @@ -0,0 +1,602 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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/gfx/cairo/cairo/src/cairo-gl-surface.c b/gfx/cairo/cairo/src/cairo-gl-surface.c index 278e6429dce0..b0f42d6aa1e0 100644 --- a/gfx/cairo/cairo/src/cairo-gl-surface.c +++ b/gfx/cairo/cairo/src/cairo-gl-surface.c @@ -40,46 +40,144 @@ #include "cairoint.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" #include "cairo-gl-private.h" -static 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); +#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 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); +static const cairo_surface_backend_t _cairo_gl_surface_backend; static cairo_status_t -_cairo_gl_surface_flush (void *abstract_surface); +_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; } -cairo_bool_t -_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, - GLenum *internal_format, GLenum *format, - GLenum *type, cairo_bool_t *has_alpha) +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: @@ -163,6 +261,9 @@ _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, *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: @@ -190,30 +291,130 @@ _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format, case PIXMAN_yv12: case PIXMAN_x2r10g10b10: case PIXMAN_a2r10g10b10: + case PIXMAN_r8g8b8x8: + case PIXMAN_r8g8b8a8: + case PIXMAN_x14r6g6b6: 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); + 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 * @@ -225,14 +426,16 @@ _cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, { cairo_gl_surface_t *surface; - assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size); - surface = calloc (1, sizeof (cairo_gl_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - _cairo_gl_surface_init (&ctx->base, surface, content, width, height); 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); @@ -244,10 +447,11 @@ _cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, } static cairo_surface_t * -_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, - cairo_content_t content, - int width, - int height) +_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; @@ -275,8 +479,13 @@ _cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, format = GL_RGBA; break; case CAIRO_CONTENT_ALPHA: - /* We want to be trying GL_ALPHA framebuffer objects here. */ - format = GL_RGBA; + /* 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 @@ -297,9 +506,27 @@ _cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, 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) + const cairo_color_t *color) { cairo_gl_context_t *ctx; cairo_status_t status; @@ -309,27 +536,54 @@ _cairo_gl_surface_clear (cairo_gl_surface_t *surface, if (unlikely (status)) return status; - _cairo_gl_context_set_destination (ctx, surface); + _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; + r = color->red * color->alpha; + g = color->green * color->alpha; + b = color->blue * color->alpha; } else { - r = g = b = 0; + r = g = b = 0; } if (surface->base.content & CAIRO_CONTENT_ALPHA) { - a = color->alpha; + a = color->alpha; } else { - a = 1.0; + 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, @@ -343,10 +597,8 @@ cairo_gl_surface_create (cairo_device_t *abstract_device, 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 (_cairo_format_from_content (content), - width, height); - } + 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); @@ -358,17 +610,19 @@ cairo_gl_surface_create (cairo_device_t *abstract_device, 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 (ctx, content, width, height); + _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); } - /* Cairo surfaces start out initialized to transparent (black) */ - status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); - status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (&surface->base); @@ -379,7 +633,6 @@ cairo_gl_surface_create (cairo_device_t *abstract_device, } slim_hidden_def (cairo_gl_surface_create); - /** * cairo_gl_surface_create_for_texture: * @content: type of content in the surface @@ -404,6 +657,8 @@ slim_hidden_def (cairo_gl_surface_create); * 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, @@ -426,12 +681,17 @@ cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, 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)); + 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); @@ -448,25 +708,27 @@ cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, int height) { cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { - status = _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)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } - surface->width = width; - surface->height = height; + 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 @@ -495,37 +757,39 @@ void cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) { cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; - cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - return; + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; } if (! _cairo_surface_is_gl (abstract_surface)) { - status = _cairo_surface_set_error (abstract_surface, - CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + _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; + cairo_status_t status; - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return; + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return; - cairo_surface_flush (abstract_surface); + /* 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); + status = _cairo_gl_context_release (ctx, status); + if (status) + status = _cairo_surface_set_error (abstract_surface, status); } } @@ -539,229 +803,278 @@ _cairo_gl_surface_create_similar (void *abstract_surface, cairo_gl_context_t *ctx; cairo_status_t status; - if (width < 1 || height < 1) - return cairo_image_surface_create (_cairo_format_from_content (content), - width, height); + 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); - if (width > ctx->max_framebuffer_size || - height > ctx->max_framebuffer_size) - { - surface = NULL; - goto RELEASE; - } + surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); - surface = _cairo_gl_surface_create_scratch (ctx, content, width, height); - -RELEASE: status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { - cairo_surface_destroy (surface); - return _cairo_surface_create_in_error (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) + int dst_x, int dst_y, + cairo_bool_t force_flush) { GLenum internal_format, format, type; - cairo_bool_t has_alpha; + cairo_bool_t has_alpha, needs_swap; cairo_image_surface_t *clone = NULL; cairo_gl_context_t *ctx; int cpp; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (! _cairo_gl_get_image_format_and_type (src->pixman_format, - &internal_format, - &format, - &type, - &has_alpha)) - { - cairo_bool_t is_supported; - - clone = _cairo_image_surface_coerce (src); - if (unlikely (clone->base.status)) - return clone->base.status; - - is_supported = - _cairo_gl_get_image_format_and_type (clone->pixman_format, - &internal_format, - &format, - &type, - &has_alpha); - assert (is_supported); - src = clone; - } - - cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; + 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; - status = _cairo_gl_surface_flush (&dst->base); - if (unlikely (status)) - goto FAIL; + 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; + } - glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); if (_cairo_gl_surface_is_texture (dst)) { - _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); + 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, - src->data + src_y * src->stride + src_x * cpp); + 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_rectangle_int_t rect; - - rect.x = dst_x; - rect.y = dst_y; - rect.width = width; - rect.height = height; - - _cairo_gl_composite_flush (ctx); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - _cairo_gl_surface_fill_rectangles (dst, - CAIRO_OPERATOR_SOURCE, - CAIRO_COLOR_BLACK, - &rect, 1); - _cairo_gl_composite_flush (ctx); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + _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)) { - cairo_surface_destroy (tmp); - goto FAIL; - } - status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp, - src, - src_x, src_y, - width, height, - 0, 0); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_pattern_t tmp_pattern; + cairo_surface_t *tmp; - _cairo_pattern_init_for_surface (&tmp_pattern, tmp); - _cairo_gl_surface_composite (CAIRO_OPERATOR_SOURCE, - &tmp_pattern.base, - NULL, - dst, - 0, 0, - 0, 0, - dst_x, dst_y, - width, height, - NULL); - _cairo_pattern_fini (&tmp_pattern.base); - } + tmp = _cairo_gl_surface_create_scratch (ctx, + dst->base.content, + width, height); + if (unlikely (tmp->status)) + goto FAIL; - cairo_surface_destroy (tmp); + 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: - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - status = _cairo_gl_context_release (ctx, status); if (clone) - cairo_surface_destroy (&clone->base); + cairo_surface_destroy (&clone->base); + + if (rgba_clone) + cairo_surface_destroy (&rgba_clone->base); return status; } -static cairo_status_t -_cairo_gl_surface_get_image (cairo_gl_surface_t *surface, - cairo_rectangle_int_t *interest, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *rect_out) +static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface) { - cairo_image_surface_t *image; - cairo_gl_context_t *ctx; - GLenum format, type; - cairo_format_t cairo_format; - unsigned int cpp; - cairo_status_t status; - - /* Want to use a switch statement here but the compiler gets whiny. */ - if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { - format = GL_BGRA; - cairo_format = CAIRO_FORMAT_ARGB32; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - cpp = 4; - } else if (surface->base.content == CAIRO_CONTENT_COLOR) { - format = GL_BGRA; - cairo_format = CAIRO_FORMAT_RGB24; - type = GL_UNSIGNED_INT_8_8_8_8_REV; - cpp = 4; - } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { - format = GL_ALPHA; - cairo_format = CAIRO_FORMAT_A8; - type = GL_UNSIGNED_BYTE; - cpp = 1; - } else { - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - image = (cairo_image_surface_t*) - cairo_image_surface_create (cairo_format, - interest->width, interest->height); - if (unlikely (image->base.status)) - return image->base.status; - - /* 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. - */ - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - - _cairo_gl_composite_flush (ctx); - _cairo_gl_context_set_destination (ctx, surface); - - glPixelStorei (GL_PACK_ALIGNMENT, 1); - glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); - if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert) - glPixelStorei (GL_PACK_INVERT_MESA, 1); - glReadPixels (interest->x, interest->y, - interest->width, interest->height, - format, type, image->data); - if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert) - glPixelStorei (GL_PACK_INVERT_MESA, 0); - - status = _cairo_gl_context_release (ctx, status); - if (unlikely (status)) { - cairo_surface_destroy (&image->base); - return status; - } - - *image_out = image; - if (rect_out != NULL) - *rect_out = *interest; - - return CAIRO_STATUS_SUCCESS; + cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; + return ctx->gl_flavor; } static cairo_status_t @@ -773,27 +1086,217 @@ _cairo_gl_surface_finish (void *abstract_surface) status = _cairo_gl_context_acquire (surface->base.device, &ctx); if (unlikely (status)) - return 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); + 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); + 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->depth) - glDeleteFramebuffersEXT (1, &surface->depth); if (surface->fb) - glDeleteFramebuffersEXT (1, &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, @@ -807,7 +1310,10 @@ _cairo_gl_surface_acquire_source_image (void *abstract_surface, extents.x = extents.y = 0; extents.width = surface->width; extents.height = surface->height; - return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL); + + *image_out = (cairo_image_surface_t *) + _cairo_gl_surface_map_to_image (surface, &extents); + return (*image_out)->base.status; } static void @@ -818,535 +1324,25 @@ _cairo_gl_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } -static 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) +static cairo_int_status_t +_cairo_gl_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) { - cairo_gl_surface_t *surface = abstract_surface; - - *image_extra = NULL; - return _cairo_gl_surface_get_image (surface, interest_rect, image_out, - image_rect_out); -} - -static 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; + cairo_int_status_t status; status = _cairo_gl_surface_draw_image (abstract_surface, image, 0, 0, image->width, image->height, - image_rect->x, image_rect->y); - /* as we created the image, its format should be directly applicable */ - assert (status == CAIRO_STATUS_SUCCESS); + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0, + TRUE); + cairo_surface_finish (&image->base); cairo_surface_destroy (&image->base); -} - -static 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; - - /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */ - if (src->device == surface->base.device && - _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) { - *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; - cairo_status_t status; - - 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); - 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; -} - -static 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; - - 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); - 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; } -static 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_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; -} - -static 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_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); -} - -static 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; -} - -static 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_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_surface_span_renderer_t *renderer; - cairo_status_t status; - const cairo_rectangle_int_t *extents; - - 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_mask_spans (&renderer->setup); - _cairo_gl_composite_set_clip_region (&renderer->setup, clip_region); - - 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); -} - static cairo_bool_t _cairo_gl_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) @@ -1361,277 +1357,194 @@ _cairo_gl_surface_get_extents (void *abstract_surface, return TRUE; } -static void -_cairo_gl_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); -} - static cairo_status_t -_cairo_gl_surface_flush (void *abstract_surface) +_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; + 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)) + 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 *abstract_surface, +_cairo_gl_surface_paint (void *surface, cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) + 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 (abstract_surface, CAIRO_COLOR_TRANSPARENT); + 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 (abstract_surface, - &((cairo_solid_pattern_t *) source)->color); - } - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_gl_surface_polygon (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_region_t *clip_region = NULL; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (_cairo_status_is_error (status))) - return status; - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - return UNSUPPORTED ("a clip surface would be required"); - } - - if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias)) - return UNSUPPORTED ("no span renderer"); - - if (op == CAIRO_OPERATOR_SOURCE) - return UNSUPPORTED ("SOURCE compositing doesn't work in GL"); - if (op == CAIRO_OPERATOR_CLEAR) { - op = CAIRO_OPERATOR_DEST_OUT; - src = &_cairo_pattern_white.base; - } - - status = _cairo_surface_composite_polygon (&dst->base, - op, - src, - fill_rule, - antialias, - extents, - polygon, - clip_region); - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_polygon_t polygon; - cairo_status_t status; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - surface->width, - surface->height, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_stroke_to_polygon (path, - style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_polygon_t polygon; - cairo_status_t status; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - surface->width, - surface->height, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - -#if 0 - if (extents.is_bounded && clip != NULL) { - cairo_clip_path_t *clip_path; - - if (((clip_path = _clip_get_single_path (clip)) != NULL) && - _cairo_path_fixed_equal (&clip_path->path, path)) - { - clip = NULL; + (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); } } -#endif - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; + return _cairo_compositor_paint (get_compositor (surface), surface, + op, source, clip); } -const cairo_surface_backend_t _cairo_gl_surface_backend = { - CAIRO_SURFACE_TYPE_GL, - _cairo_gl_surface_create_similar, - _cairo_gl_surface_finish, +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, - _cairo_gl_surface_acquire_dest_image, - _cairo_gl_surface_release_dest_image, - - _cairo_gl_surface_clone_similar, - _cairo_gl_surface_composite, - _cairo_gl_surface_fill_rectangles, - _cairo_gl_surface_composite_trapezoids, - _cairo_gl_surface_create_span_renderer, - _cairo_gl_surface_check_span_renderer, + NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ + _cairo_gl_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_gl_surface_get_font_options, + _cairo_image_surface_get_font_options, + _cairo_gl_surface_flush, NULL, /* mark_dirty_rectangle */ - _cairo_gl_surface_scaled_font_fini, - _cairo_gl_surface_scaled_glyph_fini, + _cairo_gl_surface_paint, - NULL, /* mask */ + _cairo_gl_surface_mask, _cairo_gl_surface_stroke, _cairo_gl_surface_fill, - _cairo_gl_surface_show_glyphs, /* show_glyphs */ - NULL /* snapshot */ + NULL, /* fill/stroke */ + _cairo_gl_surface_glyphs, }; diff --git a/gfx/cairo/cairo/src/cairo-gl-traps-compositor.c b/gfx/cairo/cairo/src/cairo-gl-traps-compositor.c new file mode 100644 index 000000000000..7938c5b20a51 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-traps-compositor.c @@ -0,0 +1,531 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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/gfx/cairo/cairo/src/cairo-gl.h b/gfx/cairo/cairo/src/cairo-gl.h index 131d1148bee2..7cd869c76b1f 100644 --- a/gfx/cairo/cairo/src/cairo-gl.h +++ b/gfx/cairo/cairo/src/cairo-gl.h @@ -31,12 +31,38 @@ * 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 +#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE CAIRO_BEGIN_DECLS @@ -62,6 +88,10 @@ 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 @@ -108,6 +138,12 @@ cairo_gl_surface_create_for_egl (cairo_device_t *device, 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 diff --git a/gfx/cairo/cairo/src/cairo-glitz-surface.c b/gfx/cairo/cairo/src/cairo-glitz-surface.c deleted file mode 100644 index 5f97f65e8a08..000000000000 --- a/gfx/cairo/cairo/src/cairo-glitz-surface.c +++ /dev/null @@ -1,2446 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 David Reveman - * - * Permission to use, copy, modify, distribute, and sell this software - * and its documentation for any purpose is hereby granted without - * fee, provided that the above copyright notice appear in all copies - * and that both that copyright notice and this permission notice - * appear in supporting documentation, and that the name of David - * Reveman not be used in advertising or publicity pertaining to - * distribution of the software without specific, written prior - * permission. David Reveman makes no representations about the - * suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR - * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author: David Reveman - */ - -#include "cairoint.h" -#include "cairo-glitz.h" -#include "cairo-glitz-private.h" -#include "cairo-region-private.h" - -typedef struct _cairo_glitz_surface { - cairo_surface_t base; - - glitz_surface_t *surface; - glitz_format_t *format; - - cairo_region_t *clip_region; - cairo_bool_t has_clip; - glitz_box_t *clip_boxes; - int num_clip_boxes; -} cairo_glitz_surface_t; - -static const cairo_surface_backend_t * -_cairo_glitz_surface_get_backend (void); - -static cairo_status_t -_cairo_glitz_surface_finish (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (surface->clip_boxes) - free (surface->clip_boxes); - - cairo_region_destroy (surface->clip_region); - glitz_surface_destroy (surface->surface); - - return CAIRO_STATUS_SUCCESS; -} - -static glitz_format_name_t -_glitz_format_from_content (cairo_content_t content) -{ - switch (content) { - case CAIRO_CONTENT_COLOR: - return GLITZ_STANDARD_RGB24; - case CAIRO_CONTENT_ALPHA: - return GLITZ_STANDARD_A8; - case CAIRO_CONTENT_COLOR_ALPHA: - return GLITZ_STANDARD_ARGB32; - } - - ASSERT_NOT_REACHED; - return GLITZ_STANDARD_ARGB32; -} - -static cairo_surface_t * -_cairo_glitz_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - cairo_glitz_surface_t *src = abstract_src; - cairo_surface_t *crsurface; - glitz_drawable_t *drawable; - glitz_surface_t *surface; - glitz_format_t *gformat; - - drawable = glitz_surface_get_drawable (src->surface); - - gformat = - glitz_find_standard_format (drawable, - _glitz_format_from_content (content)); - if (!gformat) - return NULL; - - surface = glitz_surface_create (drawable, gformat, - width <= 0 ? 1 : width, - height <= 0 ? 1 : height, - 0, NULL); - - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - crsurface = cairo_glitz_surface_create (surface); - - glitz_surface_destroy (surface); - - return crsurface; -} - -static cairo_status_t -_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, - cairo_rectangle_int_t *interest, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *rect_out) -{ - cairo_image_surface_t *image; - cairo_rectangle_int_t extents; - cairo_format_t format; - cairo_format_masks_t masks; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - - extents.x = 0; - extents.y = 0; - extents.width = glitz_surface_get_width (surface->surface); - extents.height = glitz_surface_get_height (surface->surface); - - if (interest != NULL) { - if (! _cairo_rectangle_intersect (&extents, interest)) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - } - - if (rect_out != NULL) - *rect_out = extents; - - if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) { - if (surface->format->color.red_size > 0) { - if (surface->format->color.alpha_size > 0) - format = CAIRO_FORMAT_ARGB32; - else - format = CAIRO_FORMAT_RGB24; - } else { - format = CAIRO_FORMAT_A8; - } - } else - format = CAIRO_FORMAT_ARGB32; - - image = (cairo_image_surface_t*) - cairo_image_surface_create (format, extents.width, extents.height); - if (image->base.status) - return image->base.status; - - _pixman_format_to_masks (image->pixman_format, &masks); - pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = masks.bpp; - pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; - pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; - pf.xoffset = 0; - pf.skip_lines = 0; - - /* XXX: we should eventually return images with negative stride, - need to verify that libpixman have no problem with this first. */ - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - - buffer = glitz_buffer_create_for_data (image->data); - if (buffer == NULL) { - cairo_surface_destroy (&image->base); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* clear out the glitz clip; the clip affects glitz_get_pixels */ - if (surface->has_clip) - glitz_surface_set_clip_region (surface->surface, - 0, 0, NULL, 0); - - glitz_get_pixels (surface->surface, - extents.x, extents.y, - extents.width, extents.height, - &pf, - buffer); - - glitz_buffer_destroy (buffer); - - /* restore the clip, if any */ - if (surface->has_clip) { - glitz_surface_set_clip_region (surface->surface, - 0, 0, - surface->clip_boxes, - surface->num_clip_boxes); - } - - *image_out = image; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image, - int src_x, - int src_y, - int width, - int height, - int x_dst, - int y_dst) -{ - cairo_glitz_surface_t *surface = abstract_surface; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - cairo_format_masks_t masks; - char *data; - - _pixman_format_to_masks (image->pixman_format, &masks); - - pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = masks.bpp; - pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; - pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; - pf.xoffset = src_x; - pf.skip_lines = src_y; - - /* check for negative stride */ - if (image->stride < 0) { - pf.bytes_per_line = -image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; - data = (char *) image->data + image->stride * (image->height - 1); - } else { - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - data = (char *) image->data; - } - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - glitz_set_pixels (surface->surface, - x_dst, y_dst, - width, height, - &pf, - buffer); - - glitz_buffer_destroy (buffer); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - *image_extra = NULL; - - return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); -} - -static cairo_surface_t * -_cairo_glitz_surface_snapshot (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_image_surface_t *image; - - status = _cairo_glitz_surface_get_image (surface, NULL, &image, NULL); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - return &image->base; -} - -static void -_cairo_glitz_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_glitz_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_glitz_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, - image_rect_out); - if (status) - return status; - - *image_out = image; - *image_extra = NULL; - - return status; -} - -static void -_cairo_glitz_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_glitz_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _cairo_glitz_surface_set_image (surface, image, 0, 0, - image->width, image->height, - image_rect->x, image_rect->y); - if (status) - status = _cairo_surface_set_error (&surface->base, status); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_glitz_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_glitz_surface_t *surface = abstract_surface; - cairo_glitz_surface_t *clone; - cairo_status_t status; - - if (surface->base.status) - return surface->base.status; - - if (src->backend == surface->base.backend) - { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - else if (_cairo_surface_is_image (src)) - { - cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; - - clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (surface, src->content, - width, height); - if (clone == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (clone->base.status) - return clone->base.status; - - status = _cairo_glitz_surface_set_image (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (status) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_out = &clone->base; - *clone_offset_x = src_x; - *clone_offset_y = src_y; - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static void -_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, - cairo_matrix_t *matrix) -{ - glitz_transform_t transform; - - transform.matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); - transform.matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); - transform.matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); - - transform.matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); - transform.matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); - transform.matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); - - transform.matrix[2][0] = 0; - transform.matrix[2][1] = 0; - transform.matrix[2][2] = _cairo_fixed_16_16_from_double (1); - - glitz_surface_set_transform (surface->surface, &transform); -} - -static cairo_bool_t -_is_supported_operator (cairo_operator_t op) -{ - /* This is really just a if (op < SATURATE), but we use a switch - * so the compiler will warn if we ever add more operators. - */ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - return TRUE; - - default: - ASSERT_NOT_REACHED; - case CAIRO_OPERATOR_SATURATE: - /* nobody likes saturate, expect that it's required to do - * seamless polygons! - */ - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return FALSE; - } -} - -static glitz_operator_t -_glitz_operator (cairo_operator_t op) -{ - switch ((int) op) { - case CAIRO_OPERATOR_CLEAR: - return GLITZ_OPERATOR_CLEAR; - - case CAIRO_OPERATOR_SOURCE: - return GLITZ_OPERATOR_SRC; - case CAIRO_OPERATOR_OVER: - return GLITZ_OPERATOR_OVER; - case CAIRO_OPERATOR_IN: - return GLITZ_OPERATOR_IN; - case CAIRO_OPERATOR_OUT: - return GLITZ_OPERATOR_OUT; - case CAIRO_OPERATOR_ATOP: - return GLITZ_OPERATOR_ATOP; - - case CAIRO_OPERATOR_DEST: - return GLITZ_OPERATOR_DST; - case CAIRO_OPERATOR_DEST_OVER: - return GLITZ_OPERATOR_OVER_REVERSE; - case CAIRO_OPERATOR_DEST_IN: - return GLITZ_OPERATOR_IN_REVERSE; - case CAIRO_OPERATOR_DEST_OUT: - return GLITZ_OPERATOR_OUT_REVERSE; - case CAIRO_OPERATOR_DEST_ATOP: - return GLITZ_OPERATOR_ATOP_REVERSE; - - case CAIRO_OPERATOR_XOR: - return GLITZ_OPERATOR_XOR; - case CAIRO_OPERATOR_ADD: - return GLITZ_OPERATOR_ADD; - - default: - ASSERT_NOT_REACHED; - - /* Something's very broken if this line of code can be reached, so - * we want to return something that would give a noticeably - * incorrect result. The XOR operator seems so rearely desired - * that it should fit the bill here. - */ - return CAIRO_OPERATOR_XOR; - } -} - -#define CAIRO_GLITZ_FEATURE_OK(surface, name) \ - (glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \ - (GLITZ_FEATURE_ ## name ## _MASK)) - -static glitz_status_t -_glitz_ensure_target (glitz_surface_t *surface) -{ - if (!glitz_surface_get_attached_drawable (surface)) - { - glitz_drawable_format_t *target_format, templ; - glitz_format_t *format; - glitz_drawable_t *drawable, *target; - unsigned int width, height; - unsigned long mask; - - drawable = glitz_surface_get_drawable (surface); - format = glitz_surface_get_format (surface); - width = glitz_surface_get_width (surface); - height = glitz_surface_get_height (surface); - - if (format->color.fourcc != GLITZ_FOURCC_RGB) - return CAIRO_INT_STATUS_UNSUPPORTED; - - templ.color = format->color; - templ.depth_size = 0; - templ.stencil_size = 0; - templ.doublebuffer = 0; - templ.samples = 1; - - mask = - GLITZ_FORMAT_RED_SIZE_MASK | - GLITZ_FORMAT_GREEN_SIZE_MASK | - GLITZ_FORMAT_BLUE_SIZE_MASK | - GLITZ_FORMAT_ALPHA_SIZE_MASK | - GLITZ_FORMAT_DEPTH_SIZE_MASK | - GLITZ_FORMAT_STENCIL_SIZE_MASK | - GLITZ_FORMAT_DOUBLEBUFFER_MASK | - GLITZ_FORMAT_SAMPLES_MASK; - - target_format = glitz_find_drawable_format (drawable, mask, &templ, 0); - if (!target_format) - return CAIRO_INT_STATUS_UNSUPPORTED; - - target = glitz_create_drawable (drawable, target_format, - width, height); - if (!target) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glitz_surface_attach (surface, target, - GLITZ_DRAWABLE_BUFFER_FRONT_COLOR); - - glitz_drawable_destroy (target); - } - - return CAIRO_STATUS_SUCCESS; -} - -typedef struct _cairo_glitz_surface_attributes { - cairo_surface_attributes_t base; - - glitz_fill_t fill; - glitz_filter_t filter; - glitz_fixed16_16_t *params; - int n_params; - cairo_bool_t acquired; -} cairo_glitz_surface_attributes_t; - -static cairo_int_status_t -_cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern, - cairo_glitz_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_glitz_surface_t **surface_out, - cairo_glitz_surface_attributes_t *attr) -{ - cairo_glitz_surface_t *src = NULL; - - attr->acquired = FALSE; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - char *data; - glitz_fixed16_16_t *params; - unsigned int n_params; - unsigned int *pixels; - unsigned int i, n_base_params; - glitz_buffer_t *buffer; - static const glitz_pixel_format_t format = { - GLITZ_FOURCC_RGB, - { - 32, - 0xff000000, - 0x00ff0000, - 0x0000ff00, - 0x000000ff - }, - 0, 0, 0, - GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP - }; - - /* XXX: the current color gradient acceleration provided by glitz is - * experimental, it's been proven inappropriate in a number of ways, - * most importantly, it's currently implemented as filters and - * gradients are not filters. eventually, it will be replaced with - * something more appropriate. - */ - - if (gradient->n_stops < 2) - break; - - if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM)) - break; - - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - n_base_params = 6; - else - n_base_params = 4; - - n_params = gradient->n_stops * 3 + n_base_params; - - /* check for int overflow */ - { - int size1, size2; - if (n_params >= INT32_MAX / sizeof (glitz_fixed16_16_t) || - gradient->n_stops >= INT32_MAX / sizeof (unsigned int)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - size1 = n_params * sizeof (glitz_fixed16_16_t); - size2 = gradient->n_stops * sizeof (unsigned int); - - if (size1 >= INT32_MAX - size2) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - data = malloc (size1 + size2); - } - - if (!data) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - params = (glitz_fixed16_16_t *) data; - pixels = (unsigned int *) - (data + sizeof (glitz_fixed16_16_t) * n_params); - - buffer = glitz_buffer_create_for_data (pixels); - if (!buffer) { - free (data); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - src = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - gradient->n_stops, 1); - if (src->base.status) { - glitz_buffer_destroy (buffer); - free (data); - return src->base.status; - } - - for (i = 0; i < gradient->n_stops; i++) - { - pixels[i] = - (((int) (gradient->stops[i].color.alpha_short >> 8)) << 24) | - (((int) (gradient->stops[i].color.red_short >> 8)) << 16) | - (((int) (gradient->stops[i].color.green_short >> 8)) << 8) | - (((int) (gradient->stops[i].color.blue_short >> 8))); - - params[n_base_params + 3 * i + 0] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset); - params[n_base_params + 3 * i + 1] = i << 16; - params[n_base_params + 3 * i + 2] = 0; - } - - glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1, - (glitz_pixel_format_t *)&format, buffer); - - glitz_buffer_destroy (buffer); - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) - { - cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; - - params[0] = _cairo_fixed_to_16_16 (grad->p1.x); - params[1] = _cairo_fixed_to_16_16 (grad->p1.y); - params[2] = _cairo_fixed_to_16_16 (grad->p2.x); - params[3] = _cairo_fixed_to_16_16 (grad->p2.y); - attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; - } - else - { - cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; - - params[0] = _cairo_fixed_to_16_16 (grad->c1.x); - params[1] = _cairo_fixed_to_16_16 (grad->c1.y); - params[2] = _cairo_fixed_to_16_16 (grad->r1); - params[3] = _cairo_fixed_to_16_16 (grad->c2.x); - params[4] = _cairo_fixed_to_16_16 (grad->c2.y); - params[5] = _cairo_fixed_to_16_16 (grad->r2); - attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; - } - - switch (pattern->extend) { - case CAIRO_EXTEND_NONE: - attr->fill = GLITZ_FILL_TRANSPARENT; - break; - case CAIRO_EXTEND_REPEAT: - attr->fill = GLITZ_FILL_REPEAT; - break; - case CAIRO_EXTEND_REFLECT: - attr->fill = GLITZ_FILL_REFLECT; - break; - case CAIRO_EXTEND_PAD: - attr->fill = GLITZ_FILL_NEAREST; - break; - } - - attr->params = params; - attr->n_params = n_params; - attr->base.matrix = pattern->matrix; - attr->base.x_offset = 0; - attr->base.y_offset = 0; - } break; - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_SURFACE: - default: - break; - } - - if (!src) - { - cairo_int_status_t status; - - status = _cairo_pattern_acquire_surface (pattern, &dst->base, - x, y, width, height, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attr->base); - if (status) - return status; - - if (src) - { - switch (attr->base.extend) { - case CAIRO_EXTEND_NONE: - attr->fill = GLITZ_FILL_TRANSPARENT; - break; - case CAIRO_EXTEND_REPEAT: - attr->fill = GLITZ_FILL_REPEAT; - break; - case CAIRO_EXTEND_REFLECT: - attr->fill = GLITZ_FILL_REFLECT; - break; - case CAIRO_EXTEND_PAD: - default: - attr->fill = GLITZ_FILL_NEAREST; - break; - } - - switch (attr->base.filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - attr->filter = GLITZ_FILTER_NEAREST; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - case CAIRO_FILTER_GAUSSIAN: - default: - attr->filter = GLITZ_FILTER_BILINEAR; - break; - } - - attr->params = NULL; - attr->n_params = 0; - attr->acquired = TRUE; - } - } - - *surface_out = src; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_pattern_release_surface (const cairo_pattern_t *pattern, - cairo_glitz_surface_t *surface, - cairo_glitz_surface_attributes_t *attr) -{ - if (attr->acquired) - _cairo_pattern_release_surface (pattern, &surface->base, &attr->base); - else - cairo_surface_destroy (&surface->base); -} - -static cairo_int_status_t -_cairo_glitz_pattern_acquire_surfaces (const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_glitz_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_glitz_surface_t **src_out, - cairo_glitz_surface_t **mask_out, - cairo_glitz_surface_attributes_t *sattr, - cairo_glitz_surface_attributes_t *mattr) -{ - cairo_int_status_t status; - cairo_solid_pattern_t tmp; - - /* If src and mask are both solid, then the mask alpha can be - * combined into src and mask can be ignored. */ - - /* XXX: This optimization assumes that there is no color - * information in mask, so this will need to change when we - * support RENDER-style 4-channel masks. */ - - if (src->type == CAIRO_PATTERN_TYPE_SOLID && - mask->type == CAIRO_PATTERN_TYPE_SOLID) - { - cairo_color_t combined; - cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; - cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; - - combined = src_solid->color; - _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - - _cairo_pattern_init_solid (&tmp, &combined, CAIRO_CONTENT_COLOR_ALPHA); - - mask = NULL; - src = &tmp.base; - } - - status = _cairo_glitz_pattern_acquire_surface (src, dst, - src_x, src_y, - width, height, - src_out, sattr); - - if (src == &tmp.base) - _cairo_pattern_fini (&tmp.base); - - if (status) - return status; - - if (mask) - { - status = _cairo_glitz_pattern_acquire_surface (mask, dst, - mask_x, mask_y, - width, height, - mask_out, mattr); - - if (status) { - /* XXX src == &tmp.base -> invalid (currently inconsequential) */ - _cairo_glitz_pattern_release_surface (src, *src_out, sattr); - } - - return status; - } - else - { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, - cairo_glitz_surface_attributes_t *a) -{ - _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); - glitz_surface_set_fill (surface->surface, a->fill); - glitz_surface_set_filter (surface->surface, a->filter, - a->params, a->n_params); -} - -static cairo_status_t -_cairo_glitz_get_boxes_from_region (cairo_region_t *region, - glitz_box_t **boxes, - int *nboxes) -{ - pixman_box32_t *pboxes; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - int n, i; - - n = 0; - pboxes = pixman_region32_rectangles (®ion->rgn, &n); - if (n == 0) { - *nboxes = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (n > *nboxes) { - *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t)); - if (*boxes == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto done; - } - } - - for (i = 0; i < n; i++) { - (*boxes)[i].x1 = pboxes[i].x1; - (*boxes)[i].y1 = pboxes[i].y1; - (*boxes)[i].x2 = pboxes[i].x2; - (*boxes)[i].y2 = pboxes[i].y2; - } - - *nboxes = n; -done: - return status; -} - -static cairo_status_t -_cairo_glitz_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - if (region == surface->clip_region) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - if (region != NULL) { - cairo_status_t status; - - status = _cairo_glitz_get_boxes_from_region (region, - &surface->clip_boxes, - &surface->num_clip_boxes); - if (status) - return status; - - glitz_surface_set_clip_region (surface->surface, - 0, 0, - surface->clip_boxes, - surface->num_clip_boxes); - surface->has_clip = TRUE; - } else { - glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - surface->has_clip = FALSE; - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_glitz_surface_attributes_t src_attr, mask_attr; - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - cairo_glitz_surface_t *mask; - cairo_int_status_t status; - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, clip_region); - if (status) - return status; - - status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, - dst, - src_x, src_y, - mask_x, mask_y, - width, height, - &src, &mask, - &src_attr, &mask_attr); - if (status) - return status; - - _cairo_glitz_surface_set_attributes (src, &src_attr); - if (mask) - { - _cairo_glitz_surface_set_attributes (mask, &mask_attr); - glitz_composite (_glitz_operator (op), - src->surface, - mask->surface, - dst->surface, - src_x + src_attr.base.x_offset, - src_y + src_attr.base.y_offset, - mask_x + mask_attr.base.x_offset, - mask_y + mask_attr.base.y_offset, - dst_x, dst_y, - width, height); - } - else - { - glitz_composite (_glitz_operator (op), - src->surface, - NULL, - dst->surface, - src_x + src_attr.base.x_offset, - src_y + src_attr.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - status = CAIRO_INT_STATUS_UNSUPPORTED; - - if (status == CAIRO_STATUS_SUCCESS && - ! _cairo_operator_bounded_by_source (op)) - { - int src_width, src_height; - int mask_width, mask_height; - - src_width = glitz_surface_get_width (src->surface); - src_height = glitz_surface_get_height (src->surface); - if (mask) - { - mask_width = glitz_surface_get_width (mask->surface); - mask_height = glitz_surface_get_height (mask->surface); - } - else - { - mask_width = 0; - mask_height = 0; - } - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr.base, - src_width, src_height, - mask ? &mask_attr.base : NULL, - mask_width, mask_height, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); - } - - if (mask) - { - if (mask_attr.n_params) - free (mask_attr.params); - - _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr); - } - - if (src_attr.n_params) - free (src_attr.params); - - _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int n_rects) -{ - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)]; - glitz_rectangle_t *glitz_rects = stack_rects; - glitz_rectangle_t *current_rect; - cairo_status_t status; - int i; - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - if (n_rects > ARRAY_LENGTH (stack_rects)) { - glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t)); - if (glitz_rects == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < n_rects; i++) { - glitz_rects[i].x = rects[i].x; - glitz_rects[i].y = rects[i].y; - glitz_rects[i].width = rects[i].width; - glitz_rects[i].height = rects[i].height; - } - - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: { - glitz_color_t glitz_color; - glitz_format_t *format; - - glitz_color.red = color->red_short; - glitz_color.green = color->green_short; - glitz_color.blue = color->blue_short; - glitz_color.alpha = color->alpha_short; - - /* - * XXX even if the dst surface don't have an alpha channel, the - * above alpha still effect the dst surface because the - * underlying glitz drawable may have an alpha channel. So - * replacing the color with an opaque one is needed. - */ - format = glitz_surface_get_format (dst->surface); - if (format->color.alpha_size == 0) - glitz_color.alpha = 0xffff; - - glitz_set_rectangles (dst->surface, &glitz_color, - glitz_rects, n_rects); - } break; - case CAIRO_OPERATOR_SATURATE: - return CAIRO_INT_STATUS_UNSUPPORTED; - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - default: - if (_glitz_ensure_target (dst->surface)) - { - if (glitz_rects != stack_rects) - free (glitz_rects); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - src = (cairo_glitz_surface_t *) - _cairo_surface_create_similar_solid (&dst->base, - CAIRO_CONTENT_COLOR_ALPHA, - 1, 1, - (cairo_color_t *) color, - FALSE); - if (src == NULL || src->base.status) { - if (glitz_rects != stack_rects) - free (glitz_rects); - return src ? src->base.status : CAIRO_INT_STATUS_UNSUPPORTED; - } - - glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); - - current_rect = glitz_rects; - while (n_rects--) - { - glitz_composite (_glitz_operator (op), - src->surface, - NULL, - dst->surface, - 0, 0, - 0, 0, - current_rect->x, current_rect->y, - current_rect->width, current_rect->height); - current_rect++; - } - - cairo_surface_destroy (&src->base); - break; - } - - if (glitz_rects != stack_rects) - free (glitz_rects); - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_glitz_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 n_traps, - cairo_region_t *clip_region) -{ - cairo_glitz_surface_attributes_t attributes; - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src; - cairo_glitz_surface_t *mask = NULL; - glitz_buffer_t *buffer = NULL; - void *data = NULL; - cairo_int_status_t status; - unsigned short alpha; - pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)]; - pixman_trapezoid_t *pixman_traps = stack_traps; - int i; - - if (antialias != CAIRO_ANTIALIAS_DEFAULT && - antialias != CAIRO_ANTIALIAS_GRAY) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - - /* Convert traps to pixman traps */ - if (n_traps > ARRAY_LENGTH (stack_traps)) { - pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t)); - if (pixman_traps == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < n_traps; i++) { - pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top); - pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom); - pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); - pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); - pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); - pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); - pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); - pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); - pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); - pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); - } - - status = _cairo_glitz_pattern_acquire_surface (pattern, dst, - src_x, src_y, - width, height, - &src, &attributes); - if (status) - goto FAIL; - - alpha = 0xffff; - - if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) { - static const glitz_color_t clear_black = { 0, 0, 0, 0 }; - glitz_color_t color; - glitz_geometry_format_t format; - int n_trap_added; - int offset = 0; - int data_size = 0; - int size = 30 * n_traps; /* just a guess */ - - format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; - format.vertex.type = GLITZ_DATA_TYPE_FLOAT; - format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); - format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; - format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; - format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; - format.vertex.mask.offset = 2 * sizeof (glitz_float_t); - - mask = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_CONTENT_ALPHA, - 2, 1); - if (mask == NULL) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FAIL; - } - if (mask->base.status) { - status = mask->base.status; - goto FAIL; - } - - color.red = color.green = color.blue = color.alpha = 0xffff; - - glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); - glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); - - glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); - glitz_surface_set_filter (mask->surface, - GLITZ_FILTER_BILINEAR, - NULL, 0); - - size *= format.vertex.bytes_per_vertex; - - while (n_traps) { - if (data_size < size) { - void *p; - - data_size = size; - p = realloc (data, data_size); - if (p == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - data = p; - - if (buffer) - glitz_buffer_destroy (buffer); - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - free (data); - goto FAIL; - } - } - - offset += - glitz_add_trapezoids (buffer, - offset, size - offset, - format.vertex.type, mask->surface, - (glitz_trapezoid_t *) pixman_traps, n_traps, - &n_trap_added); - - n_traps -= n_trap_added; - traps += n_trap_added; - size *= 2; - } - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_VERTEX, - &format, buffer); - glitz_set_array (dst->surface, 0, 3, - offset / format.vertex.bytes_per_vertex, - 0, 0); - } else { - cairo_image_surface_t *image; - unsigned char *ptr; - int stride; - - stride = (width + 3) & -4; - data = calloc (stride, height); - if (data == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - /* using negative stride */ - ptr = (unsigned char *) data + stride * (height - 1); - - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (ptr, - CAIRO_FORMAT_A8, - width, height, - -stride); - status = image->base.status; - if (status) { - free (data); - goto FAIL; - } - - pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, - n_traps, (pixman_trapezoid_t *) pixman_traps); - - mask = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (&dst->base, - CAIRO_CONTENT_ALPHA, - width, height); - status = mask->base.status; - if (status) { - free (data); - cairo_surface_destroy (&image->base); - goto FAIL; - } - - status = _cairo_glitz_surface_set_image (mask, image, - 0, 0, width, height, 0, 0); - - cairo_surface_destroy (&image->base); - - if (status) - goto FAIL; - } - - _cairo_glitz_surface_set_attributes (src, &attributes); - - glitz_composite (_glitz_operator (op), - src->surface, - mask->surface, - dst->surface, - src_x + attributes.base.x_offset, - src_y + attributes.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - - if (attributes.n_params) - free (attributes.params); - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_NONE, - NULL, NULL); - - if (buffer) - glitz_buffer_destroy (buffer); - - free (data); - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FAIL; - } - - if (! _cairo_operator_bounded_by_mask (op)) { - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes.base, - glitz_surface_get_width (src->surface), - glitz_surface_get_height (src->surface), - width, height, - src_x, src_y, - 0, 0, - dst_x, dst_y, - width, height, - clip_region); - } - -FAIL: - _cairo_glitz_pattern_release_surface (pattern, src, &attributes); - - if (mask != NULL) - cairo_surface_destroy (&mask->base); - - if (pixman_traps != stack_traps) - free (pixman_traps); - - return status; -} - -static cairo_bool_t -_cairo_glitz_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - rectangle->width = glitz_surface_get_width (surface->surface); - rectangle->height = glitz_surface_get_height (surface->surface); - - return TRUE; -} - -#define CAIRO_GLITZ_AREA_AVAILABLE 0 -#define CAIRO_GLITZ_AREA_DIVIDED 1 -#define CAIRO_GLITZ_AREA_OCCUPIED 2 - -typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t; - -typedef struct _cairo_glitz_area { - int state; - int level; - int x, y; - int width, height; - struct _cairo_glitz_area *area[4]; - cairo_glitz_root_area_t *root; - void *closure; -} cairo_glitz_area_t; - -static cairo_glitz_area_t _empty_area = { - 0, 0, 0, 0, 0, 0, - { NULL, NULL, NULL, NULL }, - NULL, - NULL -}; - -typedef struct _cairo_glitz_area_funcs { - cairo_status_t (*move_in) (cairo_glitz_area_t *area, - void *closure); - - void (*move_out) (cairo_glitz_area_t *area, - void *closure); - - int (*compare_score) (cairo_glitz_area_t *area, - void *closure1, - void *closure2); -} cairo_glitz_area_funcs_t; - -struct _cairo_glitz_root_area { - int max_level; - int width, height; - cairo_glitz_area_t *area; - const cairo_glitz_area_funcs_t *funcs; -}; - -static cairo_status_t -_cairo_glitz_area_move_in (cairo_glitz_area_t *area, - void *closure) -{ - area->closure = closure; - area->state = CAIRO_GLITZ_AREA_OCCUPIED; - - return (*area->root->funcs->move_in) (area, area->closure); -} - -static void -_cairo_glitz_area_move_out (cairo_glitz_area_t *area) -{ - if (area->root) - { - (*area->root->funcs->move_out) (area, area->closure); - - area->closure = NULL; - area->state = CAIRO_GLITZ_AREA_AVAILABLE; - } -} - -static cairo_glitz_area_t * -_cairo_glitz_area_create (cairo_glitz_root_area_t *root, - int level, - int x, - int y, - int width, - int height) -{ - cairo_glitz_area_t *area; - int n = 4; - - area = malloc (sizeof (cairo_glitz_area_t)); - if (!area) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return NULL; - } - - area->level = level; - area->x = x; - area->y = y; - area->width = width; - area->height = height; - area->root = root; - area->closure = NULL; - area->state = CAIRO_GLITZ_AREA_AVAILABLE; - - while (n--) - area->area[n] = NULL; - - return area; -} - -static void -_cairo_glitz_area_destroy (cairo_glitz_area_t *area) -{ - if (area == NULL) - return; - - if (area->state == CAIRO_GLITZ_AREA_OCCUPIED) - { - _cairo_glitz_area_move_out (area); - } - else - { - int n = 4; - - while (n--) - _cairo_glitz_area_destroy (area->area[n]); - } - - free (area); -} - -static cairo_glitz_area_t * -_cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area) -{ - if (!area) - return NULL; - - switch (area->state) { - case CAIRO_GLITZ_AREA_OCCUPIED: - return area; - case CAIRO_GLITZ_AREA_AVAILABLE: - break; - case CAIRO_GLITZ_AREA_DIVIDED: { - cairo_glitz_area_t *tmp, *top = NULL; - int i; - - for (i = 0; i < 4; i++) - { - tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]); - if (tmp && top) - { - if ((*area->root->funcs->compare_score) (tmp, - tmp->closure, - top->closure) > 0) - top = tmp; - } - else if (tmp) - { - top = tmp; - } - } - return top; - } - } - - return NULL; -} - -static cairo_int_status_t -_cairo_glitz_area_find (cairo_glitz_area_t *area, - int width, - int height, - cairo_bool_t kick_out, - void *closure) -{ - cairo_status_t status; - - if (area->width < width || area->height < height) - return CAIRO_INT_STATUS_UNSUPPORTED; - - switch (area->state) { - case CAIRO_GLITZ_AREA_OCCUPIED: - if (kick_out) - { - if ((*area->root->funcs->compare_score) (area, - area->closure, - closure) >= 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_glitz_area_move_out (area); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* fall-through */ - case CAIRO_GLITZ_AREA_AVAILABLE: { - if (area->level == area->root->max_level || - (area->width == width && area->height == height)) - { - return _cairo_glitz_area_move_in (area, closure); - } - else - { - int dx[4], dy[4], w[4], h[4], i; - - dx[0] = dx[2] = dy[0] = dy[1] = 0; - - w[0] = w[2] = dx[1] = dx[3] = width; - h[0] = h[1] = dy[2] = dy[3] = height; - - w[1] = w[3] = area->width - width; - h[2] = h[3] = area->height - height; - - for (i = 0; i < 2; i++) - { - if (w[i]) - area->area[i] = - _cairo_glitz_area_create (area->root, - area->level + 1, - area->x + dx[i], - area->y + dy[i], - w[i], h[i]); - } - - for (; i < 4; i++) - { - if (w[i] && h[i]) - area->area[i] = - _cairo_glitz_area_create (area->root, - area->level + 1, - area->x + dx[i], - area->y + dy[i], - w[i], h[i]); - } - - area->state = CAIRO_GLITZ_AREA_DIVIDED; - - status = _cairo_glitz_area_find (area->area[0], - width, height, - kick_out, closure); - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; - } - } break; - case CAIRO_GLITZ_AREA_DIVIDED: { - cairo_glitz_area_t *to_area; - int i, rejected = FALSE; - - for (i = 0; i < 4; i++) - { - if (area->area[i]) - { - if (area->area[i]->width >= width && - area->area[i]->height >= height) - { - status = _cairo_glitz_area_find (area->area[i], - width, height, - kick_out, closure); - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; - - rejected = TRUE; - } - } - } - - if (rejected) - return CAIRO_INT_STATUS_UNSUPPORTED; - - to_area = _cairo_glitz_area_get_top_scored_sub_area (area); - if (to_area) - { - if (kick_out) - { - if ((*area->root->funcs->compare_score) (to_area, - to_area->closure, - closure) >= 0) - return CAIRO_INT_STATUS_UNSUPPORTED; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - for (i = 0; i < 4; i++) - { - _cairo_glitz_area_destroy (area->area[i]); - area->area[i] = NULL; - } - - area->closure = NULL; - area->state = CAIRO_GLITZ_AREA_AVAILABLE; - - status = _cairo_glitz_area_find (area, width, height, - TRUE, closure); - if (status == CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_SUCCESS; - - } break; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_status_t -_cairo_glitz_root_area_init (cairo_glitz_root_area_t *root, - int max_level, - int width, - int height, - const cairo_glitz_area_funcs_t *funcs) -{ - root->max_level = max_level; - root->funcs = funcs; - - root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height); - if (!root->area) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root) -{ - _cairo_glitz_area_destroy (root->area); -} - -typedef struct _cairo_glitz_surface_font_private { - cairo_glitz_root_area_t root; - glitz_surface_t *surface; -} cairo_glitz_surface_font_private_t; - -typedef struct _cairo_glitz_surface_glyph_private { - cairo_glitz_area_t *area; - cairo_bool_t locked; - cairo_point_double_t p1, p2; -} cairo_glitz_surface_glyph_private_t; - -static cairo_status_t -_cairo_glitz_glyph_move_in (cairo_glitz_area_t *area, - void *closure) -{ - cairo_glitz_surface_glyph_private_t *glyph_private = closure; - - glyph_private->area = area; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_glyph_move_out (cairo_glitz_area_t *area, - void *closure) -{ - cairo_glitz_surface_glyph_private_t *glyph_private = closure; - - glyph_private->area = NULL; -} - -static int -_cairo_glitz_glyph_compare (cairo_glitz_area_t *area, - void *closure1, - void *closure2) -{ - cairo_glitz_surface_glyph_private_t *glyph_private = closure1; - - if (glyph_private->locked) - return 1; - - return -1; -} - -static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = { - _cairo_glitz_glyph_move_in, - _cairo_glitz_glyph_move_out, - _cairo_glitz_glyph_compare -}; - -#define GLYPH_CACHE_TEXTURE_SIZE 512 -#define GLYPH_CACHE_MAX_LEVEL 64 -#define GLYPH_CACHE_MAX_HEIGHT 96 -#define GLYPH_CACHE_MAX_WIDTH 96 - -#define WRITE_VEC2(ptr, _x, _y) \ - *(ptr)++ = (_x); \ - *(ptr)++ = (_y) - -#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \ - WRITE_VEC2 (ptr, _vx1, _vy1); \ - WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \ - WRITE_VEC2 (ptr, _vx2, _vy1); \ - WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \ - WRITE_VEC2 (ptr, _vx2, _vy2); \ - WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \ - WRITE_VEC2 (ptr, _vx1, _vy2); \ - WRITE_VEC2 (ptr, (p1)->x, (p1)->y) - -static cairo_status_t -_cairo_glitz_surface_font_init (cairo_glitz_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_format_t format) -{ - cairo_glitz_surface_font_private_t *font_private; - glitz_drawable_t *drawable; - glitz_format_t *surface_format = NULL; - cairo_int_status_t status; - - drawable = glitz_surface_get_drawable (surface->surface); - - switch (format) { - case CAIRO_FORMAT_A1: - case CAIRO_FORMAT_A8: - surface_format = - glitz_find_standard_format (drawable, GLITZ_STANDARD_A8); - break; - case CAIRO_FORMAT_RGB24: - ASSERT_NOT_REACHED; - break; - case CAIRO_FORMAT_ARGB32: - surface_format = - glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32); - default: - break; - } - - if (!surface_format) - return CAIRO_INT_STATUS_UNSUPPORTED; - - font_private = malloc (sizeof (cairo_glitz_surface_font_private_t)); - if (!font_private) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->surface = glitz_surface_create (drawable, surface_format, - GLYPH_CACHE_TEXTURE_SIZE, - GLYPH_CACHE_TEXTURE_SIZE, - 0, NULL); - if (font_private->surface == NULL) - { - free (font_private); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (format == CAIRO_FORMAT_ARGB32) - glitz_surface_set_component_alpha (font_private->surface, 1); - - status = _cairo_glitz_root_area_init (&font_private->root, - GLYPH_CACHE_MAX_LEVEL, - GLYPH_CACHE_TEXTURE_SIZE, - GLYPH_CACHE_TEXTURE_SIZE, - &_cairo_glitz_area_funcs); - if (status != CAIRO_STATUS_SUCCESS) - { - glitz_surface_destroy (font_private->surface); - free (font_private); - - return status; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = _cairo_glitz_surface_get_backend (); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_glitz_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_glitz_surface_font_private_t *font_private; - - font_private = scaled_font->surface_private; - if (font_private) - { - _cairo_glitz_root_area_fini (&font_private->root); - glitz_surface_destroy (font_private->surface); - free (font_private); - } -} - -static void -_cairo_glitz_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_glitz_surface_glyph_private_t *glyph_private; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private) - { - if (glyph_private->area) - _cairo_glitz_area_move_out (glyph_private->area); - - free (glyph_private); - } -} - -#define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536) - -static cairo_status_t -_cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) -{ - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_glitz_surface_font_private_t *font_private; - cairo_glitz_surface_glyph_private_t *glyph_private; - glitz_point_fixed_t p1, p2; - glitz_pixel_format_t pf; - glitz_buffer_t *buffer; - cairo_format_masks_t masks; - cairo_int_status_t status; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private == NULL) - { - glyph_private = malloc (sizeof (cairo_glitz_surface_glyph_private_t)); - if (!glyph_private) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - glyph_private->area = NULL; - glyph_private->locked = FALSE; - - scaled_glyph->surface_private = (void *) glyph_private; - } - - if (glyph_surface->width > GLYPH_CACHE_MAX_WIDTH || - glyph_surface->height > GLYPH_CACHE_MAX_HEIGHT) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (scaled_font->surface_private == NULL) - { - status = _cairo_glitz_surface_font_init (surface, scaled_font, - glyph_surface->format); - if (status) - return status; - } - - font_private = scaled_font->surface_private; - - if (glyph_surface->width == 0 || glyph_surface->height == 0) - { - glyph_private->area = &_empty_area; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_glitz_area_find (font_private->root.area, - glyph_surface->width, - glyph_surface->height, - FALSE, glyph_private)) - { - if (_cairo_glitz_area_find (font_private->root.area, - glyph_surface->width, - glyph_surface->height, - TRUE, glyph_private)) - return CAIRO_STATUS_SUCCESS; - } - - buffer = glitz_buffer_create_for_data (glyph_surface->data); - if (!buffer) - { - _cairo_glitz_area_move_out (glyph_private->area); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _pixman_format_to_masks (glyph_surface->pixman_format, &masks); - - pf.fourcc = GLITZ_FOURCC_RGB; - pf.masks.bpp = masks.bpp; - pf.masks.alpha_mask = masks.alpha_mask; - pf.masks.red_mask = masks.red_mask; - pf.masks.green_mask = masks.green_mask; - pf.masks.blue_mask = masks.blue_mask; - - pf.bytes_per_line = glyph_surface->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; - pf.xoffset = 0; - pf.skip_lines = 0; - - glitz_set_pixels (font_private->surface, - glyph_private->area->x, - glyph_private->area->y, - glyph_surface->width, - glyph_surface->height, - &pf, buffer); - - glitz_buffer_destroy (buffer); - - p1.x = glyph_private->area->x << 16; - p1.y = glyph_private->area->y << 16; - p2.x = (glyph_private->area->x + glyph_surface->width) << 16; - p2.y = (glyph_private->area->y + glyph_surface->height) << 16; - - glitz_surface_translate_point (font_private->surface, &p1, &p1); - glitz_surface_translate_point (font_private->surface, &p2, &p2); - - glyph_private->p1.x = FIXED_TO_FLOAT (p1.x); - glyph_private->p1.y = FIXED_TO_FLOAT (p1.y); - glyph_private->p2.x = FIXED_TO_FLOAT (p2.x); - glyph_private->p2.y = FIXED_TO_FLOAT (p2.y); - - return CAIRO_STATUS_SUCCESS; -} - -#define N_STACK_BUF 256 - -static cairo_int_status_t -_cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_surface, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region) -{ - cairo_glitz_surface_attributes_t attributes; - cairo_glitz_surface_glyph_private_t *glyph_private; - cairo_glitz_surface_t *dst = abstract_surface; - cairo_glitz_surface_t *src; - cairo_scaled_glyph_t *stack_scaled_glyphs[N_STACK_BUF]; - cairo_scaled_glyph_t **scaled_glyphs; - glitz_float_t stack_vertices[N_STACK_BUF * 16]; - glitz_float_t *vertices; - glitz_buffer_t *buffer; - cairo_int_status_t status; - int x_offset, y_offset; - int i, cached_glyphs = 0; - int remaining_glyps = num_glyphs; - glitz_float_t x1, y1, x2, y2; - static const glitz_vertex_format_t format = { - GLITZ_PRIMITIVE_QUADS, - GLITZ_DATA_TYPE_FLOAT, - sizeof (glitz_float_t) * 4, - GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK, - { 0 }, - { - GLITZ_DATA_TYPE_FLOAT, - GLITZ_COORDINATE_SIZE_XY, - sizeof (glitz_float_t) * 2, - } - }; - - if (scaled_font->surface_backend != NULL && - scaled_font->surface_backend != _cairo_glitz_surface_get_backend ()) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _is_supported_operator (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX Unbounded operators are not handled correctly */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_glitz_surface_set_clip_region (dst, NULL); - if (unlikely (status)) - return status; - - status = _cairo_glitz_pattern_acquire_surface (pattern, dst, - src_x, src_y, - width, height, - &src, &attributes); - if (status) - return status; - - _cairo_glitz_surface_set_attributes (src, &attributes); - - if (num_glyphs > N_STACK_BUF) - { - char *data; - size_t size1, size2; - - if ((size_t)num_glyphs >= INT32_MAX / sizeof(void*) || - (size_t)num_glyphs >= INT32_MAX / sizeof(glitz_float_t) || - ((size_t)num_glyphs * sizeof(glitz_float_t)) >= INT32_MAX / 16) - goto FAIL1; - - size1 = num_glyphs * sizeof(void *); - size2 = num_glyphs * sizeof(glitz_float_t) * 16; - if (size1 >= INT32_MAX - size2) - goto FAIL1; - - data = malloc (size1 + size2); - if (!data) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - goto FAIL1; - } - - scaled_glyphs = (cairo_scaled_glyph_t **) data; - vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *)); - } - else - { - scaled_glyphs = stack_scaled_glyphs; - vertices = stack_vertices; - } - - buffer = glitz_buffer_create_for_data (vertices); - if (!buffer) - goto FAIL2; - - _cairo_scaled_font_freeze_cache (scaled_font); - - for (i = 0; i < num_glyphs; i++) - { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyphs[i]); - if (status != CAIRO_STATUS_SUCCESS) - { - num_glyphs = i; - goto UNLOCK; - } - - glyph_private = scaled_glyphs[i]->surface_private; - if (!glyph_private || !glyph_private->area) - { - status = _cairo_glitz_surface_add_glyph (dst, - scaled_font, - scaled_glyphs[i]); - if (status != CAIRO_STATUS_SUCCESS) { - num_glyphs = i; - goto UNLOCK; - } - } - glyph_private = scaled_glyphs[i]->surface_private; - if (glyph_private && glyph_private->area) - { - remaining_glyps--; - - if (glyph_private->area->width) - { - x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; - y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; - - x1 = _cairo_lround (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); - x2 = x1 + glyph_private->area->width; - y2 = y1 + glyph_private->area->height; - - WRITE_BOX (vertices, x1, y1, x2, y2, - &glyph_private->p1, &glyph_private->p2); - - glyph_private->locked = TRUE; - - cached_glyphs++; - } - } - } - - if (remaining_glyps) - { - cairo_surface_t *image; - cairo_glitz_surface_t *clone; - - for (i = 0; i < num_glyphs; i++) - { - glyph_private = scaled_glyphs[i]->surface_private; - if (!glyph_private || !glyph_private->area) - { - int glyph_width, glyph_height; - int clone_offset_x, clone_offset_y; - - image = &scaled_glyphs[i]->surface->base; - glyph_width = scaled_glyphs[i]->surface->width; - glyph_height = scaled_glyphs[i]->surface->height; - status = - _cairo_glitz_surface_clone_similar (abstract_surface, - image, - 0, - 0, - glyph_width, - glyph_height, - &clone_offset_x, - &clone_offset_y, - (cairo_surface_t **) - &clone); - if (status) - goto UNLOCK; - - assert (clone_offset_x == 0); - assert (clone_offset_y == 0); - - x_offset = scaled_glyphs[i]->surface->base.device_transform.x0; - y_offset = scaled_glyphs[i]->surface->base.device_transform.y0; - x1 = _cairo_lround (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); - - glitz_composite (_glitz_operator (op), - src->surface, - clone->surface, - dst->surface, - src_x + attributes.base.x_offset + x1, - src_y + attributes.base.y_offset + y1, - 0, 0, - x1, y1, - glyph_width, - glyph_height); - - cairo_surface_destroy (&clone->base); - - if (glitz_surface_get_status (dst->surface) == - GLITZ_STATUS_NOT_SUPPORTED) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto UNLOCK; - } - } - } - } - - if (cached_glyphs) - { - cairo_glitz_surface_font_private_t *font_private; - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_VERTEX, - (glitz_geometry_format_t *) &format, - buffer); - - glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0); - - font_private = scaled_font->surface_private; - - glitz_composite (_glitz_operator (op), - src->surface, - font_private->surface, - dst->surface, - src_x + attributes.base.x_offset, - src_y + attributes.base.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - - glitz_set_geometry (dst->surface, - GLITZ_GEOMETRY_TYPE_NONE, - NULL, NULL); - } - -UNLOCK: - if (cached_glyphs) - { - for (i = 0; i < num_glyphs; i++) - { - glyph_private = scaled_glyphs[i]->surface_private; - if (glyph_private) - glyph_private->locked = FALSE; - } - } - - _cairo_scaled_font_thaw_cache (scaled_font); - - glitz_buffer_destroy (buffer); - - FAIL2: - if (num_glyphs > N_STACK_BUF) - free (scaled_glyphs); - - FAIL1: - if (attributes.n_params) - free (attributes.params); - - _cairo_glitz_pattern_release_surface (pattern, src, &attributes); - - if (status) - return status; - - if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_glitz_surface_flush (void *abstract_surface) -{ - cairo_glitz_surface_t *surface = abstract_surface; - - glitz_surface_flush (surface->surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_glitz_surface_is_similar (void *surface_a, - void *surface_b, - cairo_content_t content) -{ - cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a; - cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b; - - glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface); - glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface); - - /* XXX Disable caching of glitz surfaces by the solid pattern cache. - * Until glitz has a mechanism for releasing resources on connection - * closure, we will attempt to access invalid pointers when evicting - * old surfaces from the solid pattern cache. - */ - return FALSE; - - return drawable_a == drawable_b; -} - -static const cairo_surface_backend_t cairo_glitz_surface_backend = { - CAIRO_SURFACE_TYPE_GLITZ, - _cairo_glitz_surface_create_similar, - _cairo_glitz_surface_finish, - _cairo_glitz_surface_acquire_source_image, - _cairo_glitz_surface_release_source_image, - - _cairo_glitz_surface_acquire_dest_image, - _cairo_glitz_surface_release_dest_image, - _cairo_glitz_surface_clone_similar, - _cairo_glitz_surface_composite, - _cairo_glitz_surface_fill_rectangles, - _cairo_glitz_surface_composite_trapezoids, - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_glitz_surface_get_extents, - _cairo_glitz_surface_old_show_glyphs, - NULL, /* get_font_options */ - _cairo_glitz_surface_flush, - NULL, /* mark_dirty_rectangle */ - _cairo_glitz_surface_scaled_font_fini, - _cairo_glitz_surface_scaled_glyph_fini, - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ - - _cairo_glitz_surface_snapshot, - _cairo_glitz_surface_is_similar, -}; - -static const cairo_surface_backend_t * -_cairo_glitz_surface_get_backend (void) -{ - return &cairo_glitz_surface_backend; -} - -static cairo_content_t -_glitz_format_to_content (glitz_format_t * format) -{ - assert (format->color.fourcc == GLITZ_FOURCC_RGB); - - if (format->color.alpha_size != 0) { - if (format->color.red_size != 0 && - format->color.green_size != 0 && - format->color.blue_size != 0) - return CAIRO_CONTENT_COLOR_ALPHA; - else - return CAIRO_CONTENT_ALPHA; - } - return CAIRO_CONTENT_COLOR; -} - -cairo_surface_t * -cairo_glitz_surface_create (glitz_surface_t *surface) -{ - cairo_glitz_surface_t *crsurface; - glitz_format_t *format; - - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - - crsurface = malloc (sizeof (cairo_glitz_surface_t)); - if (crsurface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - format = glitz_surface_get_format (surface); - _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend, - _glitz_format_to_content (format)); - - glitz_surface_reference (surface); - - crsurface->surface = surface; - crsurface->format = format; - - crsurface->has_clip = FALSE; - crsurface->clip_boxes = NULL; - crsurface->num_clip_boxes = 0; - crsurface->clip_region = NULL; - - return &crsurface->base; -} -slim_hidden_def (cairo_glitz_surface_create); diff --git a/gfx/cairo/cairo/src/cairo-glx-context.c b/gfx/cairo/cairo/src/cairo-glx-context.c index fa9d8be962e6..66f5a0d1b451 100644 --- a/gfx/cairo/cairo/src/cairo-glx-context.c +++ b/gfx/cairo/cairo/src/cairo-glx-context.c @@ -52,6 +52,11 @@ typedef struct _cairo_glx_context { 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 { @@ -60,19 +65,50 @@ typedef struct _cairo_glx_surface { 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; + GLXDrawable current_drawable = _glx_get_current_drawable (ctx); - if (ctx->base.current_target == NULL || - _cairo_gl_surface_is_texture (ctx->base.current_target)) { - current_drawable = ctx->dummy_window; - } else { - cairo_glx_surface_t *surface = (cairo_glx_surface_t *) ctx->base.current_target; - current_drawable = surface->win; - } + _glx_query_current_state (ctx); + if (!_context_acquisition_changed_glx_state (ctx, current_drawable)) + return; glXMakeCurrent (ctx->display, current_drawable, ctx->context); } @@ -92,6 +128,12 @@ _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); } @@ -113,11 +155,11 @@ _glx_destroy (void *abstract_ctx) if (ctx->dummy_window != None) XDestroyWindow (ctx->display, ctx->dummy_window); - glXMakeCurrent (ctx->display, 0, 0); + glXMakeCurrent (ctx->display, None, None); } static cairo_status_t -_glx_dummy_ctx (Display *dpy, GLXContext gl_ctx, Window *dummy) +_glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy) { int attr[3] = { GLX_FBCONFIG_ID, 0, None }; GLXFBConfig *config; @@ -174,15 +216,22 @@ cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) cairo_glx_context_t *ctx; cairo_status_t status; Window dummy = None; - - status = _glx_dummy_ctx (dpy, gl_ctx, &dummy); - if (unlikely (status)) - return _cairo_gl_context_create_in_error (status); + 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; @@ -193,12 +242,24 @@ cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) 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; @@ -248,6 +309,9 @@ cairo_gl_surface_create_for_window (cairo_device_t *device, 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)); diff --git a/gfx/cairo/cairo/src/cairo-gstate-private.h b/gfx/cairo/cairo/src/cairo-gstate-private.h index b41c7a296c31..198c6699855c 100644 --- a/gfx/cairo/cairo/src/cairo-gstate-private.h +++ b/gfx/cairo/cairo/src/cairo-gstate-private.h @@ -41,6 +41,7 @@ struct _cairo_gstate { cairo_operator_t op; + double opacity; double tolerance; cairo_antialias_t antialias; @@ -54,7 +55,7 @@ struct _cairo_gstate { cairo_matrix_t font_matrix; cairo_font_options_t font_options; - cairo_clip_t clip; + cairo_clip_t *clip; cairo_surface_t *target; /* The target to which all rendering is directed */ cairo_surface_t *parent_target; /* The previous target which was receiving rendering */ @@ -88,7 +89,7 @@ cairo_private cairo_status_t _cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist); cairo_private cairo_bool_t -_cairo_gstate_is_redirected (cairo_gstate_t *gstate); +_cairo_gstate_is_group (cairo_gstate_t *gstate); cairo_private cairo_status_t _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child); @@ -96,9 +97,6 @@ _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child); cairo_private cairo_surface_t * _cairo_gstate_get_target (cairo_gstate_t *gstate); -cairo_private cairo_surface_t * -_cairo_gstate_get_parent_target (cairo_gstate_t *gstate); - cairo_private cairo_surface_t * _cairo_gstate_get_original_target (cairo_gstate_t *gstate); @@ -117,6 +115,12 @@ _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op); cairo_private cairo_operator_t _cairo_gstate_get_operator (cairo_gstate_t *gstate); +cairo_private cairo_status_t +_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double opacity); + +cairo_private double +_cairo_gstate_get_opacity (cairo_gstate_t *gstate); + cairo_private cairo_status_t _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance); @@ -204,6 +208,16 @@ _cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) _do_cairo_gstate_user_to_backend (gstate, x, y); } +cairo_private void +_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y); + +static inline void +_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y) +{ + if (! gstate->is_identity) + _do_cairo_gstate_user_to_backend_distance (gstate, x, y); +} + cairo_private void _do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y); @@ -214,6 +228,16 @@ _cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) _do_cairo_gstate_backend_to_user (gstate, x, y); } +cairo_private void +_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y); + +static inline void +_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y) +{ + if (! gstate->is_identity) + _do_cairo_gstate_backend_to_user_distance (gstate, x, y); +} + cairo_private void _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, double *x1, double *y1, @@ -300,10 +324,13 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, double height); cairo_private cairo_status_t -_cairo_gstate_select_font_face (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); +_cairo_gstate_tag_begin (cairo_gstate_t *gstate, + const char *tag_name, + const char *attributes); + +cairo_private cairo_status_t +_cairo_gstate_tag_end (cairo_gstate_t *gstate, + const char *tag_name); cairo_private cairo_status_t _cairo_gstate_set_font_size (cairo_gstate_t *gstate, @@ -341,18 +368,6 @@ cairo_private cairo_status_t _cairo_gstate_set_font_face (cairo_gstate_t *gstate, cairo_font_face_t *font_face); -cairo_private cairo_status_t -_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags); - cairo_private cairo_status_t _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, @@ -361,13 +376,9 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, cairo_private cairo_status_t _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, - const char *utf8, - int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags); + cairo_glyph_text_info_t *info); cairo_private cairo_status_t _cairo_gstate_glyph_path (cairo_gstate_t *gstate, diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c index cb07b511f2b9..ff0cb8be734e 100644 --- a/gfx/cairo/cairo/src/cairo-gstate.c +++ b/gfx/cairo/cairo/src/cairo-gstate.c @@ -37,15 +37,13 @@ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" +#include "cairo-list-inline.h" #include "cairo-gstate-private.h" - -#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) -#define ISFINITE(x) isfinite (x) -#else -#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ -#endif +#include "cairo-pattern-private.h" +#include "cairo-traps-private.h" static cairo_status_t _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other); @@ -59,7 +57,7 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); static void _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate); -static cairo_status_t +static void _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, @@ -86,13 +84,12 @@ cairo_status_t _cairo_gstate_init (cairo_gstate_t *gstate, cairo_surface_t *target) { - cairo_status_t status; - VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); gstate->next = NULL; gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT; + gstate->opacity = 1.; gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; gstate->antialias = CAIRO_ANTIALIAS_DEFAULT; @@ -111,7 +108,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, _cairo_font_options_init_default (&gstate->font_options); - _cairo_clip_init (&gstate->clip); + gstate->clip = NULL; gstate->target = cairo_surface_reference (target); gstate->parent_target = NULL; @@ -131,15 +128,7 @@ _cairo_gstate_init (cairo_gstate_t *gstate, /* Now that the gstate is fully initialized and ready for the eventual * _cairo_gstate_fini(), we can check for errors (and not worry about * the resource deallocation). */ - status = target->status; - if (unlikely (status)) - return status; - - status = gstate->source->status; - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; + return target->status; } /** @@ -157,6 +146,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); gstate->op = other->op; + gstate->opacity = other->opacity; gstate->tolerance = other->tolerance; gstate->antialias = other->antialias; @@ -176,7 +166,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); - _cairo_clip_init_copy (&gstate->clip, &other->clip); + gstate->clip = _cairo_clip_copy (other->clip); gstate->target = cairo_surface_reference (other->target); /* parent_target is always set to NULL; it's only ever set by redirect_target */ @@ -213,7 +203,7 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_scaled_font_destroy (gstate->scaled_font); gstate->scaled_font = NULL; - _cairo_clip_reset (&gstate->clip); + _cairo_clip_destroy (gstate->clip); cairo_list_del (&gstate->device_transform_observer.link); @@ -229,7 +219,7 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_pattern_destroy (gstate->source); gstate->source = NULL; - VG (VALGRIND_MAKE_MEM_NOACCESS (gstate, sizeof (cairo_gstate_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); } /** @@ -251,7 +241,7 @@ _cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) top = *freelist; if (top == NULL) { - top = malloc (sizeof (cairo_gstate_t)); + top = _cairo_malloc (sizeof (cairo_gstate_t)); if (unlikely (top == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else @@ -303,16 +293,10 @@ _cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) * Redirect @gstate rendering to a "child" target. The original * "parent" target with which the gstate was created will not be * affected. See _cairo_gstate_get_target(). - * - * Unless the redirected target has the same device offsets as the - * original #cairo_t target, the clip will be INVALID after this call, - * and the caller should either recreate or reset the clip. **/ cairo_status_t _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) { - cairo_matrix_t matrix; - /* If this gstate is already redirected, this is an error; we need a * new gstate to be able to redirect */ assert (gstate->parent_target == NULL); @@ -320,7 +304,6 @@ _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) /* Set up our new parent_target based on our current target; * gstate->parent_target will take the ref that is held by gstate->target */ - cairo_surface_destroy (gstate->parent_target); gstate->parent_target = gstate->target; /* Now set up our new target; we overwrite gstate->target directly, @@ -332,28 +315,28 @@ _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) /* The clip is in surface backend coordinates for the previous target; * translate it into the child's backend coordinates. */ - cairo_matrix_init_translate (&matrix, - child->device_transform.x0 - gstate->parent_target->device_transform.x0, - child->device_transform.y0 - gstate->parent_target->device_transform.y0); - _cairo_clip_reset (&gstate->clip); - return _cairo_clip_init_copy_transformed (&gstate->clip, - &gstate->next->clip, - &matrix); + _cairo_clip_destroy (gstate->clip); + gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip, + child->device_transform.x0 - gstate->parent_target->device_transform.x0, + child->device_transform.y0 - gstate->parent_target->device_transform.y0); + + return CAIRO_STATUS_SUCCESS; } /** - * _cairo_gstate_is_redirected + * _cairo_gstate_is_group: * @gstate: a #cairo_gstate_t * - * This space left intentionally blank. + * Check if _cairo_gstate_redirect_target has been called on the head + * of the stack. * - * Return value: %TRUE if the gstate is redirected to a target - * different than the original, %FALSE otherwise. + * Return value: %TRUE if @gstate is redirected to a target different + * than the previous state in the stack, %FALSE otherwise. **/ cairo_bool_t -_cairo_gstate_is_redirected (cairo_gstate_t *gstate) +_cairo_gstate_is_group (cairo_gstate_t *gstate) { - return (gstate->target != gstate->original_target); + return gstate->parent_target != NULL; } /** @@ -371,19 +354,6 @@ _cairo_gstate_get_target (cairo_gstate_t *gstate) return gstate->target; } -/** - * _cairo_gstate_get_parent_target: - * @gstate: a #cairo_gstate_t - * - * Return the parent surface of the current drawing target surface; - * if this particular gstate isn't a redirect gstate, this will return %NULL. - **/ -cairo_surface_t * -_cairo_gstate_get_parent_target (cairo_gstate_t *gstate) -{ - return gstate->parent_target; -} - /** * _cairo_gstate_get_original_target: * @gstate: a #cairo_gstate_t @@ -408,11 +378,11 @@ _cairo_gstate_get_original_target (cairo_gstate_t *gstate) * This space left intentionally blank. * * Return value: a pointer to the gstate's #cairo_clip_t structure. - */ + **/ cairo_clip_t * _cairo_gstate_get_clip (cairo_gstate_t *gstate) { - return &gstate->clip; + return gstate->clip; } cairo_status_t @@ -455,6 +425,20 @@ _cairo_gstate_get_operator (cairo_gstate_t *gstate) return gstate->op; } +cairo_status_t +_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double op) +{ + gstate->opacity = op; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_opacity (cairo_gstate_t *gstate) +{ + return gstate->opacity; +} + cairo_status_t _cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) { @@ -528,11 +512,10 @@ _cairo_gstate_get_line_join (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset) { - unsigned int i; - double dash_total; + double dash_total, on_total, off_total; + int i, j; - if (gstate->stroke_style.dash) - free (gstate->stroke_style.dash); + free (gstate->stroke_style.dash); gstate->stroke_style.num_dashes = num_dashes; @@ -548,14 +531,27 @@ _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dash return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - memcpy (gstate->stroke_style.dash, dash, gstate->stroke_style.num_dashes * sizeof (double)); - - dash_total = 0.0; - for (i = 0; i < gstate->stroke_style.num_dashes; i++) { - if (gstate->stroke_style.dash[i] < 0) + on_total = off_total = dash_total = 0.0; + for (i = j = 0; i < num_dashes; i++) { + if (dash[i] < 0) return _cairo_error (CAIRO_STATUS_INVALID_DASH); - dash_total += gstate->stroke_style.dash[i]; + if (dash[i] == 0 && i > 0 && i < num_dashes - 1) { + if (dash[++i] < 0) + return _cairo_error (CAIRO_STATUS_INVALID_DASH); + + gstate->stroke_style.dash[j-1] += dash[i]; + gstate->stroke_style.num_dashes -= 2; + } else + gstate->stroke_style.dash[j++] = dash[i]; + + if (dash[i]) { + dash_total += dash[i]; + if ((i & 1) == 0) + on_total += dash[i]; + else + off_total += dash[i]; + } } if (dash_total == 0.0) @@ -563,8 +559,19 @@ _cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dash /* An odd dash value indicate symmetric repeating, so the total * is twice as long. */ - if (gstate->stroke_style.num_dashes & 1) + if (gstate->stroke_style.num_dashes & 1) { dash_total *= 2; + on_total += off_total; + } + + if (dash_total - on_total < CAIRO_FIXED_ERROR_DOUBLE) { + /* Degenerate dash -> solid line */ + free (gstate->stroke_style.dash); + gstate->stroke_style.dash = NULL; + gstate->stroke_style.num_dashes = 0; + gstate->stroke_style.dash_offset = 0.0; + return CAIRO_STATUS_SUCCESS; + } /* The dashing code doesn't like a negative offset or a big positive * offset, so we compute an equivalent offset which is guaranteed to be @@ -800,6 +807,13 @@ _do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) cairo_matrix_transform_point (&gstate->target->device_transform, x, y); } +void +_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_distance (&gstate->ctm, x, y); + cairo_matrix_transform_distance (&gstate->target->device_transform, x, y); +} + void _do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) { @@ -807,6 +821,13 @@ _do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); } +void +_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_distance (&gstate->target->device_transform_inverse, x, y); + cairo_matrix_transform_distance (&gstate->ctm_inverse, x, y); +} + void _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, double *x1, double *y1, @@ -815,11 +836,21 @@ _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, { cairo_matrix_t matrix_inverse; - cairo_matrix_multiply (&matrix_inverse, - &gstate->target->device_transform_inverse, - &gstate->ctm_inverse); - _cairo_matrix_transform_bounding_box (&matrix_inverse, - x1, y1, x2, y2, is_tight); + if (! _cairo_matrix_is_identity (&gstate->target->device_transform_inverse) || + ! _cairo_matrix_is_identity (&gstate->ctm_inverse)) + { + cairo_matrix_multiply (&matrix_inverse, + &gstate->target->device_transform_inverse, + &gstate->ctm_inverse); + _cairo_matrix_transform_bounding_box (&matrix_inverse, + x1, y1, x2, y2, is_tight); + } + + else + { + if (is_tight) + *is_tight = TRUE; + } } /* XXX: NYI @@ -917,7 +948,7 @@ _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, surface = surface_pattern->surface; if (_cairo_surface_has_device_transform (surface)) - _cairo_pattern_transform (pattern, &surface->device_transform); + _cairo_pattern_pretransform (pattern, &surface->device_transform); } if (! _cairo_matrix_is_identity (ctm_inverse)) @@ -948,39 +979,6 @@ _cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, &gstate->ctm_inverse); } -/* We need to take a copy of the clip so that the lower layers may modify it - * by, perhaps, intersecting it with the operation extents and other paths. - */ -#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip) - -static cairo_bool_t -_clipped (cairo_gstate_t *gstate) -{ - cairo_rectangle_int_t extents; - - if (gstate->clip.all_clipped) - return TRUE; - - /* XXX consider applying a surface clip? */ - - if (gstate->clip.path == NULL) - return FALSE; - - if (_cairo_surface_get_extents (gstate->target, &extents)) { - if (extents.width == 0 || extents.height == 0) - return TRUE; - - if (! _cairo_rectangle_intersect (&extents, - &gstate->clip.path->extents)) - { - return TRUE; - } - } - - /* perform a simple query to exclude trivial all-clipped cases */ - return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO; -} - static cairo_operator_t _reduce_op (cairo_gstate_t *gstate) { @@ -1020,22 +1018,36 @@ _reduce_op (cairo_gstate_t *gstate) return op; } +static cairo_status_t +_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern) +{ + if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH && + ((const cairo_mesh_pattern_t *) pattern)->current_patch)) + { + /* If current patch != NULL, the pattern is under construction + * and cannot be used as a source */ + return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION; + } + + return pattern->status; +} + cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate) { cairo_pattern_union_t source_pattern; const cairo_pattern_t *pattern; - cairo_clip_t clip; cairo_status_t status; cairo_operator_t op; - if (unlikely (gstate->source->status)) - return gstate->source->status; + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; - if (_clipped (gstate)) + if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; op = _reduce_op (gstate); @@ -1046,12 +1058,9 @@ _cairo_gstate_paint (cairo_gstate_t *gstate) pattern = &source_pattern.base; } - status = _cairo_surface_paint (gstate->target, - op, pattern, - _gstate_get_clip (gstate, &clip)); - _cairo_clip_fini (&clip); - - return status; + return _cairo_surface_paint (gstate->target, + op, pattern, + gstate->clip); } cairo_status_t @@ -1061,21 +1070,24 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, cairo_pattern_union_t source_pattern, mask_pattern; const cairo_pattern_t *source; cairo_operator_t op; - cairo_clip_t clip; cairo_status_t status; - if (unlikely (mask->status)) - return mask->status; + status = _cairo_gstate_get_pattern_status (mask); + if (unlikely (status)) + return status; - if (unlikely (gstate->source->status)) - return gstate->source->status; + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; - if (_clipped (gstate)) + if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; + assert (gstate->opacity == 1.0); + if (_cairo_pattern_is_opaque (mask, NULL)) return _cairo_gstate_paint (gstate); @@ -1095,7 +1107,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 && - mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID && + mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && _cairo_operator_bounded_by_source (op)) { const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; @@ -1117,16 +1129,15 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, status = _cairo_surface_paint (gstate->target, op, &source_pattern.base, - _gstate_get_clip (gstate, &clip)); + gstate->clip); } else { status = _cairo_surface_mask (gstate->target, op, source, &mask_pattern.base, - _gstate_get_clip (gstate, &clip)); + gstate->clip); } - _cairo_clip_fini (&clip); return status; } @@ -1137,11 +1148,13 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) cairo_pattern_union_t source_pattern; cairo_stroke_style_t style; double dash[2]; - cairo_clip_t clip; cairo_status_t status; + cairo_matrix_t aggregate_transform; + cairo_matrix_t aggregate_transform_inverse; - if (unlikely (gstate->source->status)) - return gstate->source->status; + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; @@ -1149,11 +1162,20 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) if (gstate->stroke_style.line_width <= 0.0) return CAIRO_STATUS_SUCCESS; - if (_clipped (gstate)) + if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; + assert (gstate->opacity == 1.0); + + cairo_matrix_multiply (&aggregate_transform, + &gstate->ctm, + &gstate->target->device_transform); + cairo_matrix_multiply (&aggregate_transform_inverse, + &gstate->target->device_transform_inverse, + &gstate->ctm_inverse); + memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style)); - if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) { + if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &aggregate_transform, gstate->tolerance)) { style.dash = dash; _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance, &style.dash_offset, @@ -1163,19 +1185,16 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); - status = _cairo_surface_stroke (gstate->target, - gstate->op, - &source_pattern.base, - path, - &style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - gstate->antialias, - _gstate_get_clip (gstate, &clip)); - _cairo_clip_fini (&clip); - - return status; + return _cairo_surface_stroke (gstate->target, + gstate->op, + &source_pattern.base, + path, + &style, + &aggregate_transform, + &aggregate_transform_inverse, + gstate->tolerance, + gstate->antialias, + gstate->clip); } cairo_status_t @@ -1203,6 +1222,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, _cairo_path_fixed_approximate_stroke_extents (path, &gstate->stroke_style, &gstate->ctm, + gstate->target->is_vector, &extents); if (x < extents.x || x > extents.x + extents.width || y < extents.y || y > extents.y + extents.height) @@ -1211,20 +1231,20 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate, return CAIRO_STATUS_SUCCESS; } - limit.p1.x = _cairo_fixed_from_double (x) - 5; - limit.p1.y = _cairo_fixed_from_double (y) - 5; - limit.p2.x = limit.p1.x + 10; - limit.p2.y = limit.p1.y + 10; + limit.p1.x = _cairo_fixed_from_double (x) - 1; + limit.p1.y = _cairo_fixed_from_double (y) - 1; + limit.p2.x = limit.p1.x + 2; + limit.p2.y = limit.p1.y + 2; _cairo_traps_init (&traps); _cairo_traps_limit (&traps, &limit, 1); - status = _cairo_path_fixed_stroke_to_traps (path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - &traps); + status = _cairo_path_fixed_stroke_polygon_to_traps (path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + &traps); if (unlikely (status)) goto BAIL; @@ -1239,18 +1259,20 @@ BAIL: cairo_status_t _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { - cairo_clip_t clip; cairo_status_t status; - if (unlikely (gstate->source->status)) - return gstate->source->status; + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; - if (_clipped (gstate)) + if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; + assert (gstate->opacity == 1.0); + if (_cairo_path_fixed_fill_is_empty (path)) { if (_cairo_operator_bounded_by_mask (gstate->op)) return CAIRO_STATUS_SUCCESS; @@ -1258,7 +1280,7 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) status = _cairo_surface_paint (gstate->target, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, - _gstate_get_clip (gstate, &clip)); + gstate->clip); } else { cairo_pattern_union_t source_pattern; const cairo_pattern_t *pattern; @@ -1283,7 +1305,7 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height)) { status = _cairo_surface_paint (gstate->target, op, pattern, - _gstate_get_clip (gstate, &clip)); + gstate->clip); } else { @@ -1292,12 +1314,10 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) gstate->fill_rule, gstate->tolerance, gstate->antialias, - _gstate_get_clip (gstate, &clip)); + gstate->clip); } } - _cairo_clip_fini (&clip); - return status; } @@ -1320,32 +1340,49 @@ _cairo_gstate_in_clip (cairo_gstate_t *gstate, double x, double y) { - cairo_clip_path_t *clip_path; + cairo_clip_t *clip = gstate->clip; + int i; - if (gstate->clip.all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return FALSE; - clip_path = gstate->clip.path; - if (clip_path == NULL) + if (clip == NULL) return TRUE; _cairo_gstate_user_to_backend (gstate, &x, &y); - if (x < clip_path->extents.x || - x >= clip_path->extents.x + clip_path->extents.width || - y < clip_path->extents.y || - y >= clip_path->extents.y + clip_path->extents.height) + if (x < clip->extents.x || + x >= clip->extents.x + clip->extents.width || + y < clip->extents.y || + y >= clip->extents.y + clip->extents.height) { return FALSE; } - do { - if (! _cairo_path_fixed_in_fill (&clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - x, y)) + if (clip->num_boxes) { + int fx, fy; + + fx = _cairo_fixed_from_double (x); + fy = _cairo_fixed_from_double (y); + for (i = 0; i < clip->num_boxes; i++) { + if (fx >= clip->boxes[i].p1.x && fx <= clip->boxes[i].p2.x && + fy >= clip->boxes[i].p1.y && fy <= clip->boxes[i].p2.y) + break; + } + if (i == clip->num_boxes) return FALSE; - } while ((clip_path = clip_path->prev) != NULL); + } + + if (clip->path) { + cairo_clip_path_t *clip_path = clip->path; + do { + if (! _cairo_path_fixed_in_fill (&clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + x, y)) + return FALSE; + } while ((clip_path = clip_path->prev) != NULL); + } return TRUE; } @@ -1365,45 +1402,29 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate) } static void -_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t *gstate, - cairo_traps_t *traps, - double *x1, double *y1, - double *x2, double *y2) +_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t *gstate, + const cairo_box_t *extents, + double *x1, double *y1, + double *x2, double *y2) { - cairo_box_t extents; + double px1, py1, px2, py2; - if (traps->num_traps == 0) { - /* no traps, so we actually won't draw anything */ - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - } else { - double px1, py1, px2, py2; + px1 = _cairo_fixed_to_double (extents->p1.x); + py1 = _cairo_fixed_to_double (extents->p1.y); + px2 = _cairo_fixed_to_double (extents->p2.x); + py2 = _cairo_fixed_to_double (extents->p2.y); - _cairo_traps_extents (traps, &extents); - - px1 = _cairo_fixed_to_double (extents.p1.x); - py1 = _cairo_fixed_to_double (extents.p1.y); - px2 = _cairo_fixed_to_double (extents.p2.x); - py2 = _cairo_fixed_to_double (extents.p2.y); - - _cairo_gstate_backend_to_user_rectangle (gstate, - &px1, &py1, &px2, &py2, - NULL); - if (x1) - *x1 = px1; - if (y1) - *y1 = py1; - if (x2) - *x2 = px2; - if (y2) - *y2 = py2; - } + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + if (x1) + *x1 = px1; + if (y1) + *y1 = py1; + if (x2) + *x2 = px2; + if (y2) + *y2 = py2; } cairo_status_t @@ -1412,35 +1433,57 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, double *x1, double *y1, double *x2, double *y2) { - cairo_status_t status; - cairo_traps_t traps; + cairo_int_status_t status; + cairo_box_t extents; + cairo_bool_t empty; - if (gstate->stroke_style.line_width <= 0.0) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + if (gstate->stroke_style.line_width <= 0.0) return CAIRO_STATUS_SUCCESS; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + &gstate->stroke_style, + &gstate->ctm, + gstate->antialias, + &boxes); + empty = boxes.num_boxes == 0; + if (! empty) + _cairo_boxes_extents (&boxes, &extents); + _cairo_boxes_fini (&boxes); } - _cairo_traps_init (&traps); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; - status = _cairo_path_fixed_stroke_to_traps (path, - &gstate->stroke_style, - &gstate->ctm, - &gstate->ctm_inverse, - gstate->tolerance, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, - x1, y1, x2, y2); + _cairo_polygon_init (&polygon, NULL, 0); + status = _cairo_path_fixed_stroke_to_polygon (path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + &polygon); + empty = polygon.num_edges == 0; + if (! empty) + extents = polygon.extents; + _cairo_polygon_fini (&polygon); + } + if (! empty) { + _cairo_gstate_extents_to_user_rectangle (gstate, &extents, + x1, y1, x2, y2); } - - _cairo_traps_fini (&traps); return status; } @@ -1452,40 +1495,63 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate, double *x2, double *y2) { cairo_status_t status; - cairo_traps_t traps; + cairo_box_t extents; + cairo_bool_t empty; - if (path->is_empty_fill) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + if (_cairo_path_fixed_fill_is_empty (path)) return CAIRO_STATUS_SUCCESS; + + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + gstate->fill_rule, + gstate->antialias, + &boxes); + empty = boxes.num_boxes == 0; + if (! empty) + _cairo_boxes_extents (&boxes, &extents); + + _cairo_boxes_fini (&boxes); + } else { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + + status = _cairo_path_fixed_fill_to_traps (path, + gstate->fill_rule, + gstate->tolerance, + &traps); + empty = traps.num_traps == 0; + if (! empty) + _cairo_traps_extents (&traps, &extents); + + _cairo_traps_fini (&traps); } - _cairo_traps_init (&traps); - - status = _cairo_path_fixed_fill_to_traps (path, - gstate->fill_rule, - gstate->tolerance, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, - x1, y1, x2, y2); + if (! empty) { + _cairo_gstate_extents_to_user_rectangle (gstate, &extents, + x1, y1, x2, y2); } - _cairo_traps_fini (&traps); - return status; } cairo_status_t _cairo_gstate_reset_clip (cairo_gstate_t *gstate) { - _cairo_clip_reset (&gstate->clip); + _cairo_clip_destroy (gstate->clip); + gstate->clip = NULL; return CAIRO_STATUS_SUCCESS; } @@ -1493,25 +1559,27 @@ _cairo_gstate_reset_clip (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { - return _cairo_clip_clip (&gstate->clip, - path, gstate->fill_rule, - gstate->tolerance, gstate->antialias); + gstate->clip = + _cairo_clip_intersect_path (gstate->clip, + path, + gstate->fill_rule, + gstate->tolerance, + gstate->antialias); + /* XXX */ + return CAIRO_STATUS_SUCCESS; } static cairo_bool_t _cairo_gstate_int_clip_extents (cairo_gstate_t *gstate, cairo_rectangle_int_t *extents) { - const cairo_rectangle_int_t *clip_extents; cairo_bool_t is_bounded; is_bounded = _cairo_surface_get_extents (gstate->target, extents); - clip_extents = _cairo_clip_get_extents (&gstate->clip); - if (clip_extents != NULL) { - cairo_bool_t is_empty; - - is_empty = _cairo_rectangle_intersect (extents, clip_extents); + if (gstate->clip) { + _cairo_rectangle_intersect (extents, + _cairo_clip_get_extents (gstate->clip)); is_bounded = TRUE; } @@ -1555,21 +1623,82 @@ _cairo_gstate_clip_extents (cairo_gstate_t *gstate, cairo_rectangle_list_t* _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) { - cairo_clip_t clip; cairo_rectangle_int_t extents; cairo_rectangle_list_t *list; - - _cairo_clip_init_copy (&clip, &gstate->clip); + cairo_clip_t *clip; if (_cairo_surface_get_extents (gstate->target, &extents)) - _cairo_clip_rectangle (&clip, &extents); + clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents); + else + clip = gstate->clip; - list = _cairo_clip_copy_rectangle_list (&clip, gstate); - _cairo_clip_fini (&clip); + list = _cairo_clip_copy_rectangle_list (clip, gstate); + + if (clip != gstate->clip) + _cairo_clip_destroy (clip); return list; } +cairo_status_t +_cairo_gstate_tag_begin (cairo_gstate_t *gstate, + const char *tag_name, const char *attributes) +{ + cairo_pattern_union_t source_pattern; + cairo_stroke_style_t style; + double dash[2]; + cairo_status_t status; + cairo_matrix_t aggregate_transform; + cairo_matrix_t aggregate_transform_inverse; + + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; + + cairo_matrix_multiply (&aggregate_transform, + &gstate->ctm, + &gstate->target->device_transform); + cairo_matrix_multiply (&aggregate_transform_inverse, + &gstate->target->device_transform_inverse, + &gstate->ctm_inverse); + + memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style)); + if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &aggregate_transform, gstate->tolerance)) { + style.dash = dash; + _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance, + &style.dash_offset, + style.dash, + &style.num_dashes); + } + + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + + return _cairo_surface_tag (gstate->target, + TRUE, /* begin */ + tag_name, + attributes ? attributes : "", + &source_pattern.base, + &style, + &aggregate_transform, + &aggregate_transform_inverse, + gstate->clip); +} + +cairo_status_t +_cairo_gstate_tag_end (cairo_gstate_t *gstate, + const char *tag_name) +{ + return _cairo_surface_tag (gstate->target, + FALSE, /* begin */ + tag_name, + NULL, /* attributes */ + NULL, /* source */ + NULL, /* stroke_style */ + NULL, /* ctm */ + NULL, /* ctm_inverse*/ + NULL); /* clip */ +} + static void _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) { @@ -1583,25 +1712,6 @@ _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) gstate->scaled_font = NULL; } -cairo_status_t -_cairo_gstate_select_font_face (cairo_gstate_t *gstate, - const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - cairo_font_face_t *font_face; - cairo_status_t status; - - font_face = cairo_toy_font_face_create (family, slant, weight); - if (font_face->status) - return font_face->status; - - status = _cairo_gstate_set_font_face (gstate, font_face); - cairo_font_face_destroy (font_face); - - return status; -} - cairo_status_t _cairo_gstate_set_font_size (cairo_gstate_t *gstate, double size) @@ -1620,14 +1730,6 @@ _cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0) return CAIRO_STATUS_SUCCESS; - if (! _cairo_matrix_is_invertible (matrix)) { - /* rank 0 matrices are ok even though they are not invertible */ - if (!(matrix->xx == 0. && matrix->xy == 0. && - matrix->yx == 0. && matrix->yy == 0.)) { - return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); - } - } - _cairo_gstate_unset_scaled_font (gstate); gstate->font_matrix = *matrix; @@ -1794,6 +1896,7 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) cairo_status_t status; cairo_font_options_t options; cairo_scaled_font_t *scaled_font; + cairo_matrix_t font_ctm; if (gstate->scaled_font != NULL) return gstate->scaled_font->status; @@ -1805,9 +1908,13 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate) cairo_surface_get_font_options (gstate->target, &options); cairo_font_options_merge (&options, &gstate->font_options); + cairo_matrix_multiply (&font_ctm, + &gstate->ctm, + &gstate->target->device_transform); + scaled_font = cairo_scaled_font_create (gstate->font_face, &gstate->font_matrix, - &gstate->ctm, + &font_ctm, &options); status = cairo_scaled_font_status (scaled_font); @@ -1832,31 +1939,6 @@ _cairo_gstate_get_font_extents (cairo_gstate_t *gstate, return cairo_scaled_font_status (gstate->scaled_font); } -cairo_status_t -_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, - double x, - double y, - const char *utf8, - int utf8_len, - cairo_glyph_t **glyphs, - int *num_glyphs, - cairo_text_cluster_t **clusters, - int *num_clusters, - cairo_text_cluster_flags_t *cluster_flags) -{ - cairo_status_t status; - - status = _cairo_gstate_ensure_scaled_font (gstate); - if (unlikely (status)) - return status; - - return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags); -} - cairo_status_t _cairo_gstate_set_font_face (cairo_gstate_t *gstate, cairo_font_face_t *font_face) @@ -1896,31 +1978,27 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, cairo_status_t _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, - const char *utf8, - int utf8_len, const cairo_glyph_t *glyphs, int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags) + cairo_glyph_text_info_t *info) { - cairo_pattern_union_t source_pattern; - const cairo_pattern_t *pattern; cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; - cairo_glyph_t *transformed_glyphs; cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; + cairo_pattern_union_t source_pattern; + cairo_glyph_t *transformed_glyphs; + const cairo_pattern_t *pattern; cairo_text_cluster_t *transformed_clusters; cairo_operator_t op; cairo_status_t status; - cairo_clip_t clip; - if (unlikely (gstate->source->status)) - return gstate->source->status; + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; - if (_clipped (gstate)) + if (_cairo_clip_is_all_clipped (gstate->clip)) return CAIRO_STATUS_SUCCESS; status = _cairo_gstate_ensure_scaled_font (gstate); @@ -1932,34 +2010,37 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) { transformed_glyphs = cairo_glyph_allocate (num_glyphs); - if (unlikely (transformed_glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_GLYPHS; - } + if (unlikely (transformed_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - /* Just in case */ - if (!clusters) - num_clusters = 0; - - if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { - transformed_clusters = cairo_text_cluster_allocate (num_clusters); - if (unlikely (transformed_clusters == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_GLYPHS; + if (info != NULL) { + if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { + transformed_clusters = cairo_text_cluster_allocate (info->num_clusters); + if (unlikely (transformed_clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_GLYPHS; + } } + + _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + info->clusters, + info->num_clusters, + info->cluster_flags, + transformed_glyphs, + &num_glyphs, + transformed_clusters); + } else { + _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + NULL, 0, 0, + transformed_glyphs, + &num_glyphs, + NULL); } - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - clusters, - num_clusters, - cluster_flags, - transformed_glyphs, - &num_glyphs, - transformed_clusters); - - if (status || num_glyphs == 0) + if (num_glyphs == 0) goto CLEANUP_GLYPHS; op = _reduce_op (gstate); @@ -1969,33 +2050,37 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); pattern = &source_pattern.base; } - _cairo_clip_init(&clip); /* For really huge font sizes, we can just do path;fill instead of * show_glyphs, as show_glyphs would put excess pressure on the cache, - * not all components below us correctly handle huge font sizes, and - * path filling can be cheaper since parts of glyphs are likely to be - * clipped out. 256 seems like a good limit. But alas, seems like cairo's + * and moreover, not all components below us correctly handle huge font + * sizes. I wanted to set the limit at 256. But alas, seems like cairo's * rasterizer is something like ten times slower than freetype's for huge - * sizes. So, no win just yet when we're using cairo's rasterizer. - * For now, if we're using cairo's rasterizer, use path filling only - * for insanely-huge sizes, just to make sure we don't make anyone - * unhappy. When we get a really fast rasterizer in cairo, we may - * want to readjust this. The threshold calculation is - * encapsulated in _cairo_surface_get_text_path_fill_threshold. + * sizes. So, no win just yet. For now, do it for insanely-huge sizes, + * just to make sure we don't make anyone unhappy. When we get a really + * fast rasterizer in cairo, we may want to readjust this. * * Needless to say, do this only if show_text_glyphs is not available. */ if (cairo_surface_has_show_text_glyphs (gstate->target) || - _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= - _cairo_surface_get_text_path_fill_threshold (gstate->target)) + _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240) { - status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, - utf8, utf8_len, - transformed_glyphs, num_glyphs, - transformed_clusters, num_clusters, - cluster_flags, - gstate->scaled_font, - _gstate_get_clip (gstate, &clip)); + + if (info != NULL) { + status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, + info->utf8, info->utf8_len, + transformed_glyphs, num_glyphs, + transformed_clusters, info->num_clusters, + info->cluster_flags, + gstate->scaled_font, + gstate->clip); + } else { + status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, + NULL, 0, + transformed_glyphs, num_glyphs, + NULL, 0, 0, + gstate->scaled_font, + gstate->clip); + } } else { @@ -2007,31 +2092,18 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, transformed_glyphs, num_glyphs, &path); - if (status == CAIRO_STATUS_SUCCESS && !_cairo_path_fixed_fill_is_empty (&path)) { + if (status == CAIRO_STATUS_SUCCESS) { status = _cairo_surface_fill (gstate->target, op, pattern, &path, CAIRO_FILL_RULE_WINDING, gstate->tolerance, gstate->scaled_font->options.antialias, - _gstate_get_clip (gstate, &clip)); - } else { - /* if _cairo_scaled_font_glyph_path() failed, maybe the font doesn't support - * returning paths, so try the _cairo_surface_show_text_glyphs() option - */ - status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern, - utf8, utf8_len, - transformed_glyphs, num_glyphs, - transformed_clusters, num_clusters, - cluster_flags, - gstate->scaled_font, - _gstate_get_clip (gstate, &clip)); + gstate->clip); } _cairo_path_fixed_fini (&path); } - _cairo_clip_fini (&clip); - CLEANUP_GLYPHS: if (transformed_glyphs != stack_transformed_glyphs) cairo_glyph_free (transformed_glyphs); @@ -2047,35 +2119,32 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, int num_glyphs, cairo_path_fixed_t *path) { - cairo_status_t status; - cairo_glyph_t *transformed_glyphs; cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_glyph_t *transformed_glyphs; + cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) { - transformed_glyphs = stack_transformed_glyphs; + transformed_glyphs = stack_transformed_glyphs; } else { transformed_glyphs = cairo_glyph_allocate (num_glyphs); if (unlikely (transformed_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - NULL, 0, 0, - transformed_glyphs, - NULL, NULL); - if (unlikely (status)) - goto CLEANUP_GLYPHS; + _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + NULL, 0, 0, + transformed_glyphs, + &num_glyphs, NULL); status = _cairo_scaled_font_glyph_path (gstate->scaled_font, transformed_glyphs, num_glyphs, path); - CLEANUP_GLYPHS: if (transformed_glyphs != stack_transformed_glyphs) cairo_glyph_free (transformed_glyphs); @@ -2115,7 +2184,7 @@ _cairo_gstate_get_antialias (cairo_gstate_t *gstate) * This also uses information from the scaled font and the surface to * cull/drop glyphs that will not be visible. **/ -static cairo_status_t +static void _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, const cairo_glyph_t *glyphs, int num_glyphs, @@ -2126,44 +2195,40 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, int *num_transformed_glyphs, cairo_text_cluster_t *transformed_clusters) { - int i, j, k; + cairo_rectangle_int_t surface_extents; cairo_matrix_t *ctm = &gstate->ctm; cairo_matrix_t *font_matrix = &gstate->font_matrix; cairo_matrix_t *device_transform = &gstate->target->device_transform; cairo_bool_t drop = FALSE; double x1 = 0, x2 = 0, y1 = 0, y2 = 0; + int i, j, k; - if (num_transformed_glyphs != NULL) { - cairo_rectangle_int_t surface_extents; - - drop = TRUE; - if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) { - drop = FALSE; /* unbounded surface */ - } else { - double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); - if (surface_extents.width == 0 || surface_extents.height == 0) { - /* No visible area. Don't draw anything */ - *num_transformed_glyphs = 0; - return CAIRO_STATUS_SUCCESS; - } - /* XXX We currently drop any glyphs that has its position outside - * of the surface boundaries by a safety margin depending on the - * font scale. This however can fail in extreme cases where the - * font has really long swashes for example... We can correctly - * handle that by looking the glyph up and using its device bbox - * to device if it's going to be visible, but I'm not inclined to - * do that now. - */ - x1 = surface_extents.x - scale10; - y1 = surface_extents.y - scale10; - x2 = surface_extents.x + (int) surface_extents.width + scale10; - y2 = surface_extents.y + (int) surface_extents.height + scale10; + drop = TRUE; + if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) { + drop = FALSE; /* unbounded surface */ + } else { + double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); + if (surface_extents.width == 0 || surface_extents.height == 0) { + /* No visible area. Don't draw anything */ + *num_transformed_glyphs = 0; + return; } + /* XXX We currently drop any glyphs that have their position outside + * of the surface boundaries by a safety margin depending on the + * font scale. This however can fail in extreme cases where the + * font has really long swashes for example... We can correctly + * handle that by looking the glyph up and using its device bbox + * to device if it's going to be visible, but I'm not inclined to + * do that now. + */ + x1 = surface_extents.x - scale10; + y1 = surface_extents.y - scale10; + x2 = surface_extents.x + (int) surface_extents.width + scale10; + y2 = surface_extents.y + (int) surface_extents.height + scale10; + } - if (!drop) - *num_transformed_glyphs = num_glyphs; - } else - num_transformed_glyphs = &j; + if (!drop) + *num_transformed_glyphs = num_glyphs; #define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2) @@ -2175,6 +2240,8 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, if (! drop) { memcpy (transformed_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); + memcpy (transformed_clusters, clusters, + num_clusters * sizeof (cairo_text_cluster_t)); j = num_glyphs; } else if (num_clusters == 0) { for (i = 0; i < num_glyphs; i++) { @@ -2230,6 +2297,8 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, if (!drop || KEEP_GLYPH (transformed_glyphs[j])) j++; } + memcpy (transformed_clusters, clusters, + num_clusters * sizeof (cairo_text_cluster_t)); } else { const cairo_glyph_t *cur_glyph; @@ -2283,6 +2352,8 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, if (! drop || KEEP_GLYPH (transformed_glyphs[j])) j++; } + memcpy (transformed_clusters, clusters, + num_clusters * sizeof (cairo_text_cluster_t)); } else { const cairo_glyph_t *cur_glyph; @@ -2326,6 +2397,4 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, transformed_glyphs[j] = tmp; } } - - return CAIRO_STATUS_SUCCESS; } diff --git a/gfx/cairo/cairo/src/cairo-hash.c b/gfx/cairo/cairo/src/cairo-hash.c index 81a48a235985..151842eb6df9 100644 --- a/gfx/cairo/cairo/src/cairo-hash.c +++ b/gfx/cairo/cairo/src/cairo-hash.c @@ -59,21 +59,20 @@ #define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) #define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY) -/* We expect keys will not be destroyed frequently, so our table does not - * contain any explicit shrinking code nor any chain-coalescing code for - * entries randomly deleted by memory pressure (except during rehashing, of - * course). These assumptions are potentially bad, but they make the - * implementation straightforward. +/* + * This table is open-addressed with double hashing. Each table size + * is a prime and it makes for the "first" hash modulus; a second + * prime (2 less than the first prime) serves as the "second" hash + * modulus, which is smaller and thus guarantees a complete + * permutation of table indices. * - * Revisit later if evidence appears that we're using excessive memory from - * a mostly-dead table. + * Hash tables are rehashed in order to keep between 12.5% and 50% + * entries in the hash table alive and at least 25% free. When table + * size is changed, the new table has about 25% live elements. * - * This table is open-addressed with double hashing. Each table size is a - * prime chosen to be a little more than double the high water mark for a - * given arrangement, so the tables should remain < 50% full. The table - * size makes for the "first" hash modulus; a second prime (2 less than the - * first prime) serves as the "second" hash modulus, which is co-prime and - * thus guarantees a complete permutation of table indices. + * The free entries guarantee an expected constant-time lookup. + * Doubling/halving the table in the described fashion guarantees + * amortized O(1) insertion/removal. * * This structure, and accompanying table, is borrowed/modified from the * file xserver/render/glyph.c in the freedesktop.org x server, with @@ -81,52 +80,66 @@ * Packard. */ -typedef struct _cairo_hash_table_arrangement { - unsigned long high_water_mark; - unsigned long size; - unsigned long rehash; -} cairo_hash_table_arrangement_t; - -static const cairo_hash_table_arrangement_t hash_table_arrangements [] = { - { 16, 43, 41 }, - { 32, 73, 71 }, - { 64, 151, 149 }, - { 128, 283, 281 }, - { 256, 571, 569 }, - { 512, 1153, 1151 }, - { 1024, 2269, 2267 }, - { 2048, 4519, 4517 }, - { 4096, 9013, 9011 }, - { 8192, 18043, 18041 }, - { 16384, 36109, 36107 }, - { 32768, 72091, 72089 }, - { 65536, 144409, 144407 }, - { 131072, 288361, 288359 }, - { 262144, 576883, 576881 }, - { 524288, 1153459, 1153457 }, - { 1048576, 2307163, 2307161 }, - { 2097152, 4613893, 4613891 }, - { 4194304, 9227641, 9227639 }, - { 8388608, 18455029, 18455027 }, - { 16777216, 36911011, 36911009 }, - { 33554432, 73819861, 73819859 }, - { 67108864, 147639589, 147639587 }, - { 134217728, 295279081, 295279079 }, - { 268435456, 590559793, 590559791 } +static const unsigned long hash_table_sizes[] = { + 43, + 73, + 151, + 283, + 571, + 1153, + 2269, + 4519, + 9013, + 18043, + 36109, + 72091, + 144409, + 288361, + 576883, + 1153459, + 2307163, + 4613893, + 9227641, + 18455029, + 36911011, + 73819861, + 147639589, + 295279081, + 590559793 }; -#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements) - struct _cairo_hash_table { cairo_hash_keys_equal_func_t keys_equal; - const cairo_hash_table_arrangement_t *arrangement; + cairo_hash_entry_t *cache[32]; + + const unsigned long *table_size; cairo_hash_entry_t **entries; unsigned long live_entries; + unsigned long free_entries; unsigned long iterating; /* Iterating, no insert, no resize */ }; +/** + * _cairo_hash_table_uid_keys_equal: + * @key_a: the first key to be compared + * @key_b: the second key to be compared + * + * Provides a #cairo_hash_keys_equal_func_t which always returns + * %TRUE. This is useful to create hash tables using keys whose hash + * completely describes the key, because in this special case + * comparing the hashes is sufficient to guarantee that the keys are + * equal. + * + * Return value: %TRUE. + **/ +static cairo_bool_t +_cairo_hash_table_uid_keys_equal (const void *key_a, const void *key_b) +{ + return TRUE; +} + /** * _cairo_hash_table_create: * @keys_equal: a function to return %TRUE if two keys are equal @@ -139,6 +152,9 @@ struct _cairo_hash_table { * _cairo_hash_table_remove), and other times both a key and a value * will be necessary, (as in _cairo_hash_table_insert). * + * If @keys_equal is %NULL, two keys will be considered equal if and + * only if their hashes are equal. + * * See #cairo_hash_entry_t for more details. * * Return value: the new hash table or %NULL if out of memory. @@ -148,18 +164,22 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) { cairo_hash_table_t *hash_table; - hash_table = malloc (sizeof (cairo_hash_table_t)); + hash_table = _cairo_malloc (sizeof (cairo_hash_table_t)); if (unlikely (hash_table == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } - hash_table->keys_equal = keys_equal; + if (keys_equal == NULL) + hash_table->keys_equal = _cairo_hash_table_uid_keys_equal; + else + hash_table->keys_equal = keys_equal; - hash_table->arrangement = &hash_table_arrangements[0]; + memset (&hash_table->cache, 0, sizeof (hash_table->cache)); + hash_table->table_size = &hash_table_sizes[0]; - hash_table->entries = calloc (hash_table->arrangement->size, - sizeof(cairo_hash_entry_t *)); + hash_table->entries = calloc (*hash_table->table_size, + sizeof (cairo_hash_entry_t *)); if (unlikely (hash_table->entries == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); free (hash_table); @@ -167,6 +187,7 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) } hash_table->live_entries = 0; + hash_table->free_entries = *hash_table->table_size; hash_table->iterating = 0; return hash_table; @@ -183,7 +204,7 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) * _cairo_hash_table_destroy is called. It is a fatal error otherwise, * and this function will halt. The rationale for this behavior is to * avoid memory leaks and to avoid needless complication of the API - * with destroy notifiy callbacks. + * with destroy notify callbacks. * * WARNING: The hash_table must have no running iterators in it when * _cairo_hash_table_destroy is called. It is a fatal error otherwise, @@ -198,8 +219,6 @@ _cairo_hash_table_destroy (cairo_hash_table_t *hash_table) assert (hash_table->iterating == 0); free (hash_table->entries); - hash_table->entries = NULL; - free (hash_table); } @@ -210,7 +229,7 @@ _cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, unsigned long table_size, i, idx, step; cairo_hash_entry_t **entry; - table_size = hash_table->arrangement->size; + table_size = *hash_table->table_size; idx = key->hash % table_size; entry = &hash_table->entries[idx]; @@ -218,9 +237,7 @@ _cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, return entry; i = 1; - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; + step = 1 + key->hash % (table_size - 2); do { idx += step; if (idx >= table_size) @@ -236,52 +253,62 @@ _cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, } /** - * _cairo_hash_table_resize: + * _cairo_hash_table_manage: * @hash_table: a hash table * * Resize the hash table if the number of entries has gotten much * bigger or smaller than the ideal number of entries for the current - * size. + * size and guarantee some free entries to be used as lookup + * termination points. * * Return value: %CAIRO_STATUS_SUCCESS if successful or * %CAIRO_STATUS_NO_MEMORY if out of memory. **/ static cairo_status_t -_cairo_hash_table_resize (cairo_hash_table_t *hash_table) +_cairo_hash_table_manage (cairo_hash_table_t *hash_table) { cairo_hash_table_t tmp; unsigned long new_size, i; - /* This keeps the hash table between 25% and 50% full. */ - unsigned long high = hash_table->arrangement->high_water_mark; - unsigned long low = high >> 2; - - if (hash_table->live_entries >= low && hash_table->live_entries <= high) - return CAIRO_STATUS_SUCCESS; + /* Keep between 12.5% and 50% entries in the hash table alive and + * at least 25% free. */ + unsigned long live_high = *hash_table->table_size >> 1; + unsigned long live_low = live_high >> 2; + unsigned long free_low = live_high >> 1; tmp = *hash_table; - if (hash_table->live_entries > high) + if (hash_table->live_entries > live_high) { - tmp.arrangement = hash_table->arrangement + 1; + tmp.table_size = hash_table->table_size + 1; /* This code is being abused if we can't make a table big enough. */ - assert (tmp.arrangement - hash_table_arrangements < - NUM_HASH_TABLE_ARRANGEMENTS); + assert (tmp.table_size - hash_table_sizes < + ARRAY_LENGTH (hash_table_sizes)); } - else /* hash_table->live_entries < low */ + else if (hash_table->live_entries < live_low) { /* Can't shrink if we're at the smallest size */ - if (hash_table->arrangement == &hash_table_arrangements[0]) - return CAIRO_STATUS_SUCCESS; - tmp.arrangement = hash_table->arrangement - 1; + if (hash_table->table_size == &hash_table_sizes[0]) + tmp.table_size = hash_table->table_size; + else + tmp.table_size = hash_table->table_size - 1; } - new_size = tmp.arrangement->size; + if (tmp.table_size == hash_table->table_size && + hash_table->free_entries > free_low) + { + /* The number of live entries is within the desired bounds + * (we're not going to resize the table) and we have enough + * free entries. Do nothing. */ + return CAIRO_STATUS_SUCCESS; + } + + new_size = *tmp.table_size; tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*)); if (unlikely (tmp.entries == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - for (i = 0; i < hash_table->arrangement->size; ++i) { + for (i = 0; i < *hash_table->table_size; ++i) { if (ENTRY_IS_LIVE (hash_table->entries[i])) { *_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i]) = hash_table->entries[i]; @@ -290,7 +317,8 @@ _cairo_hash_table_resize (cairo_hash_table_t *hash_table) free (hash_table->entries); hash_table->entries = tmp.entries; - hash_table->arrangement = tmp.arrangement; + hash_table->table_size = tmp.table_size; + hash_table->free_entries = new_size - hash_table->live_entries; return CAIRO_STATUS_SUCCESS; } @@ -312,21 +340,24 @@ _cairo_hash_table_lookup (cairo_hash_table_t *hash_table, { cairo_hash_entry_t *entry; unsigned long table_size, i, idx, step; + unsigned long hash = key->hash; - table_size = hash_table->arrangement->size; - idx = key->hash % table_size; + entry = hash_table->cache[hash & 31]; + if (entry && entry->hash == hash && hash_table->keys_equal (key, entry)) + return entry; + + table_size = *hash_table->table_size; + idx = hash % table_size; entry = hash_table->entries[idx]; if (ENTRY_IS_LIVE (entry)) { - if (hash_table->keys_equal (key, entry)) - return entry; + if (entry->hash == hash && hash_table->keys_equal (key, entry)) + goto insert_cache; } else if (ENTRY_IS_FREE (entry)) return NULL; i = 1; - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; + step = 1 + hash % (table_size - 2); do { idx += step; if (idx >= table_size) @@ -334,13 +365,18 @@ _cairo_hash_table_lookup (cairo_hash_table_t *hash_table, entry = hash_table->entries[idx]; if (ENTRY_IS_LIVE (entry)) { - if (hash_table->keys_equal (key, entry)) - return entry; + if (entry->hash == hash && hash_table->keys_equal (key, entry)) + goto insert_cache; } else if (ENTRY_IS_FREE (entry)) return NULL; } while (++i < table_size); + ASSERT_NOT_REACHED; return NULL; + +insert_cache: + hash_table->cache[hash & 31] = entry; + return entry; } /** @@ -372,7 +408,7 @@ _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, assert (predicate != NULL); - table_size = hash_table->arrangement->size; + table_size = *hash_table->table_size; hash = rand (); idx = hash % table_size; @@ -381,9 +417,7 @@ _cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, return entry; i = 1; - step = hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; + step = 1 + hash % (table_size - 2); do { idx += step; if (idx >= table_size) @@ -421,21 +455,24 @@ cairo_status_t _cairo_hash_table_insert (cairo_hash_table_t *hash_table, cairo_hash_entry_t *key_and_value) { + cairo_hash_entry_t **entry; cairo_status_t status; /* Insert is illegal while an iterator is running. */ assert (hash_table->iterating == 0); - hash_table->live_entries++; - status = _cairo_hash_table_resize (hash_table); - if (unlikely (status)) { - /* abort the insert... */ - hash_table->live_entries--; + status = _cairo_hash_table_manage (hash_table); + if (unlikely (status)) return status; - } - *_cairo_hash_table_lookup_unique_key (hash_table, - key_and_value) = key_and_value; + entry = _cairo_hash_table_lookup_unique_key (hash_table, key_and_value); + + if (ENTRY_IS_FREE (*entry)) + hash_table->free_entries--; + + *entry = key_and_value; + hash_table->cache[key_and_value->hash & 31] = key_and_value; + hash_table->live_entries++; return CAIRO_STATUS_SUCCESS; } @@ -447,7 +484,7 @@ _cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, unsigned long table_size, i, idx, step; cairo_hash_entry_t **entry; - table_size = hash_table->arrangement->size; + table_size = *hash_table->table_size; idx = key->hash % table_size; entry = &hash_table->entries[idx]; @@ -455,9 +492,7 @@ _cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, return entry; i = 1; - step = key->hash % hash_table->arrangement->rehash; - if (step == 0) - step = 1; + step = 1 + key->hash % (table_size - 2); do { idx += step; if (idx >= table_size) @@ -487,6 +522,7 @@ _cairo_hash_table_remove (cairo_hash_table_t *hash_table, { *_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY; hash_table->live_entries--; + hash_table->cache[key->hash & 31] = NULL; /* Check for table resize. Don't do this when iterating as this will * reorder elements of the table and cause the iteration to potentially @@ -496,7 +532,7 @@ _cairo_hash_table_remove (cairo_hash_table_t *hash_table, * memory to shrink the hash table. It does leave the table in a * consistent state, and we've already succeeded in removing the * entry, so we don't examine the failure status of this call. */ - _cairo_hash_table_resize (hash_table); + _cairo_hash_table_manage (hash_table); } } @@ -525,7 +561,7 @@ _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, /* Mark the table for iteration */ ++hash_table->iterating; - for (i = 0; i < hash_table->arrangement->size; i++) { + for (i = 0; i < *hash_table->table_size; i++) { entry = hash_table->entries[i]; if (ENTRY_IS_LIVE(entry)) hash_callback (entry, closure); @@ -537,6 +573,6 @@ _cairo_hash_table_foreach (cairo_hash_table_t *hash_table, if (--hash_table->iterating == 0) { /* Should we fail to shrink the hash table, it is left unaltered, * and we don't need to propagate the error status. */ - _cairo_hash_table_resize (hash_table); + _cairo_hash_table_manage (hash_table); } } diff --git a/gfx/cairo/cairo/src/cairo-image-compositor.c b/gfx/cairo/cairo/src/cairo-image-compositor.c new file mode 100644 index 000000000000..8bf3fd4b1eb3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-compositor.c @@ -0,0 +1,3176 @@ +/* -*- 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 + * Copyright © 2009,2010,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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* The primarily reason for keeping a traps-compositor around is + * for validating cairo-xlib (which currently also uses traps). + */ + +#include "cairoint.h" + +#include "cairo-image-surface-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-spans-compositor-private.h" + +#include "cairo-region-private.h" +#include "cairo-traps-private.h" +#include "cairo-tristrip-private.h" + +#include "cairo-pixman-private.h" + +static pixman_image_t * +to_pixman_image (cairo_surface_t *s) +{ + return ((cairo_image_surface_t *)s)->pixman_image; +} + +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_image_surface_t *surface = _surface; + pixman_region32_t *rgn = region ? ®ion->rgn : NULL; + + if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + 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_image_surface_t *dst = _dst; + struct _cairo_boxes_chunk *chunk; + int i; + + TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); + + 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; + if (dst->pixman_format != image->pixman_format || + ! pixman_blt ((uint32_t *)image->data, (uint32_t *)dst->data, + image->stride / sizeof (uint32_t), + dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (image->pixman_format), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x + dx, y + dy, + x, y, + w, h)) + { + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, dst->pixman_image, + x + dx, y + dy, + 0, 0, + x, y, + w, h); + } + } + } + return CAIRO_STATUS_SUCCESS; +} + +static inline uint32_t +color_to_uint32 (const cairo_color_t *color) +{ + return + ((uint32_t)color->alpha_short >> 8 << 24) | + (color->red_short >> 8 << 16) | + (color->green_short & 0xff00) | + (color->blue_short >> 8); +} + +static inline cairo_bool_t +color_to_pixel (const cairo_color_t *color, + pixman_format_code_t format, + uint32_t *pixel) +{ + uint32_t c; + + if (!(format == PIXMAN_a8r8g8b8 || + format == PIXMAN_x8r8g8b8 || + format == PIXMAN_a8b8g8r8 || + format == PIXMAN_x8b8g8r8 || + format == PIXMAN_b8g8r8a8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_r5g6b5 || + format == PIXMAN_b5g6r5 || + format == PIXMAN_a8)) + { + return FALSE; + } + + c = color_to_uint32 (color); + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { + c = ((c & 0xff000000) >> 0) | + ((c & 0x00ff0000) >> 16) | + ((c & 0x0000ff00) >> 0) | + ((c & 0x000000ff) << 16); + } + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { + c = ((c & 0xff000000) >> 24) | + ((c & 0x00ff0000) >> 8) | + ((c & 0x0000ff00) << 8) | + ((c & 0x000000ff) << 24); + } + + if (format == PIXMAN_a8) { + c = c >> 24; + } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { + c = ((((c) >> 3) & 0x001f) | + (((c) >> 5) & 0x07e0) | + (((c) >> 8) & 0xf800)); + } + + *pixel = c; + return TRUE; +} + +static pixman_op_t +_pixman_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_CLEAR: + return PIXMAN_OP_CLEAR; + + case CAIRO_OPERATOR_SOURCE: + return PIXMAN_OP_SRC; + case CAIRO_OPERATOR_OVER: + return PIXMAN_OP_OVER; + case CAIRO_OPERATOR_IN: + return PIXMAN_OP_IN; + case CAIRO_OPERATOR_OUT: + return PIXMAN_OP_OUT; + case CAIRO_OPERATOR_ATOP: + return PIXMAN_OP_ATOP; + + case CAIRO_OPERATOR_DEST: + return PIXMAN_OP_DST; + case CAIRO_OPERATOR_DEST_OVER: + return PIXMAN_OP_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return PIXMAN_OP_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return PIXMAN_OP_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: + return PIXMAN_OP_ATOP_REVERSE; + + case CAIRO_OPERATOR_XOR: + return PIXMAN_OP_XOR; + case CAIRO_OPERATOR_ADD: + return PIXMAN_OP_ADD; + case CAIRO_OPERATOR_SATURATE: + return PIXMAN_OP_SATURATE; + + case CAIRO_OPERATOR_MULTIPLY: + return PIXMAN_OP_MULTIPLY; + case CAIRO_OPERATOR_SCREEN: + return PIXMAN_OP_SCREEN; + case CAIRO_OPERATOR_OVERLAY: + return PIXMAN_OP_OVERLAY; + case CAIRO_OPERATOR_DARKEN: + return PIXMAN_OP_DARKEN; + case CAIRO_OPERATOR_LIGHTEN: + return PIXMAN_OP_LIGHTEN; + case CAIRO_OPERATOR_COLOR_DODGE: + return PIXMAN_OP_COLOR_DODGE; + case CAIRO_OPERATOR_COLOR_BURN: + return PIXMAN_OP_COLOR_BURN; + case CAIRO_OPERATOR_HARD_LIGHT: + return PIXMAN_OP_HARD_LIGHT; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PIXMAN_OP_SOFT_LIGHT; + case CAIRO_OPERATOR_DIFFERENCE: + return PIXMAN_OP_DIFFERENCE; + case CAIRO_OPERATOR_EXCLUSION: + return PIXMAN_OP_EXCLUSION; + case CAIRO_OPERATOR_HSL_HUE: + return PIXMAN_OP_HSL_HUE; + case CAIRO_OPERATOR_HSL_SATURATION: + return PIXMAN_OP_HSL_SATURATION; + case CAIRO_OPERATOR_HSL_COLOR: + return PIXMAN_OP_HSL_COLOR; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PIXMAN_OP_HSL_LUMINOSITY; + + default: + ASSERT_NOT_REACHED; + return PIXMAN_OP_OVER; + } +} + +static cairo_bool_t +__fill_reduces_to_source (cairo_operator_t op, + const cairo_color_t *color, + const cairo_image_surface_t *dst) +{ + if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) + return TRUE; + if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color)) + return TRUE; + if (dst->base.is_clear) + return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD; + + return FALSE; +} + +static cairo_bool_t +fill_reduces_to_source (cairo_operator_t op, + const cairo_color_t *color, + const cairo_image_surface_t *dst, + uint32_t *pixel) +{ + if (__fill_reduces_to_source (op, color, dst)) { + return color_to_pixel (color, dst->pixman_format, pixel); + } + + return FALSE; +} + +static cairo_int_status_t +fill_rectangles (void *_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_image_surface_t *dst = _dst; + uint32_t pixel; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (fill_reduces_to_source (op, color, dst, &pixel)) { + for (i = 0; i < num_rects; i++) { + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + rects[i].x, rects[i].y, + rects[i].width, rects[i].height, + pixel); + } + } else { + pixman_image_t *src = _pixman_image_for_color (color); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + op = _pixman_operator (op); + for (i = 0; i < num_rects; i++) { + pixman_image_composite32 (op, + src, NULL, dst->pixman_image, + 0, 0, + 0, 0, + rects[i].x, rects[i].y, + rects[i].width, rects[i].height); + } + + pixman_image_unref (src); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +fill_boxes (void *_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_image_surface_t *dst = _dst; + struct _cairo_boxes_chunk *chunk; + uint32_t pixel; + int i; + + TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); + + if (fill_reduces_to_source (op, color, dst, &pixel)) { + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + int x = _cairo_fixed_integer_part (chunk->base[i].p1.x); + int y = _cairo_fixed_integer_part (chunk->base[i].p1.y); + int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x; + int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y; + pixman_fill ((uint32_t *) dst->data, + dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x, y, w, h, pixel); + } + } + } + else + { + pixman_image_t *src = _pixman_image_for_color (color); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + op = _pixman_operator (op); + 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); + pixman_image_composite32 (op, + src, NULL, dst->pixman_image, + 0, 0, + 0, 0, + x1, y1, + x2-x1, y2-y1); + } + } + + pixman_image_unref (src); + } + + return CAIRO_STATUS_SUCCESS; +} + +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_image_source_t *src = (cairo_image_source_t *)abstract_src; + cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (mask) { + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, mask->pixman_image, to_pixman_image (_dst), + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); + } else { + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, NULL, to_pixman_image (_dst), + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +lerp (void *_dst, + 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_image_surface_t *dst = _dst; + cairo_image_source_t *src = (cairo_image_source_t *)abstract_src; + cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP_SRC, + src->pixman_image, mask->pixman_image, dst->pixman_image, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); +#else + /* Punch the clip out of the destination */ + TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n", + __FUNCTION__, + mask->base.unique_id, mask->pixman_image, + dst->base.unique_id, dst->pixman_image)); + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask->pixman_image, NULL, dst->pixman_image, + mask_x, mask_y, + 0, 0, + dst_x, dst_y, + width, height); + + /* Now add the two results together */ + TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n", + __FUNCTION__, + src->base.unique_id, src->pixman_image, + mask->base.unique_id, mask->pixman_image, + dst->base.unique_id, dst->pixman_image)); + pixman_image_composite32 (PIXMAN_OP_ADD, + src->pixman_image, mask->pixman_image, dst->pixman_image, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +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) +{ + pixman_image_t *dst = to_pixman_image (_dst); + pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image; + pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL; + pixman_image_t *free_src = NULL; + struct _cairo_boxes_chunk *chunk; + int i; + + /* XXX consider using a region? saves multiple prepare-composite */ + TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes)); + + if (((cairo_surface_t *)_dst)->is_clear && + (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_ADD)) { + op = PIXMAN_OP_SRC; + } else if (mask) { + if (op == CAIRO_OPERATOR_CLEAR) { +#if PIXMAN_HAS_OP_LERP + op = PIXMAN_OP_LERP_CLEAR; +#else + free_src = src = _pixman_image_for_color (CAIRO_COLOR_WHITE); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + op = PIXMAN_OP_OUT_REVERSE; +#endif + } else if (op == CAIRO_OPERATOR_SOURCE) { +#if PIXMAN_HAS_OP_LERP + op = PIXMAN_OP_LERP_SRC; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif + } else { + op = _pixman_operator (op); + } + } else { + op = _pixman_operator (op); + } + + 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); + + pixman_image_composite32 (op, src, mask, dst, + x1 + src_x, y1 + src_y, + x1 + mask_x, y1 + mask_y, + x1 + dst_x, y1 + dst_y, + x2 - x1, y2 - y1); + } + } + + if (free_src) + pixman_image_unref (free_src); + + return CAIRO_STATUS_SUCCESS; +} + +#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) +#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) + +static cairo_bool_t +line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x <= CAIRO_FIXED_16_16_MIN || + line->p1.x >= CAIRO_FIXED_16_16_MAX || + + line->p2.x <= CAIRO_FIXED_16_16_MIN || + line->p2.x >= CAIRO_FIXED_16_16_MAX || + + line->p1.y <= CAIRO_FIXED_16_16_MIN || + line->p1.y >= CAIRO_FIXED_16_16_MAX || + + line->p2.y <= CAIRO_FIXED_16_16_MIN || + line->p2.y >= CAIRO_FIXED_16_16_MAX; +} + +static void +project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + pixman_line_fixed_t *out) +{ + /* XXX use fixed-point arithmetic? */ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} + +void +_pixman_image_add_traps (pixman_image_t *image, + int dst_x, int dst_y, + cairo_traps_t *traps) +{ + cairo_trapezoid_t *t = traps->traps; + int num_traps = traps->num_traps; + while (num_traps--) { + pixman_trapezoid_t trap; + + /* top/bottom will be clamped to surface bounds */ + trap.top = _cairo_fixed_to_16_16 (t->top); + trap.bottom = _cairo_fixed_to_16_16 (t->bottom); + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (line_exceeds_16_16 (&t->left))) { + project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); + trap.left.p1.y = trap.top; + trap.left.p2.y = trap.bottom; + } else { + trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); + } + + if (unlikely (line_exceeds_16_16 (&t->right))) { + project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); + trap.right.p1.y = trap.top; + trap.right.p2.y = trap.bottom; + } else { + trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); + } + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + t++; + } +} + +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_image_surface_t *dst = (cairo_image_surface_t *) _dst; + cairo_image_source_t *src = (cairo_image_source_t *) abstract_src; + cairo_int_status_t status; + pixman_image_t *mask; + pixman_format_code_t format; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* pixman doesn't eliminate self-intersecting trapezoids/edges */ + status = _cairo_bentley_ottmann_tessellate_traps (traps, + CAIRO_FILL_RULE_WINDING); + if (status != CAIRO_INT_STATUS_SUCCESS) + return status; + + /* Special case adding trapezoids onto a mask surface; we want to avoid + * creating an intermediate temporary mask unnecessarily. + * + * We make the assumption here that the portion of the trapezoids + * contained within the surface is bounded by [dst_x,dst_y,width,height]; + * the Cairo core code passes bounds based on the trapezoid extents. + */ + format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; + if (dst->pixman_format == format && + (abstract_src == NULL || + (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid))) + { + _pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps); + return CAIRO_STATUS_SUCCESS; + } + + mask = pixman_image_create_bits (format, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _pixman_image_add_traps (mask, extents->x, extents->y, traps); + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) +static void +set_point (pixman_point_fixed_t *p, cairo_point_t *c) +{ + p->x = _cairo_fixed_to_16_16 (c->x); + p->y = _cairo_fixed_to_16_16 (c->y); +} + +void +_pixman_image_add_tristrip (pixman_image_t *image, + int dst_x, int dst_y, + cairo_tristrip_t *strip) +{ + pixman_triangle_t tri; + pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 }; + int n; + + set_point (p[0], &strip->points[0]); + set_point (p[1], &strip->points[1]); + set_point (p[2], &strip->points[2]); + pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri); + for (n = 3; n < strip->num_points; n++) { + set_point (p[n%3], &strip->points[n]); + pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri); + } +} + +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_image_surface_t *dst = (cairo_image_surface_t *) _dst; + cairo_image_source_t *src = (cairo_image_source_t *) abstract_src; + pixman_image_t *mask; + pixman_format_code_t format; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (strip->num_points < 3) + return CAIRO_STATUS_SUCCESS; + + if (1) { /* pixman doesn't eliminate self-intersecting triangles/edges */ + cairo_int_status_t status; + cairo_traps_t traps; + int n; + + _cairo_traps_init (&traps); + for (n = 0; n < strip->num_points; n++) { + cairo_point_t p[4]; + + p[0] = strip->points[0]; + p[1] = strip->points[1]; + p[2] = strip->points[2]; + p[3] = strip->points[0]; + + _cairo_traps_tessellate_convex_quad (&traps, p); + } + status = composite_traps (_dst, op, abstract_src, + src_x, src_y, + dst_x, dst_y, + extents, antialias, &traps); + _cairo_traps_fini (&traps); + + return status; + } + + format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; + if (dst->pixman_format == format && + (abstract_src == NULL || + (op == CAIRO_OPERATOR_ADD && src->is_opaque_solid))) + { + _pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip); + return CAIRO_STATUS_SUCCESS; + } + + mask = pixman_image_create_bits (format, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _pixman_image_add_tristrip (mask, extents->x, extents->y, strip); + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_int_status_t +check_composite_glyphs (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs) +{ + return CAIRO_STATUS_SUCCESS; +} + +#if HAS_PIXMAN_GLYPHS +static pixman_glyph_cache_t *global_glyph_cache; + +static inline pixman_glyph_cache_t * +get_glyph_cache (void) +{ + if (!global_glyph_cache) + global_glyph_cache = pixman_glyph_cache_create (); + + return global_glyph_cache; +} + +void +_cairo_image_compositor_reset_static_data (void) +{ + CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); + + if (global_glyph_cache) + pixman_glyph_cache_destroy (global_glyph_cache); + global_glyph_cache = NULL; + + CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); +} + +void +_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); + + if (global_glyph_cache) { + pixman_glyph_cache_remove ( + global_glyph_cache, scaled_font, + (void *)scaled_glyph->hash_entry.hash); + } + + CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); +} + +#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) +#define POSITION(x) ((int) floor (x + 0.125)) + +static cairo_int_status_t +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_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + pixman_glyph_cache_t *glyph_cache; + pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)]; + pixman_glyph_t *pglyphs = pglyphs_stack; + pixman_glyph_t *pg; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); + + glyph_cache = get_glyph_cache(); + if (unlikely (glyph_cache == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto out_unlock; + } + + pixman_glyph_cache_freeze (glyph_cache); + + if (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) { + pglyphs = _cairo_malloc_ab (info->num_glyphs, sizeof (pixman_glyph_t)); + if (unlikely (pglyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto out_thaw; + } + } + + pg = pglyphs; + for (i = 0; i < info->num_glyphs; i++) { + unsigned long index = info->glyphs[i].index; + const void *glyph; + unsigned long xphase, yphase; + + xphase = PHASE(info->glyphs[i].x); + yphase = PHASE(info->glyphs[i].y); + + index = index | (xphase << 24) | (yphase << 26); + + glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)index); + if (!glyph) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_image_surface_t *glyph_surface; + + /* This call can actually end up recursing, so we have to + * drop the mutex around it. + */ + CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); + status = _cairo_scaled_glyph_lookup (info->font, index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex); + + if (unlikely (status)) + goto out_thaw; + + glyph_surface = scaled_glyph->surface; + glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index, + glyph_surface->base.device_transform.x0, + glyph_surface->base.device_transform.y0, + glyph_surface->pixman_image); + if (unlikely (!glyph)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto out_thaw; + } + } + + pg->x = POSITION (info->glyphs[i].x); + pg->y = POSITION (info->glyphs[i].y); + pg->glyph = glyph; + pg++; + } + + if (info->use_mask) { + pixman_format_code_t mask_format; + + mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs); + + pixman_composite_glyphs (_pixman_operator (op), + ((cairo_image_source_t *)_src)->pixman_image, + to_pixman_image (_dst), + mask_format, + info->extents.x + src_x, info->extents.y + src_y, + info->extents.x, info->extents.y, + info->extents.x - dst_x, info->extents.y - dst_y, + info->extents.width, info->extents.height, + glyph_cache, pg - pglyphs, pglyphs); + } else { + pixman_composite_glyphs_no_mask (_pixman_operator (op), + ((cairo_image_source_t *)_src)->pixman_image, + to_pixman_image (_dst), + src_x, src_y, + - dst_x, - dst_y, + glyph_cache, pg - pglyphs, pglyphs); + } + +out_thaw: + pixman_glyph_cache_thaw (glyph_cache); + + if (pglyphs != pglyphs_stack) + free(pglyphs); + +out_unlock: + CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); + return status; +} +#else +void +_cairo_image_compositor_reset_static_data (void) +{ +} + +void +_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ +} + +static cairo_int_status_t +composite_one_glyph (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_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + int x, y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = _cairo_scaled_glyph_lookup (info->font, + info->glyphs[0].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + return status; + + glyph_surface = scaled_glyph->surface; + if (glyph_surface->width == 0 || glyph_surface->height == 0) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (info->glyphs[0].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (info->glyphs[0].y - + glyph_surface->base.device_transform.y0); + + pixman_image_composite32 (_pixman_operator (op), + ((cairo_image_source_t *)_src)->pixman_image, + glyph_surface->pixman_image, + to_pixman_image (_dst), + x + src_x, y + src_y, + 0, 0, + x - dst_x, y - dst_y, + glyph_surface->width, + glyph_surface->height); + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +composite_glyphs_via_mask (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_scaled_glyph_t *glyph_cache[64]; + pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE); + cairo_scaled_glyph_t *scaled_glyph; + uint8_t buf[2048]; + pixman_image_t *mask; + pixman_format_code_t format; + cairo_status_t status; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (unlikely (white == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit + * optimised paths through pixman. Should we increase the bit + * depth of the target surface, we should reconsider the appropriate + * mask formats. + */ + + status = _cairo_scaled_glyph_lookup (info->font, + info->glyphs[0].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) { + pixman_image_unref (white); + return status; + } + + memset (glyph_cache, 0, sizeof (glyph_cache)); + glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph; + + format = PIXMAN_a8; + i = (info->extents.width + 3) & ~3; + if (scaled_glyph->surface->base.content & CAIRO_CONTENT_COLOR) { + format = PIXMAN_a8r8g8b8; + i = info->extents.width * 4; + } + + if (i * info->extents.height > (int) sizeof (buf)) { + mask = pixman_image_create_bits (format, + info->extents.width, + info->extents.height, + NULL, 0); + } else { + memset (buf, 0, i * info->extents.height); + mask = pixman_image_create_bits (format, + info->extents.width, + info->extents.height, + (uint32_t *)buf, i); + } + if (unlikely (mask == NULL)) { + pixman_image_unref (white); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = CAIRO_STATUS_SUCCESS; + for (i = 0; i < info->num_glyphs; i++) { + unsigned long glyph_index = info->glyphs[i].index; + int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + cairo_image_surface_t *glyph_surface; + int x, y; + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) + { + status = _cairo_scaled_glyph_lookup (info->font, glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) { + pixman_image_unref (mask); + pixman_image_unref (white); + return status; + } + + glyph_cache[cache_index] = scaled_glyph; + } + + glyph_surface = scaled_glyph->surface; + if (glyph_surface->width && glyph_surface->height) { + if (glyph_surface->base.content & CAIRO_CONTENT_COLOR && + format == PIXMAN_a8) { + pixman_image_t *ca_mask; + + format = PIXMAN_a8r8g8b8; + ca_mask = pixman_image_create_bits (format, + info->extents.width, + info->extents.height, + NULL, 0); + if (unlikely (ca_mask == NULL)) { + pixman_image_unref (mask); + pixman_image_unref (white); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + white, mask, ca_mask, + 0, 0, + 0, 0, + 0, 0, + info->extents.width, + info->extents.height); + pixman_image_unref (mask); + mask = ca_mask; + } + + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (info->glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (info->glyphs[i].y - + glyph_surface->base.device_transform.y0); + + if (glyph_surface->pixman_format == format) { + pixman_image_composite32 (PIXMAN_OP_ADD, + glyph_surface->pixman_image, NULL, mask, + 0, 0, + 0, 0, + x - info->extents.x, y - info->extents.y, + glyph_surface->width, + glyph_surface->height); + } else { + pixman_image_composite32 (PIXMAN_OP_ADD, + white, glyph_surface->pixman_image, mask, + 0, 0, + 0, 0, + x - info->extents.x, y - info->extents.y, + glyph_surface->width, + glyph_surface->height); + } + } + } + + if (format == PIXMAN_a8r8g8b8) + pixman_image_set_component_alpha (mask, TRUE); + + pixman_image_composite32 (_pixman_operator (op), + ((cairo_image_source_t *)_src)->pixman_image, + mask, + to_pixman_image (_dst), + info->extents.x + src_x, info->extents.y + src_y, + 0, 0, + info->extents.x - dst_x, info->extents.y - dst_y, + info->extents.width, info->extents.height); + pixman_image_unref (mask); + pixman_image_unref (white); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +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_scaled_glyph_t *glyph_cache[64]; + pixman_image_t *dst, *src; + cairo_status_t status; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (info->num_glyphs == 1) + return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info); + + if (info->use_mask) + return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info); + + op = _pixman_operator (op); + dst = to_pixman_image (_dst); + src = ((cairo_image_source_t *)_src)->pixman_image; + + memset (glyph_cache, 0, sizeof (glyph_cache)); + status = CAIRO_STATUS_SUCCESS; + + for (i = 0; i < info->num_glyphs; i++) { + int x, y; + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + unsigned long glyph_index = info->glyphs[i].index; + int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) + { + status = _cairo_scaled_glyph_lookup (info->font, glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + break; + + glyph_cache[cache_index] = scaled_glyph; + } + + glyph_surface = scaled_glyph->surface; + if (glyph_surface->width && glyph_surface->height) { + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (info->glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (info->glyphs[i].y - + glyph_surface->base.device_transform.y0); + + pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst, + x + src_x, y + src_y, + 0, 0, + x - dst_x, y - dst_y, + glyph_surface->width, + glyph_surface->height); + } + } + + return status; +} +#endif + +static cairo_int_status_t +check_composite (const cairo_composite_rectangles_t *extents) +{ + return CAIRO_STATUS_SUCCESS; +} + +const cairo_compositor_t * +_cairo_image_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_no_compositor); + compositor.acquire = acquire; + compositor.release = release; + compositor.set_clip_region = set_clip_region; + compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; + 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; +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) + compositor.composite_tristrip = composite_tristrip; +#endif + compositor.check_composite_glyphs = check_composite_glyphs; + compositor.composite_glyphs = composite_glyphs; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} + +const cairo_compositor_t * +_cairo_image_mask_compositor_get (void) +{ + static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; + static cairo_mask_compositor_t compositor; + + if (_cairo_atomic_init_once_enter(&once)) { + _cairo_mask_compositor_init (&compositor, + _cairo_image_traps_compositor_get ()); + compositor.acquire = acquire; + compositor.release = release; + compositor.set_clip_region = set_clip_region; + compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; + compositor.draw_image_boxes = draw_image_boxes; + compositor.fill_rectangles = fill_rectangles; + 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_glyphs = check_composite_glyphs; + compositor.composite_glyphs = composite_glyphs; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} + +#if PIXMAN_HAS_COMPOSITOR +typedef struct _cairo_image_span_renderer { + cairo_span_renderer_t base; + + pixman_image_compositor_t *compositor; + pixman_image_t *src, *mask; + float opacity; + cairo_rectangle_int_t extents; +} cairo_image_span_renderer_t; +COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t)); + +static cairo_status_t +_cairo_image_bounded_opaque_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) + pixman_image_compositor_blt (r->compositor, + spans[0].x, y, + spans[1].x - spans[0].x, height, + spans[0].coverage); + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_bounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + pixman_image_compositor_blt (r->compositor, + spans[0].x, y, + spans[1].x - spans[0].x, height, + r->opacity * spans[0].coverage); + } + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_unbounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + assert (y + height <= r->extents.height); + if (y > r->extents.y) { + pixman_image_compositor_blt (r->compositor, + r->extents.x, r->extents.y, + r->extents.width, y - r->extents.y, + 0); + } + + if (num_spans == 0) { + pixman_image_compositor_blt (r->compositor, + r->extents.x, y, + r->extents.width, height, + 0); + } else { + if (spans[0].x != r->extents.x) { + pixman_image_compositor_blt (r->compositor, + r->extents.x, y, + spans[0].x - r->extents.x, + height, + 0); + } + + do { + assert (spans[0].x < r->extents.x + r->extents.width); + pixman_image_compositor_blt (r->compositor, + spans[0].x, y, + spans[1].x - spans[0].x, height, + r->opacity * spans[0].coverage); + spans++; + } while (--num_spans > 1); + + if (spans[0].x != r->extents.x + r->extents.width) { + assert (spans[0].x < r->extents.x + r->extents.width); + pixman_image_compositor_blt (r->compositor, + spans[0].x, y, + r->extents.x + r->extents.width - spans[0].x, height, + 0); + } + } + + r->extents.y = y + height; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_clipped_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + assert (num_spans); + + do { + if (! spans[0].inverse) + pixman_image_compositor_blt (r->compositor, + spans[0].x, y, + spans[1].x - spans[0].x, height, + r->opacity * spans[0].coverage); + spans++; + } while (--num_spans > 1); + + r->extents.y = y + height; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_finish_unbounded_spans (void *abstract_renderer) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (r->extents.y < r->extents.height) { + pixman_image_compositor_blt (r->compositor, + r->extents.x, r->extents.y, + r->extents.width, + r->extents.height - r->extents.y, + 0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +span_renderer_init (cairo_abstract_span_renderer_t *_r, + const cairo_composite_rectangles_t *composite, + cairo_bool_t needs_clip) +{ + cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r; + cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; + const cairo_pattern_t *source = &composite->source_pattern.base; + cairo_operator_t op = composite->op; + int src_x, src_y; + int mask_x, mask_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (op == CAIRO_OPERATOR_CLEAR) { + op = PIXMAN_OP_LERP_CLEAR; + } else if (dst->base.is_clear && + (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_ADD)) { + op = PIXMAN_OP_SRC; + } else if (op == CAIRO_OPERATOR_SOURCE) { + op = PIXMAN_OP_LERP_SRC; + } else { + op = _pixman_operator (op); + } + + r->compositor = NULL; + r->mask = NULL; + r->src = _pixman_image_for_pattern (dst, source, FALSE, + &composite->unbounded, + &composite->source_sample_area, + &src_x, &src_y); + if (unlikely (r->src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + r->opacity = 1.0; + if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { + r->opacity = composite->mask_pattern.solid.color.alpha; + } else { + r->mask = _pixman_image_for_pattern (dst, + &composite->mask_pattern.base, + TRUE, + &composite->unbounded, + &composite->mask_sample_area, + &mask_x, &mask_y); + if (unlikely (r->mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* XXX Component-alpha? */ + if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 && + _cairo_pattern_is_opaque (source, &composite->source_sample_area)) + { + pixman_image_unref (r->src); + r->src = r->mask; + src_x = mask_x; + src_y = mask_y; + r->mask = NULL; + } + } + + if (composite->is_bounded) { + if (r->opacity == 1.) + r->base.render_rows = _cairo_image_bounded_opaque_spans; + else + r->base.render_rows = _cairo_image_bounded_spans; + r->base.finish = NULL; + } else { + if (needs_clip) + r->base.render_rows = _cairo_image_clipped_spans; + else + r->base.render_rows = _cairo_image_unbounded_spans; + r->base.finish = _cairo_image_finish_unbounded_spans; + r->extents = composite->unbounded; + r->extents.height += r->extents.y; + } + + r->compositor = + pixman_image_create_compositor (op, r->src, r->mask, dst->pixman_image, + composite->unbounded.x + src_x, + composite->unbounded.y + src_y, + composite->unbounded.x + mask_x, + composite->unbounded.y + mask_y, + composite->unbounded.x, + composite->unbounded.y, + composite->unbounded.width, + composite->unbounded.height); + if (unlikely (r->compositor == NULL)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + return CAIRO_STATUS_SUCCESS; +} + +static void +span_renderer_fini (cairo_abstract_span_renderer_t *_r, + cairo_int_status_t status) +{ + cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish) + r->base.finish (r); + + if (r->compositor) + pixman_image_compositor_destroy (r->compositor); + + if (r->src) + pixman_image_unref (r->src); + if (r->mask) + pixman_image_unref (r->mask); +} +#else +typedef struct _cairo_image_span_renderer { + cairo_span_renderer_t base; + + const cairo_composite_rectangles_t *composite; + + float opacity; + uint8_t op; + int bpp; + + pixman_image_t *src, *mask; + union { + struct fill { + ptrdiff_t stride; + uint8_t *data; + uint32_t pixel; + } fill; + struct blit { + int stride; + uint8_t *data; + int src_stride; + uint8_t *src_data; + } blit; + struct composite { + pixman_image_t *dst; + int src_x, src_y; + int mask_x, mask_y; + int run_length; + } composite; + struct finish { + cairo_rectangle_int_t extents; + int src_x, src_y; + ptrdiff_t stride; + uint8_t *data; + } mask; + } u; + uint8_t _buf[0]; +#define SZ_BUF (int)(sizeof (cairo_abstract_span_renderer_t) - sizeof (cairo_image_span_renderer_t)) +} cairo_image_span_renderer_t; +COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t)); + +static cairo_status_t +_cairo_image_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + uint8_t *mask, *row; + int len; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + mask = r->u.mask.data + (y - r->u.mask.extents.y) * r->u.mask.stride; + mask += spans[0].x - r->u.mask.extents.x; + row = mask; + + do { + len = spans[1].x - spans[0].x; + if (spans[0].coverage) { + *row++ = r->opacity * spans[0].coverage; + if (--len) + memset (row, row[-1], len); + } + row += len; + spans++; + } while (--num_spans > 1); + + len = row - mask; + row = mask; + while (--height) { + mask += r->u.mask.stride; + memcpy (mask, row, len); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_spans_and_zero (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + uint8_t *mask; + int len; + + mask = r->u.mask.data; + if (y > r->u.mask.extents.y) { + len = (y - r->u.mask.extents.y) * r->u.mask.stride; + memset (mask, 0, len); + mask += len; + } + + r->u.mask.extents.y = y + height; + r->u.mask.data = mask + height * r->u.mask.stride; + if (num_spans == 0) { + memset (mask, 0, height * r->u.mask.stride); + } else { + uint8_t *row = mask; + + if (spans[0].x != r->u.mask.extents.x) { + len = spans[0].x - r->u.mask.extents.x; + memset (row, 0, len); + row += len; + } + + do { + len = spans[1].x - spans[0].x; + *row++ = r->opacity * spans[0].coverage; + if (len > 1) { + memset (row, row[-1], --len); + row += len; + } + spans++; + } while (--num_spans > 1); + + if (spans[0].x != r->u.mask.extents.x + r->u.mask.extents.width) { + len = r->u.mask.extents.x + r->u.mask.extents.width - spans[0].x; + memset (row, 0, len); + } + + row = mask; + while (--height) { + mask += r->u.mask.stride; + memcpy (mask, row, r->u.mask.extents.width); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_finish_spans_and_zero (void *abstract_renderer) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (r->u.mask.extents.y < r->u.mask.extents.height) + memset (r->u.mask.data, 0, (r->u.mask.extents.height - r->u.mask.extents.y) * r->u.mask.stride); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_fill8_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + do { + if (spans[0].coverage) { + int len = spans[1].x - spans[0].x; + uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; + if (len == 1) + *d = r->u.fill.pixel; + else + memset(d, r->u.fill.pixel, len); + } + spans++; + } while (--num_spans > 1); + } else { + do { + if (spans[0].coverage) { + int yy = y, hh = h; + do { + int len = spans[1].x - spans[0].x; + uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; + if (len == 1) + *d = r->u.fill.pixel; + else + memset(d, r->u.fill.pixel, len); + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_fill16_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + do { + if (spans[0].coverage) { + int len = spans[1].x - spans[0].x; + uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*2); + while (len-- > 0) + *d++ = r->u.fill.pixel; + } + spans++; + } while (--num_spans > 1); + } else { + do { + if (spans[0].coverage) { + int yy = y, hh = h; + do { + int len = spans[1].x - spans[0].x; + uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*2); + while (len-- > 0) + *d++ = r->u.fill.pixel; + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_fill32_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + do { + if (spans[0].coverage) { + int len = spans[1].x - spans[0].x; + if (len > 32) { + pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp, + spans[0].x, y, len, 1, r->u.fill.pixel); + } else { + uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); + while (len-- > 0) + *d++ = r->u.fill.pixel; + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + if (spans[0].coverage) { + if (spans[1].x - spans[0].x > 16) { + pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp, + spans[0].x, y, spans[1].x - spans[0].x, h, + r->u.fill.pixel); + } else { + int yy = y, hh = h; + do { + int len = spans[1].x - spans[0].x; + uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); + while (len-- > 0) + *d++ = r->u.fill.pixel; + yy++; + } while (--hh); + } + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 +static cairo_status_t +_fill_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + pixman_fill ((uint32_t *) r->data, r->stride, r->bpp, + spans[0].x, y, + spans[1].x - spans[0].x, h, + r->pixel); + } + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_status_t +_blit_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + int cpp; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + cpp = r->bpp/8; + if (likely (h == 1)) { + uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; + uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; + do { + if (spans[0].coverage) { + void *s = src + spans[0].x*cpp; + void *d = dst + spans[0].x*cpp; + int len = (spans[1].x - spans[0].x) * cpp; + switch (len) { + case 1: + *(uint8_t *)d = *(uint8_t *)s; + break; + case 2: + *(uint16_t *)d = *(uint16_t *)s; + break; + case 4: + *(uint32_t *)d = *(uint32_t *)s; + break; +#if HAVE_UINT64_T + case 8: + *(uint64_t *)d = *(uint64_t *)s; + break; +#endif + default: + memcpy(d, s, len); + break; + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + if (spans[0].coverage) { + int yy = y, hh = h; + do { + void *src = r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x*cpp; + void *dst = r->u.blit.data + yy*r->u.blit.stride + spans[0].x*cpp; + int len = (spans[1].x - spans[0].x) * cpp; + switch (len) { + case 1: + *(uint8_t *)dst = *(uint8_t *)src; + break; + case 2: + *(uint16_t *)dst = *(uint16_t *)src; + break; + case 4: + *(uint32_t *)dst = *(uint32_t *)src; + break; +#if HAVE_UINT64_T + case 8: + *(uint64_t *)dst = *(uint64_t *)src; + break; +#endif + default: + memcpy(dst, src, len); + break; + } + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_mono_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + pixman_image_composite32 (r->op, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, + 0, 0, + spans[0].x, y, + spans[1].x - spans[0].x, h); + } + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_mono_unbounded_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) { + pixman_image_composite32 (PIXMAN_OP_CLEAR, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, + 0, 0, + r->composite->unbounded.x, y, + r->composite->unbounded.width, h); + r->u.composite.mask_y = y + h; + return CAIRO_STATUS_SUCCESS; + } + + if (y != r->u.composite.mask_y) { + pixman_image_composite32 (PIXMAN_OP_CLEAR, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, + 0, 0, + r->composite->unbounded.x, r->u.composite.mask_y, + r->composite->unbounded.width, y - r->u.composite.mask_y); + } + + if (spans[0].x != r->composite->unbounded.x) { + pixman_image_composite32 (PIXMAN_OP_CLEAR, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, + 0, 0, + r->composite->unbounded.x, y, + spans[0].x - r->composite->unbounded.x, h); + } + + do { + int op = spans[0].coverage ? r->op : PIXMAN_OP_CLEAR; + pixman_image_composite32 (op, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, + 0, 0, + spans[0].x, y, + spans[1].x - spans[0].x, h); + spans++; + } while (--num_spans > 1); + + if (spans[0].x != r->composite->unbounded.x + r->composite->unbounded.width) { + pixman_image_composite32 (PIXMAN_OP_CLEAR, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y, + 0, 0, + spans[0].x, y, + r->composite->unbounded.x + r->composite->unbounded.width - spans[0].x, h); + } + + r->u.composite.mask_y = y + h; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_mono_finish_unbounded_spans (void *abstract_renderer) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (r->u.composite.mask_y < r->composite->unbounded.y + r->composite->unbounded.height) { + pixman_image_composite32 (PIXMAN_OP_CLEAR, + r->src, NULL, r->u.composite.dst, + r->composite->unbounded.x + r->u.composite.src_x, r->u.composite.mask_y + r->u.composite.src_y, + 0, 0, + r->composite->unbounded.x, r->u.composite.mask_y, + r->composite->unbounded.width, + r->composite->unbounded.y + r->composite->unbounded.height - r->u.composite.mask_y); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +mono_renderer_init (cairo_image_span_renderer_t *r, + const cairo_composite_rectangles_t *composite, + cairo_antialias_t antialias, + cairo_bool_t needs_clip) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; + + if (antialias != CAIRO_ANTIALIAS_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!_cairo_pattern_is_opaque_solid (&composite->mask_pattern.base)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + r->base.render_rows = NULL; + if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_color_t *color; + + color = &composite->source_pattern.solid.color; + if (composite->op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + + if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) { + /* Use plain C for the fill operations as the span length is + * typically small, too small to payback the startup overheads of + * using SSE2 etc. + */ + switch (PIXMAN_FORMAT_BPP(dst->pixman_format)) { + case 8: r->base.render_rows = _fill8_spans; break; + case 16: r->base.render_rows = _fill16_spans; break; + case 32: r->base.render_rows = _fill32_spans; break; + default: break; + } + r->u.fill.data = dst->data; + r->u.fill.stride = dst->stride; + } + } else if ((composite->op == CAIRO_OPERATOR_SOURCE || + (composite->op == CAIRO_OPERATOR_OVER && + (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) && + composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && + composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE && + to_image_surface(composite->source_pattern.surface.surface)->format == dst->format) + { + cairo_image_surface_t *src = + to_image_surface(composite->source_pattern.surface.surface); + int tx, ty; + + if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix, + &tx, &ty) && + composite->bounded.x + tx >= 0 && + composite->bounded.y + ty >= 0 && + composite->bounded.x + composite->bounded.width + tx <= src->width && + composite->bounded.y + composite->bounded.height + ty <= src->height) { + + r->u.blit.stride = dst->stride; + r->u.blit.data = dst->data; + r->u.blit.src_stride = src->stride; + r->u.blit.src_data = src->data + src->stride * ty + tx * 4; + r->base.render_rows = _blit_spans; + } + } + + if (r->base.render_rows == NULL) { + r->src = _pixman_image_for_pattern (dst, &composite->source_pattern.base, FALSE, + &composite->unbounded, + &composite->source_sample_area, + &r->u.composite.src_x, &r->u.composite.src_y); + if (unlikely (r->src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + r->u.composite.dst = to_pixman_image (composite->surface); + r->op = _pixman_operator (composite->op); + if (composite->is_bounded == 0) { + r->base.render_rows = _mono_unbounded_spans; + r->base.finish = _mono_finish_unbounded_spans; + r->u.composite.mask_y = composite->unbounded.y; + } else + r->base.render_rows = _mono_spans; + } + r->bpp = PIXMAN_FORMAT_BPP(dst->pixman_format); + + return CAIRO_INT_STATUS_SUCCESS; +} + +#define ONE_HALF 0x7f +#define RB_MASK 0x00ff00ff +#define RB_ONE_HALF 0x007f007f +#define RB_MASK_PLUS_ONE 0x01000100 +#define G_SHIFT 8 +static inline uint32_t +mul8x2_8 (uint32_t a, uint8_t b) +{ + uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF; + return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK; +} + +static inline uint32_t +add8x2_8x2 (uint32_t a, uint32_t b) +{ + uint32_t t = a + b; + t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); + return t & RB_MASK; +} + +static inline uint8_t +mul8_8 (uint8_t a, uint8_t b) +{ + uint16_t t = a * (uint16_t)b + ONE_HALF; + return ((t >> G_SHIFT) + t) >> G_SHIFT; +} + +static inline uint32_t +lerp8x4 (uint32_t src, uint8_t a, uint32_t dst) +{ + return (add8x2_8x2 (mul8x2_8 (src, a), + mul8x2_8 (dst, ~a)) | + add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a), + mul8x2_8 (dst >> G_SHIFT, ~a)) << G_SHIFT); +} + +static cairo_status_t +_fill_a8_lerp_opaque_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + uint8_t *d = r->u.fill.data + r->u.fill.stride*y; + do { + uint8_t a = spans[0].coverage; + if (a) { + int len = spans[1].x - spans[0].x; + if (a == 0xff) { + memset(d + spans[0].x, r->u.fill.pixel, len); + } else { + uint8_t s = mul8_8(a, r->u.fill.pixel); + uint8_t *dst = d + spans[0].x; + a = ~a; + while (len-- > 0) { + uint8_t t = mul8_8(*dst, a); + *dst++ = t + s; + } + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + uint8_t a = spans[0].coverage; + if (a) { + int yy = y, hh = h; + if (a == 0xff) { + do { + int len = spans[1].x - spans[0].x; + uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; + memset(d, r->u.fill.pixel, len); + yy++; + } while (--hh); + } else { + uint8_t s = mul8_8(a, r->u.fill.pixel); + a = ~a; + do { + int len = spans[1].x - spans[0].x; + uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; + while (len-- > 0) { + uint8_t t = mul8_8(*d, a); + *d++ = t + s; + } + yy++; + } while (--hh); + } + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_fill_xrgb32_lerp_opaque_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + do { + uint8_t a = spans[0].coverage; + if (a) { + int len = spans[1].x - spans[0].x; + uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); + if (a == 0xff) { + if (len > 31) { + pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32, + spans[0].x, y, len, 1, r->u.fill.pixel); + } else { + uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); + while (len-- > 0) + *d++ = r->u.fill.pixel; + } + } else while (len-- > 0) { + *d = lerp8x4 (r->u.fill.pixel, a, *d); + d++; + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + uint8_t a = spans[0].coverage; + if (a) { + if (a == 0xff) { + if (spans[1].x - spans[0].x > 16) { + pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32, + spans[0].x, y, spans[1].x - spans[0].x, h, + r->u.fill.pixel); + } else { + int yy = y, hh = h; + do { + int len = spans[1].x - spans[0].x; + uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); + while (len-- > 0) + *d++ = r->u.fill.pixel; + yy++; + } while (--hh); + } + } else { + int yy = y, hh = h; + do { + int len = spans[1].x - spans[0].x; + uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); + while (len-- > 0) { + *d = lerp8x4 (r->u.fill.pixel, a, *d); + d++; + } + yy++; + } while (--hh); + } + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_fill_a8_lerp_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + do { + uint8_t a = mul8_8 (spans[0].coverage, r->bpp); + if (a) { + int len = spans[1].x - spans[0].x; + uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; + uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; + uint16_t ia = ~a; + while (len-- > 0) { + uint16_t t = *d*ia + p; + *d++ = (t + (t>>8)) >> 8; + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + uint8_t a = mul8_8 (spans[0].coverage, r->bpp); + if (a) { + int yy = y, hh = h; + uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; + uint16_t ia = ~a; + do { + int len = spans[1].x - spans[0].x; + uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x; + while (len-- > 0) { + uint16_t t = *d*ia + p; + *d++ = (t + (t>>8)) >> 8; + } + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + do { + uint8_t a = mul8_8 (spans[0].coverage, r->bpp); + if (a) { + int len = spans[1].x - spans[0].x; + uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); + while (len-- > 0) { + *d = lerp8x4 (r->u.fill.pixel, a, *d); + d++; + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + uint8_t a = mul8_8 (spans[0].coverage, r->bpp); + if (a) { + int yy = y, hh = h; + do { + int len = spans[1].x - spans[0].x; + uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4); + while (len-- > 0) { + *d = lerp8x4 (r->u.fill.pixel, a, *d); + d++; + } + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_blit_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; + uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; + do { + uint8_t a = mul8_8 (spans[0].coverage, r->bpp); + if (a) { + uint32_t *s = (uint32_t*)src + spans[0].x; + uint32_t *d = (uint32_t*)dst + spans[0].x; + int len = spans[1].x - spans[0].x; + if (a == 0xff) { + if (len == 1) + *d = *s; + else + memcpy(d, s, len*4); + } else { + while (len-- > 0) { + *d = lerp8x4 (*s, a, *d); + s++, d++; + } + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + uint8_t a = mul8_8 (spans[0].coverage, r->bpp); + if (a) { + int yy = y, hh = h; + do { + uint32_t *s = (uint32_t *)(r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x * 4); + uint32_t *d = (uint32_t *)(r->u.blit.data + yy*r->u.blit.stride + spans[0].x * 4); + int len = spans[1].x - spans[0].x; + if (a == 0xff) { + if (len == 1) + *d = *s; + else + memcpy(d, s, len * 4); + } else { + while (len-- > 0) { + *d = lerp8x4 (*s, a, *d); + s++, d++; + } + } + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_inplace_spans (void *abstract_renderer, + int y, int h, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + uint8_t *mask; + int x0, x1; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (num_spans == 2 && spans[0].coverage == 0xff) { + pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + spans[0].x, y, + spans[1].x - spans[0].x, h); + return CAIRO_STATUS_SUCCESS; + } + + mask = (uint8_t *)pixman_image_get_data (r->mask); + x1 = x0 = spans[0].x; + do { + int len = spans[1].x - spans[0].x; + *mask++ = spans[0].coverage; + if (len > 1) { + if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) { + if (x1 != x0) { + pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + x1 - x0, h); + } + pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + spans[0].x, y, + len, h); + mask = (uint8_t *)pixman_image_get_data (r->mask); + x0 = spans[1].x; + } else if (spans[0].coverage == 0x0 && + x1 - x0 > r->u.composite.run_length) { + pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + x1 - x0, h); + mask = (uint8_t *)pixman_image_get_data (r->mask); + x0 = spans[1].x; + }else { + memset (mask, spans[0].coverage, --len); + mask += len; + } + } + x1 = spans[1].x; + spans++; + } while (--num_spans > 1); + + if (x1 != x0) { + pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + x1 - x0, h); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_inplace_opacity_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + uint8_t *mask; + int x0, x1; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + mask = (uint8_t *)pixman_image_get_data (r->mask); + x1 = x0 = spans[0].x; + do { + int len = spans[1].x - spans[0].x; + uint8_t m = mul8_8(spans[0].coverage, r->bpp); + *mask++ = m; + if (len > 1) { + if (m == 0 && + x1 - x0 > r->u.composite.run_length) { + pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + x1 - x0, h); + mask = (uint8_t *)pixman_image_get_data (r->mask); + x0 = spans[1].x; + }else { + memset (mask, m, --len); + mask += len; + } + } + x1 = spans[1].x; + spans++; + } while (--num_spans > 1); + + if (x1 != x0) { + pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + x1 - x0, h); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_inplace_src_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + uint8_t *m, *base = (uint8_t*)pixman_image_get_data(r->mask); + int x0; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + x0 = spans[0].x; + m = base; + do { + int len = spans[1].x - spans[0].x; + if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) { + if (spans[0].x != x0) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP_SRC, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#else + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + r->mask, NULL, r->u.composite.dst, + 0, 0, + 0, 0, + x0, y, + spans[0].x - x0, h); + pixman_image_composite32 (PIXMAN_OP_ADD, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#endif + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + r->src, NULL, r->u.composite.dst, + spans[0].x + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + spans[0].x, y, + spans[1].x - spans[0].x, h); + + m = base; + x0 = spans[1].x; + } else if (spans[0].coverage == 0x0) { + if (spans[0].x != x0) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP_SRC, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#else + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + r->mask, NULL, r->u.composite.dst, + 0, 0, + 0, 0, + x0, y, + spans[0].x - x0, h); + pixman_image_composite32 (PIXMAN_OP_ADD, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#endif + } + + m = base; + x0 = spans[1].x; + } else { + *m++ = spans[0].coverage; + if (len > 1) { + memset (m, spans[0].coverage, --len); + m += len; + } + } + spans++; + } while (--num_spans > 1); + + if (spans[0].x != x0) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP_SRC, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#else + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + r->mask, NULL, r->u.composite.dst, + 0, 0, + 0, 0, + x0, y, + spans[0].x - x0, h); + pixman_image_composite32 (PIXMAN_OP_ADD, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#endif + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_inplace_src_opacity_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + uint8_t *mask; + int x0; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + x0 = spans[0].x; + mask = (uint8_t *)pixman_image_get_data (r->mask); + do { + int len = spans[1].x - spans[0].x; + uint8_t m = mul8_8(spans[0].coverage, r->bpp); + if (m == 0) { + if (spans[0].x != x0) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP_SRC, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#else + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + r->mask, NULL, r->u.composite.dst, + 0, 0, + 0, 0, + x0, y, + spans[0].x - x0, h); + pixman_image_composite32 (PIXMAN_OP_ADD, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#endif + } + + mask = (uint8_t *)pixman_image_get_data (r->mask); + x0 = spans[1].x; + } else { + *mask++ = m; + if (len > 1) { + memset (mask, m, --len); + mask += len; + } + } + spans++; + } while (--num_spans > 1); + + if (spans[0].x != x0) { +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP_SRC, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#else + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + r->mask, NULL, r->u.composite.dst, + 0, 0, + 0, 0, + x0, y, + spans[0].x - x0, h); + pixman_image_composite32 (PIXMAN_OP_ADD, + r->src, r->mask, r->u.composite.dst, + x0 + r->u.composite.src_x, + y + r->u.composite.src_y, + 0, 0, + x0, y, + spans[0].x - x0, h); +#endif + } + + return CAIRO_STATUS_SUCCESS; +} + +static void free_pixels (pixman_image_t *image, void *data) +{ + free (data); +} + +static cairo_int_status_t +inplace_renderer_init (cairo_image_span_renderer_t *r, + const cairo_composite_rectangles_t *composite, + cairo_antialias_t antialias, + cairo_bool_t needs_clip) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; + uint8_t *buf; + + if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + r->base.render_rows = NULL; + r->bpp = composite->mask_pattern.solid.color.alpha_short >> 8; + + if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_color_t *color; + + color = &composite->source_pattern.solid.color; + if (composite->op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + + if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) { + /* Use plain C for the fill operations as the span length is + * typically small, too small to payback the startup overheads of + * using SSE2 etc. + */ + if (r->bpp == 0xff) { + switch (dst->format) { + case CAIRO_FORMAT_A8: + r->base.render_rows = _fill_a8_lerp_opaque_spans; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + r->base.render_rows = _fill_xrgb32_lerp_opaque_spans; + break; + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB96F: + case CAIRO_FORMAT_RGBA128F: + case CAIRO_FORMAT_INVALID: + default: break; + } + } else { + switch (dst->format) { + case CAIRO_FORMAT_A8: + r->base.render_rows = _fill_a8_lerp_spans; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + r->base.render_rows = _fill_xrgb32_lerp_spans; + break; + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB96F: + case CAIRO_FORMAT_RGBA128F: + case CAIRO_FORMAT_INVALID: + default: break; + } + } + r->u.fill.data = dst->data; + r->u.fill.stride = dst->stride; + } + } else if ((dst->format == CAIRO_FORMAT_ARGB32 || dst->format == CAIRO_FORMAT_RGB24) && + (composite->op == CAIRO_OPERATOR_SOURCE || + (composite->op == CAIRO_OPERATOR_OVER && + (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) && + composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && + composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE && + to_image_surface(composite->source_pattern.surface.surface)->format == dst->format) + { + cairo_image_surface_t *src = + to_image_surface(composite->source_pattern.surface.surface); + int tx, ty; + + if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix, + &tx, &ty) && + composite->bounded.x + tx >= 0 && + composite->bounded.y + ty >= 0 && + composite->bounded.x + composite->bounded.width + tx <= src->width && + composite->bounded.y + composite->bounded.height + ty <= src->height) { + + assert(PIXMAN_FORMAT_BPP(dst->pixman_format) == 32); + r->u.blit.stride = dst->stride; + r->u.blit.data = dst->data; + r->u.blit.src_stride = src->stride; + r->u.blit.src_data = src->data + src->stride * ty + tx * 4; + r->base.render_rows = _blit_xrgb32_lerp_spans; + } + } + if (r->base.render_rows == NULL) { + const cairo_pattern_t *src = &composite->source_pattern.base; + unsigned int width; + + if (composite->is_bounded == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + r->base.render_rows = r->bpp == 0xff ? _inplace_spans : _inplace_opacity_spans; + width = (composite->bounded.width + 3) & ~3; + + r->u.composite.run_length = 8; + if (src->type == CAIRO_PATTERN_TYPE_LINEAR || + src->type == CAIRO_PATTERN_TYPE_RADIAL) + r->u.composite.run_length = 256; + if (dst->base.is_clear && + (composite->op == CAIRO_OPERATOR_SOURCE || + composite->op == CAIRO_OPERATOR_OVER || + composite->op == CAIRO_OPERATOR_ADD)) { + r->op = PIXMAN_OP_SRC; + } else if (composite->op == CAIRO_OPERATOR_SOURCE) { + r->base.render_rows = r->bpp == 0xff ? _inplace_src_spans : _inplace_src_opacity_spans; + r->u.composite.mask_y = r->composite->unbounded.y; + width = (composite->unbounded.width + 3) & ~3; + } else if (composite->op == CAIRO_OPERATOR_CLEAR) { + r->op = PIXMAN_OP_OUT_REVERSE; + src = NULL; + } else { + r->op = _pixman_operator (composite->op); + } + + r->src = _pixman_image_for_pattern (dst, src, FALSE, + &composite->bounded, + &composite->source_sample_area, + &r->u.composite.src_x, &r->u.composite.src_y); + if (unlikely (r->src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* Create an effectively unbounded mask by repeating the single line */ + buf = r->_buf; + if (width > SZ_BUF) { + buf = _cairo_malloc (width); + if (unlikely (buf == NULL)) { + pixman_image_unref (r->src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + r->mask = pixman_image_create_bits (PIXMAN_a8, + width, composite->unbounded.height, + (uint32_t *)buf, 0); + if (unlikely (r->mask == NULL)) { + pixman_image_unref (r->src); + if (buf != r->_buf) + free (buf); + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + } + + if (buf != r->_buf) + pixman_image_set_destroy_function (r->mask, free_pixels, buf); + + r->u.composite.dst = dst->pixman_image; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +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_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r; + cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface; + const cairo_pattern_t *source = &composite->source_pattern.base; + cairo_operator_t op = composite->op; + cairo_int_status_t status; + + TRACE ((stderr, "%s: antialias=%d, needs_clip=%d\n", __FUNCTION__, + antialias, needs_clip)); + + if (needs_clip) + return CAIRO_INT_STATUS_UNSUPPORTED; + + r->composite = composite; + r->mask = NULL; + r->src = NULL; + r->base.finish = NULL; + + status = mono_renderer_init (r, composite, antialias, needs_clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = inplace_renderer_init (r, composite, antialias, needs_clip); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + r->bpp = 0; + + if (op == CAIRO_OPERATOR_CLEAR) { +#if PIXMAN_HAS_OP_LERP + op = PIXMAN_OP_LERP_CLEAR; +#else + source = &_cairo_pattern_white.base; + op = PIXMAN_OP_OUT_REVERSE; +#endif + } else if (dst->base.is_clear && + (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_ADD)) { + op = PIXMAN_OP_SRC; + } else if (op == CAIRO_OPERATOR_SOURCE) { + if (_cairo_pattern_is_opaque (&composite->source_pattern.base, + &composite->source_sample_area)) + { + op = PIXMAN_OP_OVER; + } + else + { +#if PIXMAN_HAS_OP_LERP + op = PIXMAN_OP_LERP_SRC; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif + } + } else { + op = _pixman_operator (op); + } + r->op = op; + + r->src = _pixman_image_for_pattern (dst, source, FALSE, + &composite->unbounded, + &composite->source_sample_area, + &r->u.mask.src_x, &r->u.mask.src_y); + if (unlikely (r->src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + r->opacity = 1.0; + if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) { + r->opacity = composite->mask_pattern.solid.color.alpha; + } else { + pixman_image_t *mask; + int mask_x, mask_y; + + mask = _pixman_image_for_pattern (dst, + &composite->mask_pattern.base, + TRUE, + &composite->unbounded, + &composite->mask_sample_area, + &mask_x, &mask_y); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* XXX Component-alpha? */ + if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 && + _cairo_pattern_is_opaque (source, &composite->source_sample_area)) + { + pixman_image_unref (r->src); + r->src = mask; + r->u.mask.src_x = mask_x; + r->u.mask.src_y = mask_y; + mask = NULL; + } + + if (mask) { + pixman_image_unref (mask); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + r->u.mask.extents = composite->unbounded; + r->u.mask.stride = (r->u.mask.extents.width + 3) & ~3; + if (r->u.mask.extents.height * r->u.mask.stride > SZ_BUF) { + r->mask = pixman_image_create_bits (PIXMAN_a8, + r->u.mask.extents.width, + r->u.mask.extents.height, + NULL, 0); + + r->base.render_rows = _cairo_image_spans; + r->base.finish = NULL; + } else { + r->mask = pixman_image_create_bits (PIXMAN_a8, + r->u.mask.extents.width, + r->u.mask.extents.height, + (uint32_t *)r->_buf, r->u.mask.stride); + + r->base.render_rows = _cairo_image_spans_and_zero; + r->base.finish = _cairo_image_finish_spans_and_zero; + } + if (unlikely (r->mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + r->u.mask.data = (uint8_t *) pixman_image_get_data (r->mask); + r->u.mask.stride = pixman_image_get_stride (r->mask); + + r->u.mask.extents.height += r->u.mask.extents.y; + return CAIRO_STATUS_SUCCESS; +} + +static void +span_renderer_fini (cairo_abstract_span_renderer_t *_r, + cairo_int_status_t status) +{ + cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + if (r->base.finish) + r->base.finish (r); + } + if (likely (status == CAIRO_INT_STATUS_SUCCESS && r->bpp == 0)) { + const cairo_composite_rectangles_t *composite = r->composite; + + pixman_image_composite32 (r->op, r->src, r->mask, + to_pixman_image (composite->surface), + composite->unbounded.x + r->u.mask.src_x, + composite->unbounded.y + r->u.mask.src_y, + 0, 0, + composite->unbounded.x, + composite->unbounded.y, + composite->unbounded.width, + composite->unbounded.height); + } + + if (r->src) + pixman_image_unref (r->src); + if (r->mask) + pixman_image_unref (r->mask); +} +#endif + +const cairo_compositor_t * +_cairo_image_spans_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)) { + _cairo_shape_mask_compositor_init (&shape, + _cairo_image_traps_compositor_get()); + shape.glyphs = NULL; + + _cairo_spans_compositor_init (&spans, &shape); + + spans.flags = 0; +#if PIXMAN_HAS_OP_LERP + spans.flags |= CAIRO_SPANS_COMPOSITOR_HAS_LERP; +#endif + + //spans.acquire = acquire; + //spans.release = release; + spans.fill_boxes = fill_boxes; + spans.draw_image_boxes = draw_image_boxes; + //spans.copy_boxes = copy_boxes; + spans.pattern_to_surface = _cairo_image_source_create_for_pattern; + //spans.check_composite_boxes = check_composite_boxes; + spans.composite_boxes = composite_boxes; + //spans.check_span_renderer = check_span_renderer; + spans.renderer_init = span_renderer_init; + spans.renderer_fini = span_renderer_fini; + + _cairo_atomic_init_once_leave(&once); + } + + return &spans.base; +} diff --git a/gfx/cairo/cairo/src/cairo-image-info-private.h b/gfx/cairo/cairo/src/cairo-image-info-private.h index 0d9ef84985f3..e64928e40ec6 100644 --- a/gfx/cairo/cairo/src/cairo-image-info-private.h +++ b/gfx/cairo/cairo/src/cairo-image-info-private.h @@ -60,4 +60,9 @@ _cairo_image_info_get_png_info (cairo_image_info_t *info, const unsigned char *data, unsigned long length); +cairo_private cairo_int_status_t +_cairo_image_info_get_jbig2_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length); + #endif /* CAIRO_IMAGE_INFO_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-image-info.c b/gfx/cairo/cairo/src/cairo-image-info.c index 63201e65beeb..d147e37239c8 100644 --- a/gfx/cairo/cairo/src/cairo-image-info.c +++ b/gfx/cairo/cairo/src/cairo-image-info.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 © 2008 Adrian Johnson @@ -34,13 +35,9 @@ */ #include "cairoint.h" -#include "cairo-image-info-private.h" -static uint32_t -_get_be32 (const unsigned char *p) -{ - return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -} +#include "cairo-error-private.h" +#include "cairo-image-info-private.h" /* JPEG (image/jpeg) * @@ -140,7 +137,7 @@ _cairo_image_info_get_jpeg_info (cairo_image_info_t *info, break; } - if (p + 2 > data + length) + if (p + 3 > data + length) return CAIRO_INT_STATUS_UNSUPPORTED; p = _jpeg_skip_segment (p); @@ -167,7 +164,7 @@ static const unsigned char _jpx_signature[] = { static const unsigned char * _jpx_next_box (const unsigned char *p) { - return p + _get_be32 (p); + return p + get_unaligned_be32 (p); } static const unsigned char * @@ -182,8 +179,8 @@ _jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type) uint32_t length; if (p + 8 < end) { - length = _get_be32 (p); - if (_get_be32 (p + 4) == type && p + length < end) + length = get_unaligned_be32 (p); + if (get_unaligned_be32 (p + 4) == type && p + length < end) return TRUE; } @@ -205,8 +202,8 @@ _jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type) static void _jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) { - info->height = _get_be32 (p); - info->width = _get_be32 (p + 4); + info->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]; } @@ -278,13 +275,147 @@ _cairo_image_info_get_png_info (cairo_image_info_t *info, return CAIRO_INT_STATUS_UNSUPPORTED; p += 4; - if (_get_be32 (p) != PNG_IHDR) + if (get_unaligned_be32 (p) != PNG_IHDR) return CAIRO_INT_STATUS_UNSUPPORTED; p += 4; - info->width = _get_be32 (p); + info->width = get_unaligned_be32 (p); p += 4; - info->height = _get_be32 (p); + info->height = get_unaligned_be32 (p); return CAIRO_STATUS_SUCCESS; } + +static const unsigned char * +_jbig2_find_data_end (const unsigned char *p, + const unsigned char *end, + int type) +{ + unsigned char end_seq[2]; + int mmr; + + /* Segments of type "Immediate generic region" may have an + * unspecified data length. The JBIG2 specification specifies the + * method to find the end of the data for these segments. */ + if (type == 36 || type == 38 || type == 39) { + if (p + 18 < end) { + mmr = p[17] & 0x01; + if (mmr) { + /* MMR encoding ends with 0x00, 0x00 */ + end_seq[0] = 0x00; + end_seq[1] = 0x00; + } else { + /* Template encoding ends with 0xff, 0xac */ + end_seq[0] = 0xff; + end_seq[1] = 0xac; + } + p += 18; + while (p < end) { + if (p[0] == end_seq[0] && p[1] == end_seq[1]) { + /* Skip the 2 terminating bytes and the 4 byte row count that follows. */ + p += 6; + if (p < end) + return p; + } + p++; + } + } + } + + return NULL; +} + +static const unsigned char * +_jbig2_get_next_segment (const unsigned char *p, + const unsigned char *end, + int *type, + const unsigned char **data, + unsigned long *data_len) +{ + unsigned long seg_num; + cairo_bool_t big_page_size; + int num_segs; + int ref_seg_bytes; + int referred_size; + + if (p + 6 >= end) + return NULL; + + seg_num = get_unaligned_be32 (p); + *type = p[4] & 0x3f; + big_page_size = (p[4] & 0x40) != 0; + p += 5; + + num_segs = p[0] >> 5; + if (num_segs == 7) { + num_segs = get_unaligned_be32 (p) & 0x1fffffff; + ref_seg_bytes = 4 + ((num_segs + 1)/8); + } else { + ref_seg_bytes = 1; + } + p += ref_seg_bytes; + + if (seg_num <= 256) + referred_size = 1; + else if (seg_num <= 65536) + referred_size = 2; + else + referred_size = 4; + + p += num_segs * referred_size; + p += big_page_size ? 4 : 1; + if (p + 4 >= end) + return NULL; + + *data_len = get_unaligned_be32 (p); + p += 4; + *data = p; + + if (*data_len == 0xffffffff) { + /* if data length is -1 we have to scan through the data to find the end */ + p = _jbig2_find_data_end (*data, end, *type); + if (!p || p >= end) + return NULL; + + *data_len = p - *data; + } else { + p += *data_len; + } + + if (p < end) + return p; + else + return NULL; +} + +static void +_jbig2_extract_info (cairo_image_info_t *info, const unsigned char *p) +{ + info->width = get_unaligned_be32 (p); + info->height = get_unaligned_be32 (p + 4); + info->num_components = 1; + info->bits_per_component = 1; +} + +cairo_int_status_t +_cairo_image_info_get_jbig2_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + int seg_type; + const unsigned char *seg_data; + unsigned long seg_data_len; + + while (p && p < end) { + p = _jbig2_get_next_segment (p, end, &seg_type, &seg_data, &seg_data_len); + if (p && seg_type == 48 && seg_data_len > 8) { + /* page information segment */ + _jbig2_extract_info (info, seg_data); + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} diff --git a/gfx/cairo/cairo/src/cairo-image-mask-compositor.c b/gfx/cairo/cairo/src/cairo-image-mask-compositor.c new file mode 100644 index 000000000000..4934216f87e4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-mask-compositor.c @@ -0,0 +1,414 @@ +/* -*- 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 + * Copyright © 2009,2010,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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* This compositor is slightly pointless. Just exists for testing + * and as skeleton code. + */ + +#include "cairoint.h" + +#include "cairo-image-surface-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-region-private.h" + +#error This file isn't included in any Makefile + +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_image_surface_t *surface = _surface; + pixman_region32_t *rgn = region ? ®ion->rgn : NULL; + + if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +has_snapshot (void *_dst, + const cairo_pattern_t *pattern) +{ + return FALSE; +} + +static cairo_int_status_t +draw_image (void *_dst, + cairo_image_surface_t *image, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)_dst; + + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, dst->pixman_image, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height); + return CAIRO_STATUS_SUCCESS; +} + +static inline uint32_t +color_to_uint32 (const cairo_color_t *color) +{ + return + ((uint32_t)color->alpha_short >> 8 << 24) | + (color->red_short >> 8 << 16) | + (color->green_short & 0xff00) | + (color->blue_short >> 8); +} + +static inline cairo_bool_t +color_to_pixel (const cairo_color_t *color, + double opacity, + pixman_format_code_t format, + uint32_t *pixel) +{ + cairo_color_t opacity_color; + uint32_t c; + + if (!(format == PIXMAN_a8r8g8b8 || + format == PIXMAN_x8r8g8b8 || + format == PIXMAN_a8b8g8r8 || + format == PIXMAN_x8b8g8r8 || + format == PIXMAN_b8g8r8a8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_r5g6b5 || + format == PIXMAN_b5g6r5 || + format == PIXMAN_a8)) + { + return FALSE; + } + + if (opacity != 1.0) { + _cairo_color_init_rgba (&opacity_color, + color->red, + color->green, + color->blue, + color->alpha * opacity); + color = &opacity_color; + } + c = color_to_uint32 (color); + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { + c = ((c & 0xff000000) >> 0) | + ((c & 0x00ff0000) >> 16) | + ((c & 0x0000ff00) >> 0) | + ((c & 0x000000ff) << 16); + } + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { + c = ((c & 0xff000000) >> 24) | + ((c & 0x00ff0000) >> 8) | + ((c & 0x0000ff00) << 8) | + ((c & 0x000000ff) << 24); + } + + if (format == PIXMAN_a8) { + c = c >> 24; + } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { + c = ((((c) >> 3) & 0x001f) | + (((c) >> 5) & 0x07e0) | + (((c) >> 8) & 0xf800)); + } + + *pixel = c; + return TRUE; +} + +static cairo_int_status_t +fill_rectangles (void *_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_image_surface_t *dst = _dst; + uint32_t pixel; + int i; + + if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + for (i = 0; i < num_rects; i++) { + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + rects[i].x, rects[i].y, + rects[i].width, rects[i].height, + pixel); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +fill_boxes (void *_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_image_surface_t *dst = _dst; + struct _cairo_boxes_chunk *chunk; + uint32_t pixel; + int i; + + assert (boxes->is_pixel_aligned); + + if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + 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); + pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + pixel); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static pixman_op_t +_pixman_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_CLEAR: + return PIXMAN_OP_CLEAR; + + case CAIRO_OPERATOR_SOURCE: + return PIXMAN_OP_SRC; + case CAIRO_OPERATOR_OVER: + return PIXMAN_OP_OVER; + case CAIRO_OPERATOR_IN: + return PIXMAN_OP_IN; + case CAIRO_OPERATOR_OUT: + return PIXMAN_OP_OUT; + case CAIRO_OPERATOR_ATOP: + return PIXMAN_OP_ATOP; + + case CAIRO_OPERATOR_DEST: + return PIXMAN_OP_DST; + case CAIRO_OPERATOR_DEST_OVER: + return PIXMAN_OP_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return PIXMAN_OP_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return PIXMAN_OP_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: + return PIXMAN_OP_ATOP_REVERSE; + + case CAIRO_OPERATOR_XOR: + return PIXMAN_OP_XOR; + case CAIRO_OPERATOR_ADD: + return PIXMAN_OP_ADD; + case CAIRO_OPERATOR_SATURATE: + return PIXMAN_OP_SATURATE; + + case CAIRO_OPERATOR_MULTIPLY: + return PIXMAN_OP_MULTIPLY; + case CAIRO_OPERATOR_SCREEN: + return PIXMAN_OP_SCREEN; + case CAIRO_OPERATOR_OVERLAY: + return PIXMAN_OP_OVERLAY; + case CAIRO_OPERATOR_DARKEN: + return PIXMAN_OP_DARKEN; + case CAIRO_OPERATOR_LIGHTEN: + return PIXMAN_OP_LIGHTEN; + case CAIRO_OPERATOR_COLOR_DODGE: + return PIXMAN_OP_COLOR_DODGE; + case CAIRO_OPERATOR_COLOR_BURN: + return PIXMAN_OP_COLOR_BURN; + case CAIRO_OPERATOR_HARD_LIGHT: + return PIXMAN_OP_HARD_LIGHT; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PIXMAN_OP_SOFT_LIGHT; + case CAIRO_OPERATOR_DIFFERENCE: + return PIXMAN_OP_DIFFERENCE; + case CAIRO_OPERATOR_EXCLUSION: + return PIXMAN_OP_EXCLUSION; + case CAIRO_OPERATOR_HSL_HUE: + return PIXMAN_OP_HSL_HUE; + case CAIRO_OPERATOR_HSL_SATURATION: + return PIXMAN_OP_HSL_SATURATION; + case CAIRO_OPERATOR_HSL_COLOR: + return PIXMAN_OP_HSL_COLOR; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PIXMAN_OP_HSL_LUMINOSITY; + + default: + ASSERT_NOT_REACHED; + return PIXMAN_OP_OVER; + } +} + +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_image_surface_t *dst = _dst; + cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src; + cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask; + if (mask) { + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, mask->pixman_image, dst->pixman_image, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); + } else { + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, NULL, dst->pixman_image, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height); + } + + return CAIRO_STATUS_SUCCESS; +} + +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) +{ + cairo_image_surface_t *dst = _dst; + cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src; + cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask; + struct _cairo_boxes_chunk *chunk; + int i; + + assert (boxes->is_pixel_aligned); + + op = _pixman_operator (op); + 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); + + if (mask) { + pixman_image_composite32 (op, + src->pixman_image, mask->pixman_image, dst->pixman_image, + x1 + src_x, y1 + src_y, + x1 + mask_x, y1 + mask_y, + x1 + dst_x, y1 + dst_y, + x2 - x1, y2 - y1); + } else { + pixman_image_composite32 (op, + src->pixman_image, NULL, dst->pixman_image, + x1 + src_x, y1 + src_y, + 0, 0, + x1 + dst_x, y1 + dst_y, + x2 - x1, y2 - y1); + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +const cairo_compositor_t * +_cairo_image_mask_compositor_get (void) +{ + static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; + static cairo_mask_compositor_t compositor; + + if (_cairo_atomic_init_once_enter(&once)) { + _cairo_mask_compositor_init (&compositor, + _cairo_image_traps_compositor_get ()); + compositor.acquire = acquire; + compositor.release = release; + compositor.set_clip_region = set_clip_region; + compositor.pattern_to_surface = _cairo_pixman_source_create_for_pattern; + compositor.has_snapshot = has_snapshot; + compositor.draw_image = draw_image; + compositor.fill_rectangles = fill_rectangles; + compositor.fill_boxes = fill_boxes; +#error check_composite must never be NULL, because it gets called without a NULL pointer check + //compositor.check_composite = check_composite; + compositor.composite = composite; + //compositor.check_composite_boxes = check_composite_boxes; + compositor.composite_boxes = composite_boxes; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} diff --git a/gfx/cairo/cairo/src/cairo-image-source.c b/gfx/cairo/cairo/src/cairo-image-source.c new file mode 100644 index 000000000000..c56845ab2d91 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-source.c @@ -0,0 +1,1648 @@ +/* -*- 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 + * Copyright © 2009,2010,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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* The purpose of this file/surface is to simply translate a pattern + * to a pixman_image_t and thence to feed it back to the general + * compositor interface. + */ + +#include "cairoint.h" + +#include "cairo-image-surface-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-error-private.h" +#include "cairo-pattern-inline.h" +#include "cairo-paginated-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-observer-private.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-subsurface-private.h" + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +#if CAIRO_NO_MUTEX +#define PIXMAN_HAS_ATOMIC_OPS 1 +#endif + +#if PIXMAN_HAS_ATOMIC_OPS +static pixman_image_t *__pixman_transparent_image; +static pixman_image_t *__pixman_black_image; +static pixman_image_t *__pixman_white_image; + +static pixman_image_t * +_pixman_transparent_image (void) +{ + pixman_image_t *image; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = __pixman_transparent_image; + if (unlikely (image == NULL)) { + pixman_color_t color; + + color.red = 0x00; + color.green = 0x00; + color.blue = 0x00; + color.alpha = 0x00; + + image = pixman_image_create_solid_fill (&color); + if (unlikely (image == NULL)) + return NULL; + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, + NULL, image)) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} + +static pixman_image_t * +_pixman_black_image (void) +{ + pixman_image_t *image; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = __pixman_black_image; + if (unlikely (image == NULL)) { + pixman_color_t color; + + color.red = 0x00; + color.green = 0x00; + color.blue = 0x00; + color.alpha = 0xffff; + + image = pixman_image_create_solid_fill (&color); + if (unlikely (image == NULL)) + return NULL; + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, + NULL, image)) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} + +static pixman_image_t * +_pixman_white_image (void) +{ + pixman_image_t *image; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + image = __pixman_white_image; + if (unlikely (image == NULL)) { + pixman_color_t color; + + color.red = 0xffff; + color.green = 0xffff; + color.blue = 0xffff; + color.alpha = 0xffff; + + image = pixman_image_create_solid_fill (&color); + if (unlikely (image == NULL)) + return NULL; + + if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, + NULL, image)) + { + pixman_image_ref (image); + } + } else { + pixman_image_ref (image); + } + + return image; +} + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static struct { + cairo_color_t color; + pixman_image_t *image; +} cache[16]; +static int n_cached; + +#else /* !PIXMAN_HAS_ATOMIC_OPS */ +static pixman_image_t * +_pixman_transparent_image (void) +{ + TRACE ((stderr, "%s\n", __FUNCTION__)); + return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT); +} + +static pixman_image_t * +_pixman_black_image (void) +{ + TRACE ((stderr, "%s\n", __FUNCTION__)); + return _pixman_image_for_color (CAIRO_COLOR_BLACK); +} + +static pixman_image_t * +_pixman_white_image (void) +{ + TRACE ((stderr, "%s\n", __FUNCTION__)); + return _pixman_image_for_color (CAIRO_COLOR_WHITE); +} +#endif /* !PIXMAN_HAS_ATOMIC_OPS */ + + +pixman_image_t * +_pixman_image_for_color (const cairo_color_t *cairo_color) +{ + pixman_color_t color; + pixman_image_t *image; + +#if PIXMAN_HAS_ATOMIC_OPS + int i; + + if (CAIRO_COLOR_IS_CLEAR (cairo_color)) + return _pixman_transparent_image (); + + if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) { + if (cairo_color->red_short <= 0x00ff && + cairo_color->green_short <= 0x00ff && + cairo_color->blue_short <= 0x00ff) + { + return _pixman_black_image (); + } + + if (cairo_color->red_short >= 0xff00 && + cairo_color->green_short >= 0xff00 && + cairo_color->blue_short >= 0xff00) + { + return _pixman_white_image (); + } + } + + CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); + for (i = 0; i < n_cached; i++) { + if (_cairo_color_equal (&cache[i].color, cairo_color)) { + image = pixman_image_ref (cache[i].image); + goto UNLOCK; + } + } +#endif + + color.red = cairo_color->red_short; + color.green = cairo_color->green_short; + color.blue = cairo_color->blue_short; + color.alpha = cairo_color->alpha_short; + + image = pixman_image_create_solid_fill (&color); +#if PIXMAN_HAS_ATOMIC_OPS + if (image == NULL) + goto UNLOCK; + + if (n_cached < ARRAY_LENGTH (cache)) { + i = n_cached++; + } else { + i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); + pixman_image_unref (cache[i].image); + } + cache[i].image = pixman_image_ref (image); + cache[i].color = *cairo_color; + +UNLOCK: + CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); +#endif + return image; +} + + +void +_cairo_image_reset_static_data (void) +{ +#if PIXMAN_HAS_ATOMIC_OPS + while (n_cached) + pixman_image_unref (cache[--n_cached].image); + + if (__pixman_transparent_image) { + pixman_image_unref (__pixman_transparent_image); + __pixman_transparent_image = NULL; + } + + if (__pixman_black_image) { + pixman_image_unref (__pixman_black_image); + __pixman_black_image = NULL; + } + + if (__pixman_white_image) { + pixman_image_unref (__pixman_white_image); + __pixman_white_image = NULL; + } +#endif +} + +static pixman_image_t * +_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *ix, int *iy) +{ + pixman_image_t *pixman_image; + pixman_gradient_stop_t pixman_stops_static[2]; + pixman_gradient_stop_t *pixman_stops = pixman_stops_static; + pixman_transform_t pixman_transform; + cairo_matrix_t matrix; + cairo_circle_double_t extremes[2]; + pixman_point_fixed_t p1, p2; + unsigned int i; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { + pixman_stops = _cairo_malloc_ab (pattern->n_stops, + sizeof(pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) + return NULL; + } + + for (i = 0; i < pattern->n_stops; i++) { + pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); + pixman_stops[i].color.red = pattern->stops[i].color.red_short; + pixman_stops[i].color.green = pattern->stops[i].color.green_short; + pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; + pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; + } + + _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes); + + p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); + p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); + p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); + p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); + + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + pixman_image = pixman_image_create_linear_gradient (&p1, &p2, + pixman_stops, + pattern->n_stops); + } else { + pixman_fixed_t r1, r2; + + r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); + r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); + + pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2, + pixman_stops, + pattern->n_stops); + } + + if (pixman_stops != pixman_stops_static) + free (pixman_stops); + + if (unlikely (pixman_image == NULL)) + return NULL; + + *ix = *iy = 0; + status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter, + extents->x + extents->width/2., + extents->y + extents->height/2., + &pixman_transform, ix, iy); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) || + ! pixman_image_set_transform (pixman_image, &pixman_transform)) + { + pixman_image_unref (pixman_image); + return NULL; + } + } + + { + pixman_repeat_t pixman_repeat; + + switch (pattern->base.extend) { + default: + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (pixman_image, pixman_repeat); + } + + return pixman_image; +} + +static pixman_image_t * +_pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *tx, int *ty) +{ + pixman_image_t *image; + int width, height; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + *tx = -extents->x; + *ty = -extents->y; + width = extents->width; + height = extents->height; + + image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0); + if (unlikely (image == NULL)) + return NULL; + + _cairo_mesh_pattern_rasterize (pattern, + pixman_image_get_data (image), + width, height, + pixman_image_get_stride (image), + *tx, *ty); + return image; +} + +struct acquire_source_cleanup { + cairo_surface_t *surface; + cairo_image_surface_t *image; + void *image_extra; +}; + +static void +_acquire_source_cleanup (pixman_image_t *pixman_image, + void *closure) +{ + struct acquire_source_cleanup *data = closure; + + _cairo_surface_release_source_image (data->surface, + data->image, + data->image_extra); + free (data); +} + +static void +_defer_free_cleanup (pixman_image_t *pixman_image, + void *closure) +{ + cairo_surface_destroy (closure); +} + +static uint16_t +expand_channel (uint16_t v, uint32_t bits) +{ + int offset = 16 - bits; + while (offset > 0) { + v |= v >> bits; + offset -= bits; + bits += bits; + } + return v; +} + +static pixman_image_t * +_pixel_to_solid (cairo_image_surface_t *image, int x, int y) +{ + uint32_t pixel; + float *rgba; + pixman_color_t color; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + switch (image->format) { + default: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return NULL; + + case CAIRO_FORMAT_A1: + pixel = *(uint8_t *) (image->data + y * image->stride + x/8); + return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image (); + + case CAIRO_FORMAT_A8: + color.alpha = *(uint8_t *) (image->data + y * image->stride + x); + color.alpha |= color.alpha << 8; + if (color.alpha == 0) + return _pixman_transparent_image (); + if (color.alpha == 0xffff) + return _pixman_black_image (); + + color.red = color.green = color.blue = 0; + return pixman_image_create_solid_fill (&color); + + case CAIRO_FORMAT_RGB16_565: + pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x); + if (pixel == 0) + return _pixman_black_image (); + if (pixel == 0xffff) + return _pixman_white_image (); + + color.alpha = 0xffff; + color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5); + color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6); + color.blue = expand_channel ((pixel & 0x1f) << 11, 5); + return pixman_image_create_solid_fill (&color); + + case CAIRO_FORMAT_RGB30: + pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); + pixel &= 0x3fffffff; /* ignore alpha bits */ + if (pixel == 0) + return _pixman_black_image (); + if (pixel == 0x3fffffff) + return _pixman_white_image (); + + /* convert 10bpc to 16bpc */ + color.alpha = 0xffff; + color.red = expand_channel((pixel >> 20) & 0x3fff, 10); + color.green = expand_channel((pixel >> 10) & 0x3fff, 10); + color.blue = expand_channel(pixel & 0x3fff, 10); + return pixman_image_create_solid_fill (&color); + + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); + color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff; + if (color.alpha == 0) + return _pixman_transparent_image (); + if (pixel == 0xffffffff) + return _pixman_white_image (); + if (color.alpha == 0xffff && (pixel & 0xffffff) == 0) + return _pixman_black_image (); + + color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00); + color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00); + color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00); + return pixman_image_create_solid_fill (&color); + + case CAIRO_FORMAT_RGB96F: + case CAIRO_FORMAT_RGBA128F: + if (image->format == CAIRO_FORMAT_RGBA128F) + { + rgba = (float *)&image->data[y * image->stride + 16 * x]; + color.alpha = 65535.f * rgba[3]; + + if (color.alpha == 0) + return _pixman_transparent_image (); + } + else + { + rgba = (float *)&image->data[y * image->stride + 12 * x]; + color.alpha = 0xffff; + } + + if (color.alpha == 0xffff && rgba[0] == 0.f && rgba[1] == 0.f && rgba[2] == 0.f) + return _pixman_black_image (); + if (color.alpha == 0xffff && rgba[0] == 1.f && rgba[1] == 1.f && rgba[2] == 1.f) + return _pixman_white_image (); + + color.red = rgba[0] * 65535.f; + color.green = rgba[1] * 65535.f; + color.blue = rgba[2] * 65535.f; + return pixman_image_create_solid_fill (&color); + } +} + +/* ========================================================================== */ + +/* Index into filter table */ +typedef enum +{ + KERNEL_IMPULSE, + KERNEL_BOX, + KERNEL_LINEAR, + KERNEL_MITCHELL, + KERNEL_NOTCH, + KERNEL_CATMULL_ROM, + KERNEL_LANCZOS3, + KERNEL_LANCZOS3_STRETCHED, + KERNEL_TENT +} kernel_t; + +/* Produce contribution of a filter of size r for pixel centered on x. + For a typical low-pass function this evaluates the function at x/r. + If the frequency is higher than 1/2, such as when r is less than 1, + this may need to integrate several samples, see cubic for examples. +*/ +typedef double (* kernel_func_t) (double x, double r); + +/* Return maximum number of pixels that will be non-zero. Except for + impluse this is the maximum of 2 and the width of the non-zero part + of the filter rounded up to the next integer. +*/ +typedef int (* kernel_width_func_t) (double r); + +/* Table of filters */ +typedef struct +{ + kernel_t kernel; + kernel_func_t func; + kernel_width_func_t width; +} filter_info_t; + +/* PIXMAN_KERNEL_IMPULSE: Returns pixel nearest the center. This + matches PIXMAN_FILTER_NEAREST. This is useful if you wish to + combine the result of nearest in one direction with another filter + in the other. +*/ + +static double +impulse_kernel (double x, double r) +{ + return 1; +} + +static int +impulse_width (double r) +{ + return 1; +} + +/* PIXMAN_KERNEL_BOX: Intersection of a box of width r with square + pixels. This is the smallest possible filter such that the output + image contains an equal contribution from all the input + pixels. Lots of software uses this. The function is a trapazoid of + width r+1, not a box. + + When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and + PIXMAN_KERNEL_TENT all produce the same filter, allowing + them to be exchanged at this point. +*/ + +static double +box_kernel (double x, double r) +{ + return MAX (0.0, MIN (MIN (r, 1.0), + MIN ((r + 1) / 2 - x, (r + 1) / 2 + x))); +} + +static int +box_width (double r) +{ + return r < 1.0 ? 2 : ceil(r + 1); +} + +/* PIXMAN_KERNEL_LINEAR: Weighted sum of the two pixels nearest the + center, or a triangle of width 2. This matches + PIXMAN_FILTER_BILINEAR. This is useful if you wish to combine the + result of bilinear in one direction with another filter in the + other. This is not a good filter if r > 1. You may actually want + PIXMAN_FILTER_TENT. + + When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and + PIXMAN_KERNEL_TENT all produce the same filter, allowing + them to be exchanged at this point. +*/ + +static double +linear_kernel (double x, double r) +{ + return MAX (1.0 - fabs(x), 0.0); +} + +static int +linear_width (double r) +{ + return 2; +} + +/* Cubic functions described in the Mitchell-Netravali paper. + http://mentallandscape.com/Papers_siggraph88.pdf. This describes + all possible cubic functions that can be used for sampling. +*/ + +static double +general_cubic (double x, double r, double B, double C) +{ + double ax; + if (r < 1.0) + return + general_cubic(x * 2 - .5, r * 2, B, C) + + general_cubic(x * 2 + .5, r * 2, B, C); + + ax = fabs (x / r); + + if (ax < 1) + { + return (((12 - 9 * B - 6 * C) * ax + + (-18 + 12 * B + 6 * C)) * ax * ax + + (6 - 2 * B)) / 6; + } + else if (ax < 2) + { + return ((((-B - 6 * C) * ax + + (6 * B + 30 * C)) * ax + + (-12 * B - 48 * C)) * ax + + (8 * B + 24 * C)) / 6; + } + else + { + return 0.0; + } +} + +static int +cubic_width (double r) +{ + return MAX (2, ceil (r * 4)); +} + +/* PIXMAN_KERNEL_CATMULL_ROM: Catmull-Rom interpolation. Often called + "cubic interpolation", "b-spline", or just "cubic" by other + software. This filter has negative values so it can produce ringing + and output pixels outside the range of input pixels. This is very + close to lanczos2 so there is no reason to supply that as well. +*/ + +static double +cubic_kernel (double x, double r) +{ + return general_cubic (x, r, 0.0, 0.5); +} + +/* PIXMAN_KERNEL_MITCHELL: Cubic recommended by the Mitchell-Netravali + paper. This has negative values and because the values at +/-1 are + not zero it does not interpolate the pixels, meaning it will change + an image even if there is no translation. +*/ + +static double +mitchell_kernel (double x, double r) +{ + return general_cubic (x, r, 1/3.0, 1/3.0); +} + +/* PIXMAN_KERNEL_NOTCH: Cubic recommended by the Mitchell-Netravali + paper to remove postaliasing artifacts. This does not remove + aliasing already present in the source image, though it may appear + to due to it's excessive blurriness. In any case this is more + useful than gaussian for image reconstruction. +*/ + +static double +notch_kernel (double x, double r) +{ + return general_cubic (x, r, 1.5, -0.25); +} + +/* PIXMAN_KERNEL_LANCZOS3: lanczos windowed sinc function from -3 to + +3. Very popular with high-end software though I think any + advantage over cubics is hidden by quantization and programming + mistakes. You will see LANCZOS5 or even 7 sometimes. +*/ + +static double +sinc (double x) +{ + return x ? sin (M_PI * x) / (M_PI * x) : 1.0; +} + +static double +lanczos (double x, double n) +{ + return fabs (x) < n ? sinc (x) * sinc (x * (1.0 / n)) : 0.0; +} + +static double +lanczos3_kernel (double x, double r) +{ + if (r < 1.0) + return + lanczos3_kernel (x * 2 - .5, r * 2) + + lanczos3_kernel (x * 2 + .5, r * 2); + else + return lanczos (x / r, 3.0); +} + +static int +lanczos3_width (double r) +{ + return MAX (2, ceil (r * 6)); +} + +/* PIXMAN_KERNEL_LANCZOS3_STRETCHED - The LANCZOS3 kernel widened by + 4/3. Recommended by Jim Blinn + http://graphics.cs.cmu.edu/nsp/course/15-462/Fall07/462/papers/jaggy.pdf +*/ + +static double +nice_kernel (double x, double r) +{ + return lanczos3_kernel (x, r * (4.0/3)); +} + +static int +nice_width (double r) +{ + return MAX (2.0, ceil (r * 8)); +} + +/* PIXMAN_KERNEL_TENT: Triangle of width 2r. Lots of software uses + this as a "better" filter, twice the size of a box but smaller than + a cubic. + + When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and + PIXMAN_KERNEL_TENT all produce the same filter, allowing + them to be exchanged at this point. +*/ + +static double +tent_kernel (double x, double r) +{ + if (r < 1.0) + return box_kernel(x, r); + else + return MAX (1.0 - fabs(x / r), 0.0); +} + +static int +tent_width (double r) +{ + return r < 1.0 ? 2 : ceil(2 * r); +} + + +static const filter_info_t filters[] = +{ + { KERNEL_IMPULSE, impulse_kernel, impulse_width }, + { KERNEL_BOX, box_kernel, box_width }, + { KERNEL_LINEAR, linear_kernel, linear_width }, + { KERNEL_MITCHELL, mitchell_kernel, cubic_width }, + { KERNEL_NOTCH, notch_kernel, cubic_width }, + { KERNEL_CATMULL_ROM, cubic_kernel, cubic_width }, + { KERNEL_LANCZOS3, lanczos3_kernel, lanczos3_width }, + { KERNEL_LANCZOS3_STRETCHED,nice_kernel, nice_width }, + { KERNEL_TENT, tent_kernel, tent_width } +}; + +/* Fills in one dimension of the filter array */ +static void get_filter(kernel_t filter, double r, + int width, int subsample, + pixman_fixed_t* out) +{ + int i; + pixman_fixed_t *p = out; + int n_phases = 1 << subsample; + double step = 1.0 / n_phases; + kernel_func_t func = filters[filter].func; + + /* special-case the impulse filter: */ + if (width <= 1) + { + for (i = 0; i < n_phases; ++i) + *p++ = pixman_fixed_1; + return; + } + + for (i = 0; i < n_phases; ++i) + { + double frac = (i + .5) * step; + /* Center of left-most pixel: */ + double x1 = ceil (frac - width / 2.0 - 0.5) - frac + 0.5; + double total = 0; + pixman_fixed_t new_total = 0; + int j; + + for (j = 0; j < width; ++j) + { + double v = func(x1 + j, r); + total += v; + p[j] = pixman_double_to_fixed (v); + } + + /* Normalize */ + total = 1 / total; + for (j = 0; j < width; ++j) + new_total += (p[j] *= total); + + /* Put any error on center pixel */ + p[width / 2] += (pixman_fixed_1 - new_total); + + p += width; + } +} + + +/* Create the parameter list for a SEPARABLE_CONVOLUTION filter + * with the given kernels and scale parameters. + */ +static pixman_fixed_t * +create_separable_convolution (int *n_values, + kernel_t xfilter, + double sx, + kernel_t yfilter, + double sy) +{ + int xwidth, xsubsample, ywidth, ysubsample, size_x, size_y; + pixman_fixed_t *params; + + xwidth = filters[xfilter].width(sx); + xsubsample = 0; + if (xwidth > 1) + while (sx * (1 << xsubsample) <= 128.0) xsubsample++; + size_x = (1 << xsubsample) * xwidth; + + ywidth = filters[yfilter].width(sy); + ysubsample = 0; + if (ywidth > 1) + while (sy * (1 << ysubsample) <= 128.0) ysubsample++; + size_y = (1 << ysubsample) * ywidth; + + *n_values = 4 + size_x + size_y; + params = _cairo_malloc (*n_values * sizeof (pixman_fixed_t)); + if (!params) return 0; + + params[0] = pixman_int_to_fixed (xwidth); + params[1] = pixman_int_to_fixed (ywidth); + params[2] = pixman_int_to_fixed (xsubsample); + params[3] = pixman_int_to_fixed (ysubsample); + + get_filter(xfilter, sx, xwidth, xsubsample, params + 4); + get_filter(yfilter, sy, ywidth, ysubsample, params + 4 + size_x); + + return params; +} + +/* ========================================================================== */ + +static cairo_bool_t +_pixman_image_set_properties (pixman_image_t *pixman_image, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *ix,int *iy) +{ + pixman_transform_t pixman_transform; + cairo_int_status_t status; + + status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix, + pattern->filter, + extents->x + extents->width/2., + extents->y + extents->height/2., + &pixman_transform, ix, iy); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + { + /* If the transform is an identity, we don't need to set it + * and we can use any filtering, so choose the fastest one. */ + pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); + } + else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS || + ! pixman_image_set_transform (pixman_image, + &pixman_transform))) + { + return FALSE; + } + else + { + pixman_filter_t pixman_filter; + kernel_t kernel; + double dx, dy; + + /* Compute scale factors from the pattern matrix. These scale + * factors are from user to pattern space, and as such they + * are greater than 1.0 for downscaling and less than 1.0 for + * upscaling. The factors are the size of an axis-aligned + * rectangle with the same area as the parallelgram a 1x1 + * square transforms to. + */ + dx = hypot (pattern->matrix.xx, pattern->matrix.xy); + dy = hypot (pattern->matrix.yx, pattern->matrix.yy); + + /* Clip at maximum pixman_fixed number. Besides making it + * passable to pixman, this avoids errors from inf and nan. + */ + if (! (dx < 0x7FFF)) dx = 0x7FFF; + if (! (dy < 0x7FFF)) dy = 0x7FFF; + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + pixman_filter = PIXMAN_FILTER_FAST; + break; + case CAIRO_FILTER_GOOD: + pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION; + kernel = KERNEL_BOX; + /* Clip the filter size to prevent extreme slowness. This + value could be raised if 2-pass filtering is done */ + if (dx > 16.0) dx = 16.0; + if (dy > 16.0) dy = 16.0; + /* Match the bilinear filter for scales > .75: */ + if (dx < 1.0/0.75) dx = 1.0; + if (dy < 1.0/0.75) dy = 1.0; + break; + case CAIRO_FILTER_BEST: + pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION; + kernel = KERNEL_CATMULL_ROM; /* LANCZOS3 is better but not much */ + /* Clip the filter size to prevent extreme slowness. This + value could be raised if 2-pass filtering is done */ + if (dx > 16.0) { dx = 16.0; kernel = KERNEL_BOX; } + /* blur up to 2x scale, then blend to square pixels for larger: */ + else if (dx < 1.0) { + if (dx < 1.0/128) dx = 1.0/127; + else if (dx < 0.5) dx = 1.0 / (1.0 / dx - 1.0); + else dx = 1.0; + } + if (dy > 16.0) { dy = 16.0; kernel = KERNEL_BOX; } + else if (dy < 1.0) { + if (dy < 1.0/128) dy = 1.0/127; + else if (dy < 0.5) dy = 1.0 / (1.0 / dy - 1.0); + else dy = 1.0; + } + break; + case CAIRO_FILTER_NEAREST: + pixman_filter = PIXMAN_FILTER_NEAREST; + break; + case CAIRO_FILTER_BILINEAR: + pixman_filter = PIXMAN_FILTER_BILINEAR; + break; + case CAIRO_FILTER_GAUSSIAN: + /* XXX: The GAUSSIAN value has no implementation in cairo + * whatsoever, so it was really a mistake to have it in the + * API. We could fix this by officially deprecating it, or + * else inventing semantics and providing an actual + * implementation for it. */ + default: + pixman_filter = PIXMAN_FILTER_BEST; + } + + if (pixman_filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION) { + int n_params; + pixman_fixed_t *params; + params = create_separable_convolution + (&n_params, kernel, dx, kernel, dy); + pixman_image_set_filter (pixman_image, pixman_filter, + params, n_params); + free (params); + } else { + pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); + } + } + + { + pixman_repeat_t pixman_repeat; + + switch (pattern->extend) { + default: + case CAIRO_EXTEND_NONE: + pixman_repeat = PIXMAN_REPEAT_NONE; + break; + case CAIRO_EXTEND_REPEAT: + pixman_repeat = PIXMAN_REPEAT_NORMAL; + break; + case CAIRO_EXTEND_REFLECT: + pixman_repeat = PIXMAN_REPEAT_REFLECT; + break; + case CAIRO_EXTEND_PAD: + pixman_repeat = PIXMAN_REPEAT_PAD; + break; + } + + pixman_image_set_repeat (pixman_image, pixman_repeat); + } + + if (pattern->has_component_alpha) + pixman_image_set_component_alpha (pixman_image, TRUE); + + return TRUE; +} + +struct proxy { + cairo_surface_t base; + cairo_surface_t *image; +}; + +static cairo_status_t +proxy_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + struct proxy *proxy = abstract_surface; + return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra); +} + +static void +proxy_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + struct proxy *proxy = abstract_surface; + _cairo_surface_release_source_image (proxy->image, image, image_extra); +} + +static cairo_status_t +proxy_finish (void *abstract_surface) +{ + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t proxy_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + proxy_finish, + NULL, + + NULL, /* create similar */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, + proxy_acquire_source_image, + proxy_release_source_image, +}; + +static cairo_surface_t * +attach_proxy (cairo_surface_t *source, + cairo_surface_t *image) +{ + struct proxy *proxy; + + proxy = _cairo_malloc (sizeof (*proxy)); + if (unlikely (proxy == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content, FALSE); + + proxy->image = image; + _cairo_surface_attach_snapshot (source, &proxy->base, NULL); + + return &proxy->base; +} + +static void +detach_proxy (cairo_surface_t *source, + cairo_surface_t *proxy) +{ + cairo_surface_finish (proxy); + cairo_surface_destroy (proxy); +} + +static cairo_surface_t * +get_proxy (cairo_surface_t *proxy) +{ + return ((struct proxy *)proxy)->image; +} + +static pixman_image_t * +_pixman_image_for_recording (cairo_image_surface_t *dst, + const cairo_surface_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + const cairo_rectangle_int_t *sample, + int *ix, int *iy) +{ + cairo_surface_t *source, *clone, *proxy; + cairo_rectangle_int_t limit; + cairo_rectangle_int_t src_limit; + pixman_image_t *pixman_image; + cairo_status_t status; + cairo_extend_t extend; + cairo_matrix_t *m, matrix; + double sx = 1.0, sy = 1.0; + int tx = 0, ty = 0; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + *ix = *iy = 0; + + source = _cairo_pattern_get_source (pattern, &limit); + src_limit = limit; + + extend = pattern->base.extend; + if (_cairo_rectangle_contains_rectangle (&limit, sample)) + extend = CAIRO_EXTEND_NONE; + + if (extend == CAIRO_EXTEND_NONE) { + if (! _cairo_rectangle_intersect (&limit, sample)) + return _pixman_transparent_image (); + } + + if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { + double x1, y1, x2, y2; + + matrix = pattern->base.matrix; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + x1 = limit.x; + y1 = limit.y; + x2 = limit.x + limit.width; + y2 = limit.y + limit.height; + + _cairo_matrix_transform_bounding_box (&matrix, + &x1, &y1, &x2, &y2, NULL); + + limit.x = floor (x1); + limit.y = floor (y1); + limit.width = ceil (x2) - limit.x; + limit.height = ceil (y2) - limit.y; + sx = (double)src_limit.width / limit.width; + sy = (double)src_limit.height / limit.height; + } + tx = limit.x; + ty = limit.y; + + /* XXX transformations! */ + proxy = _cairo_surface_has_snapshot (source, &proxy_backend); + if (proxy != NULL) { + clone = cairo_surface_reference (get_proxy (proxy)); + goto done; + } + + if (is_mask) { + clone = cairo_image_surface_create (CAIRO_FORMAT_A8, + limit.width, limit.height); + } else { + if (dst->base.content == source->content) + clone = cairo_image_surface_create (dst->format, + limit.width, limit.height); + else + clone = _cairo_image_surface_create_with_content (source->content, + limit.width, + limit.height); + } + + m = NULL; + if (extend == CAIRO_EXTEND_NONE) { + matrix = pattern->base.matrix; + if (tx | ty) + cairo_matrix_translate (&matrix, tx, ty); + m = &matrix; + } else { + cairo_matrix_init_scale (&matrix, sx, sy); + cairo_matrix_translate (&matrix, src_limit.x/sx, src_limit.y/sy); + m = &matrix; + } + + /* 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); + detach_proxy (source, proxy); + if (unlikely (status)) { + cairo_surface_destroy (clone); + return NULL; + } + +done: + pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image); + cairo_surface_destroy (clone); + + if (extend == CAIRO_EXTEND_NONE) { + *ix = -limit.x; + *iy = -limit.y; + } else { + cairo_pattern_union_t tmp_pattern; + _cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base); + matrix = pattern->base.matrix; + status = cairo_matrix_invert(&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_translate (&matrix, src_limit.x, src_limit.y); + cairo_matrix_scale (&matrix, sx, sy); + status = cairo_matrix_invert(&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_pattern_set_matrix (&tmp_pattern.base, &matrix); + if (! _pixman_image_set_properties (pixman_image, + &tmp_pattern.base, extents, + ix, iy)) { + pixman_image_unref (pixman_image); + pixman_image= NULL; + } + } + + return pixman_image; +} + +static pixman_image_t * +_pixman_image_for_surface (cairo_image_surface_t *dst, + const cairo_surface_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + const cairo_rectangle_int_t *sample, + int *ix, int *iy) +{ + cairo_extend_t extend = pattern->base.extend; + pixman_image_t *pixman_image; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + *ix = *iy = 0; + pixman_image = NULL; + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return _pixman_image_for_recording(dst, pattern, + is_mask, extents, sample, + ix, iy); + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && + (! is_mask || ! pattern->base.has_component_alpha || + (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0)) + { + cairo_surface_t *defer_free = NULL; + cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; + cairo_surface_type_t type; + + if (_cairo_surface_is_snapshot (&source->base)) { + defer_free = _cairo_surface_snapshot_get_target (&source->base); + source = (cairo_image_surface_t *) defer_free; + } + + type = source->base.backend->type; + if (type == CAIRO_SURFACE_TYPE_IMAGE) { + if (extend != CAIRO_EXTEND_NONE && + sample->x >= 0 && + sample->y >= 0 && + sample->x + sample->width <= source->width && + sample->y + sample->height <= source->height) + { + extend = CAIRO_EXTEND_NONE; + } + + if (sample->width == 1 && sample->height == 1) { + if (sample->x < 0 || + sample->y < 0 || + sample->x >= source->width || + sample->y >= source->height) + { + if (extend == CAIRO_EXTEND_NONE) { + cairo_surface_destroy (defer_free); + return _pixman_transparent_image (); + } + } + else + { + pixman_image = _pixel_to_solid (source, + sample->x, sample->y); + if (pixman_image) { + cairo_surface_destroy (defer_free); + return pixman_image; + } + } + } + +#if PIXMAN_HAS_ATOMIC_OPS + /* avoid allocating a 'pattern' image if we can reuse the original */ + if (extend == CAIRO_EXTEND_NONE && + _cairo_matrix_is_pixman_translation (&pattern->base.matrix, + pattern->base.filter, + ix, iy)) + { + cairo_surface_destroy (defer_free); + return pixman_image_ref (source->pixman_image); + } +#endif + + pixman_image = pixman_image_create_bits (source->pixman_format, + source->width, + source->height, + (uint32_t *) source->data, + source->stride); + if (unlikely (pixman_image == NULL)) { + cairo_surface_destroy (defer_free); + return NULL; + } + + if (defer_free) { + pixman_image_set_destroy_function (pixman_image, + _defer_free_cleanup, + defer_free); + } + } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub; + cairo_bool_t is_contained = FALSE; + + sub = (cairo_surface_subsurface_t *) source; + source = (cairo_image_surface_t *) sub->target; + + if (sample->x >= 0 && + sample->y >= 0 && + sample->x + sample->width <= sub->extents.width && + sample->y + sample->height <= sub->extents.height) + { + is_contained = TRUE; + } + + if (sample->width == 1 && sample->height == 1) { + if (is_contained) { + pixman_image = _pixel_to_solid (source, + sub->extents.x + sample->x, + sub->extents.y + sample->y); + if (pixman_image) + return pixman_image; + } else { + if (extend == CAIRO_EXTEND_NONE) + return _pixman_transparent_image (); + } + } + +#if PIXMAN_HAS_ATOMIC_OPS + *ix = sub->extents.x; + *iy = sub->extents.y; + if (is_contained && + _cairo_matrix_is_pixman_translation (&pattern->base.matrix, + pattern->base.filter, + ix, iy)) + { + return pixman_image_ref (source->pixman_image); + } +#endif + + /* Avoid sub-byte offsets, force a copy in that case. */ + if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { + if (is_contained) { + void *data = source->data + + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + + sub->extents.y * source->stride; + pixman_image = pixman_image_create_bits (source->pixman_format, + sub->extents.width, + sub->extents.height, + data, + source->stride); + if (unlikely (pixman_image == NULL)) + return NULL; + } else { + /* XXX for a simple translation and EXTEND_NONE we can + * fix up the pattern matrix instead. + */ + } + } + } + } + + if (pixman_image == NULL) { + struct acquire_source_cleanup *cleanup; + cairo_image_surface_t *image; + void *extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra); + if (unlikely (status)) + return NULL; + + pixman_image = pixman_image_create_bits (image->pixman_format, + image->width, + image->height, + (uint32_t *) image->data, + image->stride); + if (unlikely (pixman_image == NULL)) { + _cairo_surface_release_source_image (pattern->surface, image, extra); + return NULL; + } + + cleanup = _cairo_malloc (sizeof (*cleanup)); + if (unlikely (cleanup == NULL)) { + _cairo_surface_release_source_image (pattern->surface, image, extra); + pixman_image_unref (pixman_image); + return NULL; + } + + cleanup->surface = pattern->surface; + cleanup->image = image; + cleanup->image_extra = extra; + pixman_image_set_destroy_function (pixman_image, + _acquire_source_cleanup, cleanup); + } + + if (! _pixman_image_set_properties (pixman_image, + &pattern->base, extents, + ix, iy)) { + pixman_image_unref (pixman_image); + pixman_image= NULL; + } + + return pixman_image; +} + +struct raster_source_cleanup { + const cairo_pattern_t *pattern; + cairo_surface_t *surface; + cairo_image_surface_t *image; + void *image_extra; +}; + +static void +_raster_source_cleanup (pixman_image_t *pixman_image, + void *closure) +{ + struct raster_source_cleanup *data = closure; + + _cairo_surface_release_source_image (data->surface, + data->image, + data->image_extra); + + _cairo_raster_source_pattern_release (data->pattern, + data->surface); + + free (data); +} + +static pixman_image_t * +_pixman_image_for_raster (cairo_image_surface_t *dst, + const cairo_raster_source_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + const cairo_rectangle_int_t *sample, + int *ix, int *iy) +{ + pixman_image_t *pixman_image; + struct raster_source_cleanup *cleanup; + cairo_image_surface_t *image; + void *extra; + cairo_status_t status; + cairo_surface_t *surface; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + *ix = *iy = 0; + + surface = _cairo_raster_source_pattern_acquire (&pattern->base, + &dst->base, NULL); + if (unlikely (surface == NULL || surface->status)) + return NULL; + + status = _cairo_surface_acquire_source_image (surface, &image, &extra); + if (unlikely (status)) { + _cairo_raster_source_pattern_release (&pattern->base, surface); + return NULL; + } + + assert (image->width == pattern->extents.width); + assert (image->height == pattern->extents.height); + + pixman_image = pixman_image_create_bits (image->pixman_format, + image->width, + image->height, + (uint32_t *) image->data, + image->stride); + if (unlikely (pixman_image == NULL)) { + _cairo_surface_release_source_image (surface, image, extra); + _cairo_raster_source_pattern_release (&pattern->base, surface); + return NULL; + } + + cleanup = _cairo_malloc (sizeof (*cleanup)); + if (unlikely (cleanup == NULL)) { + pixman_image_unref (pixman_image); + _cairo_surface_release_source_image (surface, image, extra); + _cairo_raster_source_pattern_release (&pattern->base, surface); + return NULL; + } + + cleanup->pattern = &pattern->base; + cleanup->surface = surface; + cleanup->image = image; + cleanup->image_extra = extra; + pixman_image_set_destroy_function (pixman_image, + _raster_source_cleanup, cleanup); + + if (! _pixman_image_set_properties (pixman_image, + &pattern->base, extents, + ix, iy)) { + pixman_image_unref (pixman_image); + pixman_image= NULL; + } + + return pixman_image; +} + +pixman_image_t * +_pixman_image_for_pattern (cairo_image_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 *tx, int *ty) +{ + *tx = *ty = 0; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (pattern == NULL) + return _pixman_white_image (); + + switch (pattern->type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color); + + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_LINEAR: + return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, + extents, tx, ty); + + case CAIRO_PATTERN_TYPE_MESH: + return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern, + extents, tx, ty); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _pixman_image_for_surface (dst, + (const cairo_surface_pattern_t *) pattern, + is_mask, extents, sample, + tx, ty); + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _pixman_image_for_raster (dst, + (const cairo_raster_source_pattern_t *) pattern, + is_mask, extents, sample, + tx, ty); + } +} + +static cairo_status_t +_cairo_image_source_finish (void *abstract_surface) +{ + cairo_image_source_t *source = abstract_surface; + + pixman_image_unref (source->pixman_image); + return CAIRO_STATUS_SUCCESS; +} + +const cairo_surface_backend_t _cairo_image_source_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_image_source_finish, + NULL, /* read-only wrapper */ +}; + +cairo_surface_t * +_cairo_image_source_create_for_pattern (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_image_source_t *source; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + source = _cairo_malloc (sizeof (cairo_image_source_t)); + if (unlikely (source == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + source->pixman_image = + _pixman_image_for_pattern ((cairo_image_surface_t *)dst, + pattern, is_mask, + extents, sample, + src_x, src_y); + if (unlikely (source->pixman_image == NULL)) { + free (source); + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_surface_init (&source->base, + &_cairo_image_source_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + FALSE); /* is_vector */ + + source->is_opaque_solid = + pattern == NULL || _cairo_pattern_is_opaque_solid (pattern); + + return &source->base; +} diff --git a/gfx/cairo/cairo/src/cairo-image-surface-inline.h b/gfx/cairo/cairo/src/cairo-image-surface-inline.h new file mode 100644 index 000000000000..743d5fd3e75f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-surface-inline.h @@ -0,0 +1,96 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#ifndef CAIRO_IMAGE_SURFACE_INLINE_H +#define CAIRO_IMAGE_SURFACE_INLINE_H + +#include "cairo-surface-private.h" +#include "cairo-image-surface-private.h" + +CAIRO_BEGIN_DECLS + +static inline cairo_image_surface_t * +_cairo_image_surface_create_in_error (cairo_status_t status) +{ + return (cairo_image_surface_t *) _cairo_surface_create_in_error (status); +} + +static inline void +_cairo_image_surface_set_parent (cairo_image_surface_t *image, + cairo_surface_t *parent) +{ + image->parent = parent; +} + +static inline cairo_bool_t +_cairo_image_surface_is_clone (cairo_image_surface_t *image) +{ + return image->parent != NULL; +} + +/** + * _cairo_surface_is_image: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_image_surface_t + * + * Return value: %TRUE if the surface is an image surface + **/ +static inline cairo_bool_t +_cairo_surface_is_image (const cairo_surface_t *surface) +{ + /* _cairo_surface_nil sets a NULL backend so be safe */ + return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE; +} + +/** + * _cairo_surface_is_image_source: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_image_source_t + * + * Return value: %TRUE if the surface is an image source + **/ +static inline cairo_bool_t +_cairo_surface_is_image_source (const cairo_surface_t *surface) +{ + return surface->backend == &_cairo_image_source_backend; +} + +CAIRO_END_DECLS + +#endif /* CAIRO_IMAGE_SURFACE_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-image-surface-private.h b/gfx/cairo/cairo/src/cairo-image-surface-private.h new file mode 100644 index 000000000000..2b7921133f72 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-surface-private.h @@ -0,0 +1,239 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H +#define CAIRO_IMAGE_SURFACE_PRIVATE_H + +#include "cairo-surface-private.h" + +#include +#include + +CAIRO_BEGIN_DECLS + +/* The canonical image backend */ +struct _cairo_image_surface { + cairo_surface_t base; + + pixman_image_t *pixman_image; + const cairo_compositor_t *compositor; + + /* Parenting is tricky wrt lifetime tracking... + * + * One use for tracking the parent of an image surface is for + * create_similar_image() where we wish to create a device specific + * surface but return an image surface to the user. In such a case, + * the image may be owned by the device specific surface, its parent, + * but the user lifetime tracking is then performed on the image. So + * when the image is then finalized we call cairo_surface_destroy() + * on the parent. However, for normal usage where the lifetime tracking + * is done on the parent surface, we need to be careful to unhook + * the image->parent pointer before finalizing the image. + */ + cairo_surface_t *parent; + + pixman_format_code_t pixman_format; + cairo_format_t format; + unsigned char *data; + + int width; + int height; + ptrdiff_t stride; + int depth; + + unsigned owns_data : 1; + unsigned transparency : 2; + unsigned color : 2; +}; +#define to_image_surface(S) ((cairo_image_surface_t *)(S)) + +/* A wrapper for holding pixman images returned by create_for_pattern */ +typedef struct _cairo_image_source { + cairo_surface_t base; + + pixman_image_t *pixman_image; + unsigned is_opaque_solid : 1; +} cairo_image_source_t; + +cairo_private extern const cairo_surface_backend_t _cairo_image_surface_backend; +cairo_private extern const cairo_surface_backend_t _cairo_image_source_backend; + +cairo_private const cairo_compositor_t * +_cairo_image_mask_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_image_traps_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_image_spans_compositor_get (void); + +#define _cairo_image_default_compositor_get _cairo_image_spans_compositor_get + +cairo_private cairo_int_status_t +_cairo_image_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_image_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_private cairo_int_status_t +_cairo_image_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_private cairo_int_status_t +_cairo_image_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_private cairo_int_status_t +_cairo_image_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_private void +_cairo_image_surface_init (cairo_image_surface_t *surface, + pixman_image_t *pixman_image, + pixman_format_code_t pixman_format); + +cairo_private cairo_surface_t * +_cairo_image_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height); + +cairo_private cairo_image_surface_t * +_cairo_image_surface_map_to_image (void *abstract_other, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_int_status_t +_cairo_image_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image); + +cairo_private cairo_surface_t * +_cairo_image_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_image_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_image_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra); + +cairo_private cairo_surface_t * +_cairo_image_surface_snapshot (void *abstract_surface); + +cairo_private_no_warn cairo_bool_t +_cairo_image_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + +cairo_private void +_cairo_image_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options); + +cairo_private cairo_surface_t * +_cairo_image_source_create_for_pattern (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_status_t +_cairo_image_surface_finish (void *abstract_surface); + +cairo_private pixman_image_t * +_pixman_image_for_color (const cairo_color_t *cairo_color); + +cairo_private pixman_image_t * +_pixman_image_for_pattern (cairo_image_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 *tx, int *ty); + +cairo_private void +_pixman_image_add_traps (pixman_image_t *image, + int dst_x, int dst_y, + cairo_traps_t *traps); + +cairo_private void +_pixman_image_add_tristrip (pixman_image_t *image, + int dst_x, int dst_y, + cairo_tristrip_t *strip); + +cairo_private cairo_image_surface_t * +_cairo_image_surface_clone_subimage (cairo_surface_t *surface, + const cairo_rectangle_int_t *extents); + +/* Similar to clone; but allow format conversion */ +cairo_private cairo_image_surface_t * +_cairo_image_surface_create_from_image (cairo_image_surface_t *other, + pixman_format_code_t format, + int x, int y, int width, int height, + int stride); + +CAIRO_END_DECLS + +#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c index 4aaf1c159203..0e17f3a1619c 100644 --- a/gfx/cairo/cairo/src/cairo-image-surface.c +++ b/gfx/cairo/cairo/src/cairo-image-surface.c @@ -2,7 +2,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2003 University of Southern California - * Copyright © 2009,2010 Intel Corporation + * Copyright © 2009,2010,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 @@ -42,9 +42,17 @@ #include "cairo-boxes-private.h" #include "cairo-clip-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-paginated-private.h" +#include "cairo-pattern-private.h" +#include "cairo-pixman-private.h" +#include "cairo-recording-surface-private.h" #include "cairo-region-private.h" #include "cairo-scaled-font-private.h" +#include "cairo-surface-snapshot-inline.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" @@ -52,7 +60,6 @@ * mainly determined by coordinates of things sent to pixman at the * moment being in 16.16 format. */ #define MAX_IMAGE_SIZE 32767 -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ /** * SECTION:cairo-image @@ -63,7 +70,7 @@ * Image surfaces provide the ability to render to memory buffers * either allocated by cairo or by the calling code. The supported * image formats are those defined in #cairo_format_t. - */ + **/ /** * CAIRO_HAS_IMAGE_SURFACE: @@ -72,21 +79,8 @@ * The image surface backend is always built in. * This macro was added for completeness in cairo 1.8. * - * @Since: 1.8 - */ - -static cairo_int_status_t -_cairo_image_surface_fill (void *dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - -static pixman_image_t * -_pixman_image_for_solid (const cairo_solid_pattern_t *pattern); + * Since: 1.8 + **/ static cairo_bool_t _cairo_image_surface_is_size_valid (int width, int height) @@ -99,8 +93,14 @@ cairo_format_t _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) { switch (pixman_format) { + case PIXMAN_rgba_float: + return CAIRO_FORMAT_RGBA128F; + case PIXMAN_rgb_float: + return CAIRO_FORMAT_RGB96F; case PIXMAN_a8r8g8b8: return CAIRO_FORMAT_ARGB32; + case PIXMAN_x2r10g10b10: + return CAIRO_FORMAT_RGB30; case PIXMAN_x8r8g8b8: return CAIRO_FORMAT_RGB24; case PIXMAN_a8: @@ -109,6 +109,12 @@ _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) return CAIRO_FORMAT_A1; case PIXMAN_r5g6b5: return CAIRO_FORMAT_RGB16_565; +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) + case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8: +#endif +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,27,2) + case PIXMAN_a8r8g8b8_sRGB: +#endif case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8: case PIXMAN_b8g8r8: case PIXMAN_b5g6r5: case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5: @@ -122,10 +128,12 @@ _cairo_format_from_pixman_format (pixman_format_code_t pixman_format) case PIXMAN_yuy2: case PIXMAN_yv12: case PIXMAN_b8g8r8x8: case PIXMAN_b8g8r8a8: - case PIXMAN_x2b10g10r10: case PIXMAN_a2b10g10r10: - case PIXMAN_x2r10g10b10: + case PIXMAN_x2b10g10r10: case PIXMAN_a2r10g10b10: +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) + case PIXMAN_x14r6g6b6: +#endif default: return CAIRO_FORMAT_INVALID; } @@ -147,23 +155,12 @@ _cairo_content_from_pixman_format (pixman_format_code_t pixman_format) return content; } -cairo_surface_t * -_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, - pixman_format_code_t pixman_format) +void +_cairo_image_surface_init (cairo_image_surface_t *surface, + pixman_image_t *pixman_image, + pixman_format_code_t pixman_format) { - cairo_image_surface_t *surface; - int width = pixman_image_get_width (pixman_image); - int height = pixman_image_get_height (pixman_image); - - surface = malloc (sizeof (cairo_image_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &_cairo_image_surface_backend, - NULL, /* device */ - _cairo_content_from_pixman_format (pixman_format)); - + surface->parent = NULL; surface->pixman_image = pixman_image; surface->pixman_format = pixman_format; @@ -171,12 +168,36 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, surface->data = (uint8_t *) pixman_image_get_data (pixman_image); surface->owns_data = FALSE; surface->transparency = CAIRO_IMAGE_UNKNOWN; + surface->color = CAIRO_IMAGE_UNKNOWN_COLOR; - surface->width = width; - surface->height = height; + surface->width = pixman_image_get_width (pixman_image); + surface->height = pixman_image_get_height (pixman_image); surface->stride = pixman_image_get_stride (pixman_image); surface->depth = pixman_image_get_depth (pixman_image); + surface->base.is_clear = surface->width == 0 || surface->height == 0; + + surface->compositor = _cairo_image_spans_compositor_get (); +} + +cairo_surface_t * +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, + pixman_format_code_t pixman_format) +{ + cairo_image_surface_t *surface; + + surface = _cairo_malloc (sizeof (cairo_image_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_image_surface_backend, + NULL, /* device */ + _cairo_content_from_pixman_format (pixman_format), + FALSE); /* is_vector */ + + _cairo_image_surface_init (surface, pixman_image, pixman_format); + return &surface->base; } @@ -299,9 +320,18 @@ _cairo_format_to_pixman_format_code (cairo_format_t format) case CAIRO_FORMAT_RGB24: ret = PIXMAN_x8r8g8b8; break; + case CAIRO_FORMAT_RGB30: + ret = PIXMAN_x2r10g10b10; + break; case CAIRO_FORMAT_RGB16_565: ret = PIXMAN_r5g6b5; break; + case CAIRO_FORMAT_RGB96F: + ret = PIXMAN_rgb_float; + break; + case CAIRO_FORMAT_RGBA128F: + ret = PIXMAN_rgba_float; + break; case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_INVALID: default: @@ -327,7 +357,7 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, } pixman_image = pixman_image_create_bits (pixman_format, width, height, - (uint32_t *) data, stride ? stride : 4); + (uint32_t *) data, stride); if (unlikely (pixman_image == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -351,8 +381,8 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, * @height: height of the surface, in pixels * * Creates an image surface of the specified format and - * dimensions. Initially the surface contents are all - * 0. (Specifically, within each pixel, each color or alpha channel + * dimensions. Initially the surface contents are set to 0. + * (Specifically, within each pixel, each color or alpha channel * belonging to format will be 0. The contents of bits within a pixel, * but not belonging to the given format are undefined). * @@ -363,6 +393,8 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data, * 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: 1.0 **/ cairo_surface_t * cairo_image_surface_create (cairo_format_t format, @@ -381,7 +413,7 @@ cairo_image_surface_create (cairo_format_t format, } slim_hidden_def (cairo_image_surface_create); -cairo_surface_t * + cairo_surface_t * _cairo_image_surface_create_with_content (cairo_content_t content, int width, int height) @@ -402,7 +434,7 @@ _cairo_image_surface_create_with_content (cairo_content_t content, * * int stride; * unsigned char *data; - * #cairo_surface_t *surface; + * cairo_surface_t *surface; * * stride = cairo_format_stride_for_width (format, width); * data = malloc (stride * height); @@ -417,7 +449,7 @@ _cairo_image_surface_create_with_content (cairo_content_t content, * * Since: 1.6 **/ -int + int cairo_format_stride_for_width (cairo_format_t format, int width) { @@ -478,8 +510,10 @@ slim_hidden_def (cairo_format_stride_for_width); * * See cairo_surface_set_user_data() for a means of attaching a * destroy-notification fallback to the surface if necessary. + * + * Since: 1.0 **/ -cairo_surface_t * + cairo_surface_t * cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, @@ -524,6 +558,11 @@ slim_hidden_def (cairo_image_surface_create_for_data); * Get a pointer to the data of the image surface, for direct * inspection or modification. * + * A call to cairo_surface_flush() is required before accessing the + * pixel data to ensure that all pending drawing operations are + * finished. A call to cairo_surface_mark_dirty() is required after + * the data is modified. + * * Return value: a pointer to the image data of this surface or %NULL * if @surface is not an image surface, or if cairo_surface_finish() * has been called. @@ -575,6 +614,8 @@ slim_hidden_def (cairo_image_surface_get_format); * Get the width of the image surface in pixels. * * Return value: the width of the surface in pixels. + * + * Since: 1.0 **/ int cairo_image_surface_get_width (cairo_surface_t *surface) @@ -597,6 +638,8 @@ slim_hidden_def (cairo_image_surface_get_width); * Get the height of the image surface in pixels. * * Return value: the height of the surface in pixels. + * + * Since: 1.0 **/ int cairo_image_surface_get_height (cairo_surface_t *surface) @@ -640,7 +683,7 @@ cairo_image_surface_get_stride (cairo_surface_t *surface) } slim_hidden_def (cairo_image_surface_get_stride); -cairo_format_t + cairo_format_t _cairo_format_from_content (cairo_content_t content) { switch (content) { @@ -656,12 +699,16 @@ _cairo_format_from_content (cairo_content_t content) return CAIRO_FORMAT_INVALID; } -cairo_content_t + cairo_content_t _cairo_content_from_format (cairo_format_t format) { switch (format) { + case CAIRO_FORMAT_RGBA128F: case CAIRO_FORMAT_ARGB32: return CAIRO_CONTENT_COLOR_ALPHA; + case CAIRO_FORMAT_RGB96F: + case CAIRO_FORMAT_RGB30: + return CAIRO_CONTENT_COLOR; case CAIRO_FORMAT_RGB24: return CAIRO_CONTENT_COLOR; case CAIRO_FORMAT_RGB16_565: @@ -677,12 +724,16 @@ _cairo_content_from_format (cairo_format_t format) return CAIRO_CONTENT_COLOR_ALPHA; } -int + int _cairo_format_bits_per_pixel (cairo_format_t format) { switch (format) { + case CAIRO_FORMAT_RGBA128F: + return 128; + case CAIRO_FORMAT_RGB96F: + return 96; case CAIRO_FORMAT_ARGB32: - return 32; + case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_RGB24: return 32; case CAIRO_FORMAT_RGB16_565: @@ -698,7 +749,7 @@ _cairo_format_bits_per_pixel (cairo_format_t format) } } -static cairo_surface_t * +cairo_surface_t * _cairo_image_surface_create_similar (void *abstract_other, cairo_content_t content, int width, @@ -706,6 +757,8 @@ _cairo_image_surface_create_similar (void *abstract_other, { cairo_image_surface_t *other = abstract_other; + TRACE ((stderr, "%s (other=%u)\n", __FUNCTION__, other->base.unique_id)); + if (! _cairo_image_surface_is_size_valid (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); @@ -720,7 +773,87 @@ _cairo_image_surface_create_similar (void *abstract_other, width, height); } -static cairo_status_t +cairo_surface_t * +_cairo_image_surface_snapshot (void *abstract_surface) +{ + cairo_image_surface_t *image = abstract_surface; + cairo_image_surface_t *clone; + + /* If we own the image, we can simply steal the memory for the snapshot */ + if (image->owns_data && image->base._finishing) { + clone = (cairo_image_surface_t *) + _cairo_image_surface_create_for_pixman_image (image->pixman_image, + image->pixman_format); + if (unlikely (clone->base.status)) + return &clone->base; + + image->pixman_image = NULL; + image->owns_data = FALSE; + + clone->transparency = image->transparency; + clone->color = image->color; + + clone->owns_data = TRUE; + return &clone->base; + } + + clone = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + image->pixman_format, + image->width, + image->height, + 0); + if (unlikely (clone->base.status)) + return &clone->base; + + if (clone->stride == image->stride) { + memcpy (clone->data, image->data, clone->stride * clone->height); + } else { + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + image->width, image->height); + } + clone->base.is_clear = FALSE; + return &clone->base; +} + +cairo_image_surface_t * +_cairo_image_surface_map_to_image (void *abstract_other, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *other = abstract_other; + cairo_surface_t *surface; + uint8_t *data; + + data = other->data; + data += extents->y * other->stride; + data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8; + + surface = + _cairo_image_surface_create_with_pixman_format (data, + other->pixman_format, + extents->width, + extents->height, + other->stride); + + cairo_surface_set_device_offset (surface, -extents->x, -extents->y); + return (cairo_image_surface_t *) surface; +} + +cairo_int_status_t +_cairo_image_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_status_t _cairo_image_surface_finish (void *abstract_surface) { cairo_image_surface_t *surface = abstract_surface; @@ -735,6 +868,12 @@ _cairo_image_surface_finish (void *abstract_surface) surface->data = NULL; } + if (surface->parent) { + cairo_surface_t *parent = surface->parent; + surface->parent = NULL; + cairo_surface_destroy (parent); + } + return CAIRO_STATUS_SUCCESS; } @@ -744,7 +883,22 @@ _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) surface->owns_data = TRUE; } -static cairo_status_t +cairo_surface_t * +_cairo_image_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *surface = abstract_surface; + + if (extents) { + extents->x = extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + } + + return &surface->base; +} + +cairo_status_t _cairo_image_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) @@ -755,3408 +909,15 @@ _cairo_image_surface_acquire_source_image (void *abstract_sur return CAIRO_STATUS_SUCCESS; } -static void +void _cairo_image_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { } -/* XXX: I think we should fix pixman to match the names/order of the - * cairo operators, but that will likely be better done at the same - * time the X server is ported to pixman, (which will change a lot of - * things in pixman I think). - */ -static pixman_op_t -_pixman_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return PIXMAN_OP_CLEAR; - - case CAIRO_OPERATOR_SOURCE: - return PIXMAN_OP_SRC; - case CAIRO_OPERATOR_OVER: - return PIXMAN_OP_OVER; - case CAIRO_OPERATOR_IN: - return PIXMAN_OP_IN; - case CAIRO_OPERATOR_OUT: - return PIXMAN_OP_OUT; - case CAIRO_OPERATOR_ATOP: - return PIXMAN_OP_ATOP; - - case CAIRO_OPERATOR_DEST: - return PIXMAN_OP_DST; - case CAIRO_OPERATOR_DEST_OVER: - return PIXMAN_OP_OVER_REVERSE; - case CAIRO_OPERATOR_DEST_IN: - return PIXMAN_OP_IN_REVERSE; - case CAIRO_OPERATOR_DEST_OUT: - return PIXMAN_OP_OUT_REVERSE; - case CAIRO_OPERATOR_DEST_ATOP: - return PIXMAN_OP_ATOP_REVERSE; - - case CAIRO_OPERATOR_XOR: - return PIXMAN_OP_XOR; - case CAIRO_OPERATOR_ADD: - return PIXMAN_OP_ADD; - case CAIRO_OPERATOR_SATURATE: - return PIXMAN_OP_SATURATE; - - case CAIRO_OPERATOR_MULTIPLY: - return PIXMAN_OP_MULTIPLY; - case CAIRO_OPERATOR_SCREEN: - return PIXMAN_OP_SCREEN; - case CAIRO_OPERATOR_OVERLAY: - return PIXMAN_OP_OVERLAY; - case CAIRO_OPERATOR_DARKEN: - return PIXMAN_OP_DARKEN; - case CAIRO_OPERATOR_LIGHTEN: - return PIXMAN_OP_LIGHTEN; - case CAIRO_OPERATOR_COLOR_DODGE: - return PIXMAN_OP_COLOR_DODGE; - case CAIRO_OPERATOR_COLOR_BURN: - return PIXMAN_OP_COLOR_BURN; - case CAIRO_OPERATOR_HARD_LIGHT: - return PIXMAN_OP_HARD_LIGHT; - case CAIRO_OPERATOR_SOFT_LIGHT: - return PIXMAN_OP_SOFT_LIGHT; - case CAIRO_OPERATOR_DIFFERENCE: - return PIXMAN_OP_DIFFERENCE; - case CAIRO_OPERATOR_EXCLUSION: - return PIXMAN_OP_EXCLUSION; - case CAIRO_OPERATOR_HSL_HUE: - return PIXMAN_OP_HSL_HUE; - case CAIRO_OPERATOR_HSL_SATURATION: - return PIXMAN_OP_HSL_SATURATION; - case CAIRO_OPERATOR_HSL_COLOR: - return PIXMAN_OP_HSL_COLOR; - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return PIXMAN_OP_HSL_LUMINOSITY; - - default: - ASSERT_NOT_REACHED; - return PIXMAN_OP_OVER; - } -} - -static cairo_status_t -_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, - cairo_region_t *region) -{ - if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface) -{ - pixman_image_set_clip_region32 (surface->pixman_image, NULL); -} - -static double -_pixman_nearest_sample (double d) -{ - return ceil (d - .5); -} - -static cairo_bool_t -_nearest_sample (cairo_filter_t filter, double *tx, double *ty) -{ - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { - *tx = _pixman_nearest_sample (*tx); - *ty = _pixman_nearest_sample (*ty); - } else { - if (*tx != floor (*tx) || *ty != floor (*ty)) - return FALSE; - } - return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; -} - -#if PIXMAN_HAS_ATOMIC_OPS -static pixman_image_t *__pixman_transparent_image; -static pixman_image_t *__pixman_black_image; -static pixman_image_t *__pixman_white_image; - -static pixman_image_t * -_pixman_transparent_image (void) -{ - pixman_image_t *image; - - image = __pixman_transparent_image; - if (unlikely (image == NULL)) { - pixman_color_t color; - - color.red = 0x00; - color.green = 0x00; - color.blue = 0x00; - color.alpha = 0x00; - - image = pixman_image_create_solid_fill (&color); - if (unlikely (image == NULL)) - return NULL; - - if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } - - return image; -} - -static pixman_image_t * -_pixman_black_image (void) -{ - pixman_image_t *image; - - image = __pixman_black_image; - if (unlikely (image == NULL)) { - pixman_color_t color; - - color.red = 0x00; - color.green = 0x00; - color.blue = 0x00; - color.alpha = 0xffff; - - image = pixman_image_create_solid_fill (&color); - if (unlikely (image == NULL)) - return NULL; - - if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } - - return image; -} - -static pixman_image_t * -_pixman_white_image (void) -{ - pixman_image_t *image; - - image = __pixman_white_image; - if (unlikely (image == NULL)) { - pixman_color_t color; - - color.red = 0xffff; - color.green = 0xffff; - color.blue = 0xffff; - color.alpha = 0xffff; - - image = pixman_image_create_solid_fill (&color); - if (unlikely (image == NULL)) - return NULL; - - if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } - - return image; -} - -static uint32_t -hars_petruska_f54_1_random (void) -{ -#define rol(x,k) ((x << k) | (x >> (32-k))) - static uint32_t x; - return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; -#undef rol -} - -static struct { - cairo_color_t color; - pixman_image_t *image; -} cache[16]; -static int n_cached; - -#else /* !PIXMAN_HAS_ATOMIC_OPS */ -static pixman_image_t * -_pixman_transparent_image (void) -{ - return _pixman_image_for_solid (&_cairo_pattern_clear); -} - -static pixman_image_t * -_pixman_black_image (void) -{ - return _pixman_image_for_solid (&_cairo_pattern_black); -} - -static pixman_image_t * -_pixman_white_image (void) -{ - return _pixman_image_for_solid (&_cairo_pattern_white); -} -#endif /* !PIXMAN_HAS_ATOMIC_OPS */ - -void -_cairo_image_reset_static_data (void) -{ -#if PIXMAN_HAS_ATOMIC_OPS - while (n_cached) - pixman_image_unref (cache[--n_cached].image); - - if (__pixman_transparent_image) { - pixman_image_unref (__pixman_transparent_image); - __pixman_transparent_image = NULL; - } - - if (__pixman_black_image) { - pixman_image_unref (__pixman_black_image); - __pixman_black_image = NULL; - } - - if (__pixman_white_image) { - pixman_image_unref (__pixman_white_image); - __pixman_white_image = NULL; - } -#endif -} - -static pixman_image_t * -_pixman_image_for_solid (const cairo_solid_pattern_t *pattern) -{ - pixman_color_t color; - pixman_image_t *image; - -#if PIXMAN_HAS_ATOMIC_OPS - int i; - - if (pattern->color.alpha_short <= 0x00ff) - return _pixman_transparent_image (); - - if (pattern->color.alpha_short >= 0xff00) { - if (pattern->color.red_short <= 0x00ff && - pattern->color.green_short <= 0x00ff && - pattern->color.blue_short <= 0x00ff) - { - return _pixman_black_image (); - } - - if (pattern->color.red_short >= 0xff00 && - pattern->color.green_short >= 0xff00 && - pattern->color.blue_short >= 0xff00) - { - return _pixman_white_image (); - } - } - - CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); - for (i = 0; i < n_cached; i++) { - if (_cairo_color_equal (&cache[i].color, &pattern->color)) { - image = pixman_image_ref (cache[i].image); - goto UNLOCK; - } - } -#endif - - color.red = pattern->color.red_short; - color.green = pattern->color.green_short; - color.blue = pattern->color.blue_short; - color.alpha = pattern->color.alpha_short; - - image = pixman_image_create_solid_fill (&color); -#if PIXMAN_HAS_ATOMIC_OPS - if (image == NULL) - goto UNLOCK; - - if (n_cached < ARRAY_LENGTH (cache)) { - i = n_cached++; - } else { - i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); - pixman_image_unref (cache[i].image); - } - cache[i].image = pixman_image_ref (image); - cache[i].color = pattern->color; - -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); -#endif - return image; -} - -static double -clamp (double val, double min, double max) -{ - return val < min ? min : (val > max ? max : val); -} - -static pixman_image_t * -_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - int *ix, int *iy) -{ - pixman_image_t *pixman_image; - pixman_gradient_stop_t pixman_stops_static[2]; - pixman_gradient_stop_t *pixman_stops = pixman_stops_static; - cairo_matrix_t matrix = pattern->base.matrix; - double tx, ty; - unsigned int i; - - if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { - pixman_stops = _cairo_malloc_ab (pattern->n_stops, - sizeof(pixman_gradient_stop_t)); - if (unlikely (pixman_stops == NULL)) - return NULL; - } - - for (i = 0; i < pattern->n_stops; i++) { - pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); - pixman_stops[i].color.red = pattern->stops[i].color.red_short; - pixman_stops[i].color.green = pattern->stops[i].color.green_short; - pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; - pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - pixman_point_fixed_t p1, p2; - double x0, y0, x1, y1, maxabs; - - /* - * Transform the matrix to avoid overflow when converting between - * cairo_fixed_t and pixman_fixed_t (without incurring performance - * loss when the transformation is unnecessary). - * - * Having a function to compute the required transformation to - * "normalize" a given bounding box would be generally useful - - * cf linear patterns, gradient patterns, surface patterns... - */ - x0 = _cairo_fixed_to_double (linear->p1.x); - y0 = _cairo_fixed_to_double (linear->p1.y); - x1 = _cairo_fixed_to_double (linear->p2.x); - y1 = _cairo_fixed_to_double (linear->p2.y); - cairo_matrix_transform_point (&matrix, &x0, &y0); - cairo_matrix_transform_point (&matrix, &x1, &y1); - maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1))); - - if (maxabs > PIXMAN_MAX_INT) - { - double sf; - cairo_matrix_t scale; - - sf = PIXMAN_MAX_INT / maxabs; - - p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); - p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); - p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); - p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); - - /* cairo_matrix_scale does a pre-scale, we want a post-scale */ - cairo_matrix_init_scale (&scale, sf, sf); - cairo_matrix_multiply (&matrix, &matrix, &scale); - } - else - { - p1.x = _cairo_fixed_to_16_16 (linear->p1.x); - p1.y = _cairo_fixed_to_16_16 (linear->p1.y); - p2.x = _cairo_fixed_to_16_16 (linear->p2.x); - p2.y = _cairo_fixed_to_16_16 (linear->p2.y); - } - - pixman_image = pixman_image_create_linear_gradient (&p1, &p2, - pixman_stops, - pattern->n_stops); - } else { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - pixman_point_fixed_t c1, c2; - pixman_fixed_t r1, r2; - - c1.x = _cairo_fixed_to_16_16 (radial->c1.x); - c1.y = _cairo_fixed_to_16_16 (radial->c1.y); - r1 = _cairo_fixed_to_16_16 (radial->r1); - - c2.x = _cairo_fixed_to_16_16 (radial->c2.x); - c2.y = _cairo_fixed_to_16_16 (radial->c2.y); - r2 = _cairo_fixed_to_16_16 (radial->r2); - - pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2, - pixman_stops, - pattern->n_stops); - } - - if (pixman_stops != pixman_stops_static) - free (pixman_stops); - - if (unlikely (pixman_image == NULL)) - return NULL; - - tx = matrix.x0; - ty = matrix.y0; - if (! _cairo_matrix_is_translation (&matrix) || - ! _nearest_sample (pattern->base.filter, &tx, &ty)) - { - pixman_transform_t pixman_transform; - - if (tx != 0. || ty != 0.) { - cairo_matrix_t m, inv; - cairo_status_t status; - double x, y, max_x, max_y; - - /* Pixman also limits the [xy]_offset to 16 bits. We try to evenly - * spread the bits between the two, but we need to ensure that - * fabs (tx + extents->x + extents->width) < PIXMAN_MAX_INT && - * fabs (ty + extents->y + extents->height) < PIXMAN_MAX_INT, - * otherwise the gradient won't render. - */ - inv = matrix; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - x = _cairo_lround (inv.x0 / 2); - y = _cairo_lround (inv.y0 / 2); - - max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width); - x = clamp(x, -max_x, max_x); - max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height); - y = clamp(y, -max_y, max_y); - - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &matrix); - _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - } else { - tx = ty = 0; - _cairo_matrix_to_pixman_matrix (&pattern->base.matrix, - &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - } - - if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { - pixman_image_unref (pixman_image); - return NULL; - } - } - *ix = tx; - *iy = ty; - - { - pixman_repeat_t pixman_repeat; - - switch (pattern->base.extend) { - default: - case CAIRO_EXTEND_NONE: - pixman_repeat = PIXMAN_REPEAT_NONE; - break; - case CAIRO_EXTEND_REPEAT: - pixman_repeat = PIXMAN_REPEAT_NORMAL; - break; - case CAIRO_EXTEND_REFLECT: - pixman_repeat = PIXMAN_REPEAT_REFLECT; - break; - case CAIRO_EXTEND_PAD: - pixman_repeat = PIXMAN_REPEAT_PAD; - break; - } - - pixman_image_set_repeat (pixman_image, pixman_repeat); - } - - return pixman_image; -} - -struct acquire_source_cleanup { - cairo_surface_t *surface; - cairo_image_surface_t *image; - void *image_extra; -}; - -static void -_acquire_source_cleanup (pixman_image_t *pixman_image, - void *closure) -{ - struct acquire_source_cleanup *data = closure; - - _cairo_surface_release_source_image (data->surface, - data->image, - data->image_extra); - free (data); -} - -static cairo_filter_t -sampled_area (const cairo_surface_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - cairo_rectangle_int_t *sample) -{ - cairo_filter_t filter; - double x1, x2, y1, y2; - double pad; - - x1 = extents->x; - y1 = extents->y; - x2 = extents->x + (int) extents->width; - y2 = extents->y + (int) extents->height; - - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, - &x1, &y1, &x2, &y2, - NULL); - - filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); - sample->x = floor (x1 - pad); - sample->y = floor (y1 - pad); - sample->width = ceil (x2 + pad) - sample->x; - sample->height = ceil (y2 + pad) - sample->y; - - return filter; -} - -static uint16_t -expand_channel (uint16_t v, uint32_t bits) -{ - int offset = 16 - bits; - while (offset > 0) { - v |= v >> bits; - offset -= bits; - bits += bits; - } - return v; -} - -static pixman_image_t * -_pixel_to_solid (cairo_image_surface_t *image, int x, int y) -{ - uint32_t pixel; - pixman_color_t color; - - switch (image->format) { - default: - case CAIRO_FORMAT_INVALID: - ASSERT_NOT_REACHED; - return NULL; - - case CAIRO_FORMAT_A1: - pixel = *(uint8_t *) (image->data + y * image->stride + x/8); - return pixel & (1 << (x&7)) ? _pixman_white_image () : _pixman_transparent_image (); - - case CAIRO_FORMAT_A8: - color.alpha = *(uint8_t *) (image->data + y * image->stride + x); - color.alpha |= color.alpha << 8; - if (color.alpha == 0) - return _pixman_transparent_image (); - - color.red = color.green = color.blue = 0; - return pixman_image_create_solid_fill (&color); - - case CAIRO_FORMAT_RGB16_565: - pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x); - if (pixel == 0) - return _pixman_black_image (); - if (pixel == 0xffff) - return _pixman_white_image (); - - color.alpha = 0xffff; - color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5); - color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6); - color.blue = expand_channel ((pixel & 0x1f) << 11, 5); - return pixman_image_create_solid_fill (&color); - - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); - color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff; - if (color.alpha == 0) - return _pixman_transparent_image (); - if (pixel == 0xffffffff) - return _pixman_white_image (); - if (color.alpha == 0xffff && (pixel & 0xffffff) == 0) - return _pixman_black_image (); - - color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00); - color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00); - color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00); - return pixman_image_create_solid_fill (&color); - } -} - -static pixman_image_t * -_pixman_image_for_surface (const cairo_surface_pattern_t *pattern, - cairo_bool_t is_mask, - const cairo_rectangle_int_t *extents, - int *ix, int *iy) -{ - pixman_image_t *pixman_image; - cairo_rectangle_int_t sample; - cairo_extend_t extend; - cairo_filter_t filter; - double tx, ty; - - tx = pattern->base.matrix.x0; - ty = pattern->base.matrix.y0; - - extend = pattern->base.extend; - filter = sampled_area (pattern, extents, &sample); - - pixman_image = NULL; - if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && - (! is_mask || ! pattern->base.has_component_alpha || - (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0)) - { - cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; - cairo_surface_type_t type; - - if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) - source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target; - - type = source->base.backend->type; - if (type == CAIRO_SURFACE_TYPE_IMAGE) { - if (sample.width == 1 && sample.height == 1) { - if (sample.x < 0 || - sample.y < 0 || - sample.x >= source->width || - sample.y >= source->height) - { - if (extend == CAIRO_EXTEND_NONE) - return _pixman_transparent_image (); - } - else - { - return _pixel_to_solid (source, sample.x, sample.y); - } - } - -#if PIXMAN_HAS_ATOMIC_OPS - /* avoid allocating a 'pattern' image if we can reuse the original */ - if (extend == CAIRO_EXTEND_NONE && - _cairo_matrix_is_translation (&pattern->base.matrix) && - _nearest_sample (filter, &tx, &ty)) - { - *ix = tx; - *iy = ty; - return pixman_image_ref (source->pixman_image); - } -#endif - - pixman_image = pixman_image_create_bits (source->pixman_format, - source->width, - source->height, - (uint32_t *) source->data, - source->stride); - if (unlikely (pixman_image == NULL)) - return NULL; - } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub; - cairo_bool_t is_contained = FALSE; - - sub = (cairo_surface_subsurface_t *) source; - source = (cairo_image_surface_t *) sub->target; - - if (sample.x >= 0 && - sample.y >= 0 && - sample.x + sample.width <= sub->extents.width && - sample.y + sample.height <= sub->extents.height) - { - is_contained = TRUE; - } - - if (sample.width == 1 && sample.height == 1) { - if (is_contained) { - return _pixel_to_solid (source, - sub->extents.x + sample.x, - sub->extents.y + sample.y); - } else { - if (extend == CAIRO_EXTEND_NONE) - return _pixman_transparent_image (); - } - } - -#if PIXMAN_HAS_ATOMIC_OPS - if (is_contained && - _cairo_matrix_is_translation (&pattern->base.matrix) && - _nearest_sample (filter, &tx, &ty)) - { - *ix = tx + sub->extents.x; - *iy = ty + sub->extents.y; - return pixman_image_ref (source->pixman_image); - } -#endif - - /* Avoid sub-byte offsets, force a copy in that case. */ - if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { - void *data = source->data - + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 - + sub->extents.y * source->stride; - pixman_image = pixman_image_create_bits (source->pixman_format, - sub->extents.width, - sub->extents.height, - data, - source->stride); - if (unlikely (pixman_image == NULL)) - return NULL; - } - } - } - - if (pixman_image == NULL) { - struct acquire_source_cleanup *cleanup; - cairo_image_surface_t *image; - void *extra; - cairo_status_t status; - - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra); - if (unlikely (status)) - return NULL; - - if (sample.width == 1 && sample.height == 1) { - if (sample.x < 0 || - sample.y < 0 || - sample.x >= image->width || - sample.y >= image->height) - { - if (extend == CAIRO_EXTEND_NONE) { - pixman_image = _pixman_transparent_image (); - _cairo_surface_release_source_image (pattern->surface, image, extra); - return pixman_image; - } - } - else - { - pixman_image = _pixel_to_solid (image, sample.x, sample.y); - _cairo_surface_release_source_image (pattern->surface, image, extra); - return pixman_image; - } - } - - pixman_image = pixman_image_create_bits (image->pixman_format, - image->width, - image->height, - (uint32_t *) image->data, - image->stride); - if (unlikely (pixman_image == NULL)) { - _cairo_surface_release_source_image (pattern->surface, image, extra); - return NULL; - } - - cleanup = malloc (sizeof (*cleanup)); - if (unlikely (cleanup == NULL)) { - _cairo_surface_release_source_image (pattern->surface, image, extra); - pixman_image_unref (pixman_image); - return NULL; - } - - cleanup->surface = pattern->surface; - cleanup->image = image; - cleanup->image_extra = extra; - pixman_image_set_destroy_function (pixman_image, - _acquire_source_cleanup, cleanup); - } - - if (! _cairo_matrix_is_translation (&pattern->base.matrix) || - ! _nearest_sample (filter, &tx, &ty)) - { - pixman_transform_t pixman_transform; - cairo_matrix_t m; - - m = pattern->base.matrix; - if (m.x0 != 0. || m.y0 != 0.) { - cairo_matrix_t inv; - cairo_status_t status; - double x, y; - - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - inv = m; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - x = floor (inv.x0 / 2); - y = floor (inv.y0 / 2); - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &m); - } else { - tx = ty = 0; - } - - _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { - pixman_image_unref (pixman_image); - return NULL; - } - } - *ix = tx; - *iy = ty; - - if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) && - tx == pattern->base.matrix.x0 && - ty == pattern->base.matrix.y0) - { - pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); - } - else - { - pixman_filter_t pixman_filter; - - switch (filter) { - case CAIRO_FILTER_FAST: - pixman_filter = PIXMAN_FILTER_FAST; - break; - case CAIRO_FILTER_GOOD: - pixman_filter = PIXMAN_FILTER_GOOD; - break; - case CAIRO_FILTER_BEST: - pixman_filter = PIXMAN_FILTER_BEST; - break; - case CAIRO_FILTER_NEAREST: - pixman_filter = PIXMAN_FILTER_NEAREST; - break; - case CAIRO_FILTER_BILINEAR: - pixman_filter = PIXMAN_FILTER_BILINEAR; - break; - case CAIRO_FILTER_GAUSSIAN: - /* XXX: The GAUSSIAN value has no implementation in cairo - * whatsoever, so it was really a mistake to have it in the - * API. We could fix this by officially deprecating it, or - * else inventing semantics and providing an actual - * implementation for it. */ - default: - pixman_filter = PIXMAN_FILTER_BEST; - } - - pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); - } - - { - pixman_repeat_t pixman_repeat; - - switch (extend) { - default: - case CAIRO_EXTEND_NONE: - pixman_repeat = PIXMAN_REPEAT_NONE; - break; - case CAIRO_EXTEND_REPEAT: - pixman_repeat = PIXMAN_REPEAT_NORMAL; - break; - case CAIRO_EXTEND_REFLECT: - pixman_repeat = PIXMAN_REPEAT_REFLECT; - break; - case CAIRO_EXTEND_PAD: - pixman_repeat = PIXMAN_REPEAT_PAD; - break; - } - - pixman_image_set_repeat (pixman_image, pixman_repeat); - } - - if (pattern->base.has_component_alpha) - pixman_image_set_component_alpha (pixman_image, TRUE); - - return pixman_image; -} - -static pixman_image_t * -_pixman_image_for_pattern (const cairo_pattern_t *pattern, - cairo_bool_t is_mask, - const cairo_rectangle_int_t *extents, - int *tx, int *ty) -{ - *tx = *ty = 0; - - if (pattern == NULL) - return _pixman_white_image (); - - switch (pattern->type) { - default: - ASSERT_NOT_REACHED; - case CAIRO_PATTERN_TYPE_SOLID: - return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); - - case CAIRO_PATTERN_TYPE_RADIAL: - case CAIRO_PATTERN_TYPE_LINEAR: - return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, - extents, tx, ty); - - case CAIRO_PATTERN_TYPE_SURFACE: - return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern, - is_mask, extents, tx, ty); - } -} - -static cairo_status_t -_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst, - const cairo_composite_rectangles_t *rects, - cairo_clip_t *clip) -{ - pixman_image_t *mask = NULL; - pixman_box32_t boxes[4]; - int i, mask_x = 0, mask_y = 0, n_boxes = 0; - - if (clip != NULL) { - cairo_surface_t *clip_surface; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; - mask_x = -clip_x; - mask_y = -clip_y; - } else { - if (rects->bounded.width == rects->unbounded.width && - rects->bounded.height == rects->unbounded.height) - { - return CAIRO_STATUS_SUCCESS; - } - } - - /* wholly unbounded? */ - if (rects->bounded.width == 0 || rects->bounded.height == 0) { - int x = rects->unbounded.x; - int y = rects->unbounded.y; - int width = rects->unbounded.width; - int height = rects->unbounded.height; - - if (mask != NULL) { - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - x + mask_x, y + mask_y, - 0, 0, - x, y, - width, height); - } else { - pixman_color_t color = { 0, }; - pixman_box32_t box = { x, y, x + width, y + height }; - - if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, - dst->pixman_image, - &color, - 1, &box)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - return CAIRO_STATUS_SUCCESS; - } - - /* top */ - if (rects->bounded.y != rects->unbounded.y) { - boxes[n_boxes].x1 = rects->unbounded.x; - boxes[n_boxes].y1 = rects->unbounded.y; - boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; - boxes[n_boxes].y2 = rects->bounded.y; - n_boxes++; - } - - /* left */ - if (rects->bounded.x != rects->unbounded.x) { - boxes[n_boxes].x1 = rects->unbounded.x; - boxes[n_boxes].y1 = rects->bounded.y; - boxes[n_boxes].x2 = rects->bounded.x; - boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height; - n_boxes++; - } - - /* right */ - if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { - boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width; - boxes[n_boxes].y1 = rects->bounded.y; - boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; - boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height; - n_boxes++; - } - - /* bottom */ - if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { - boxes[n_boxes].x1 = rects->unbounded.x; - boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height; - boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width; - boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height; - n_boxes++; - } - - if (mask != NULL) { - for (i = 0; i < n_boxes; i++) { - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - boxes[i].x1 + mask_x, boxes[i].y1 + mask_y, - 0, 0, - boxes[i].x1, boxes[i].y1, - boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1); - } - } else { - pixman_color_t color = { 0, }; - - if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, - dst->pixman_image, - &color, - n_boxes, - boxes)) - { - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst, - const cairo_composite_rectangles_t *extents, - cairo_region_t *clip_region, - cairo_boxes_t *boxes) -{ - cairo_boxes_t clear; - cairo_box_t box; - cairo_status_t status; - struct _cairo_boxes_chunk *chunk; - int i; - - // If we have no boxes then we need to clear the entire extents - // because we have nothing to draw. - if (boxes->num_boxes < 1 && clip_region == NULL) { - int x = extents->unbounded.x; - int y = extents->unbounded.y; - int width = extents->unbounded.width; - int height = extents->unbounded.height; - - pixman_color_t color = { 0 }; - pixman_box32_t box = { x, y, x + width, y + height }; - - if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR, - dst->pixman_image, - &color, - 1, &box)) { - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - return CAIRO_STATUS_SUCCESS; - } - - _cairo_boxes_init (&clear); - - box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); - box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); - box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); - box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); - - if (clip_region == NULL) { - cairo_boxes_t tmp; - - _cairo_boxes_init (&tmp); - - status = _cairo_boxes_add (&tmp, &box); - assert (status == CAIRO_STATUS_SUCCESS); - - tmp.chunks.next = &boxes->chunks; - tmp.num_boxes += boxes->num_boxes; - - status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, - CAIRO_FILL_RULE_WINDING, - &clear); - - tmp.chunks.next = NULL; - } else { - pixman_box32_t *pbox; - - pbox = pixman_region32_rectangles (&clip_region->rgn, &i); - _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); - - status = _cairo_boxes_add (&clear, &box); - assert (status == CAIRO_STATUS_SUCCESS); - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - for (i = 0; i < chunk->count; i++) { - status = _cairo_boxes_add (&clear, &chunk->base[i]); - if (unlikely (status)) { - _cairo_boxes_fini (&clear); - return status; - } - } - } - - status = _cairo_bentley_ottmann_tessellate_boxes (&clear, - CAIRO_FILL_RULE_WINDING, - &clear); - } - - if (likely (status == CAIRO_STATUS_SUCCESS)) { - for (chunk = &clear.chunks; chunk != NULL; 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); - - x1 = (x1 < 0 ? 0 : x1); - y1 = (y1 < 0 ? 0 : y1); - if (x2 <= x1 || y2 <= y1) - continue; - pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), - PIXMAN_FORMAT_BPP (dst->pixman_format), - x1, y1, x2 - x1, y2 - y1, - 0); - } - } - } - - _cairo_boxes_fini (&clear); - - return status; -} - -static cairo_bool_t -can_reduce_alpha_op (cairo_operator_t op) -{ - int iop = op; - switch (iop) { - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_ADD: - return TRUE; - default: - return FALSE; - } -} - -static cairo_bool_t -reduce_alpha_op (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern) -{ - return dst->base.is_clear && - dst->base.content == CAIRO_CONTENT_ALPHA && - _cairo_pattern_is_opaque_solid (pattern) && - can_reduce_alpha_op (op); -} - -/* low level compositor */ -typedef cairo_status_t -(*image_draw_func_t) (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *src, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region); - -static pixman_image_t * -_create_composite_mask_pattern (cairo_clip_t *clip, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_region_t *clip_region = NULL; - pixman_image_t *mask; - cairo_status_t status; - cairo_bool_t need_clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (! _cairo_status_is_error (status)); - - /* The all-clipped state should never propagate this far. */ - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - - need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - - if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - } - - mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height, - NULL, 0); - if (unlikely (mask == NULL)) - return NULL; - - /* Is it worth setting the clip region here? */ - if (clip_region != NULL) { - pixman_bool_t ret; - - pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y); - ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn); - pixman_region32_translate (&clip_region->rgn, extents->x, extents->y); - - if (! ret) { - pixman_image_unref (mask); - return NULL; - } - } - - status = draw_func (draw_closure, - mask, PIXMAN_a8, - CAIRO_OPERATOR_ADD, NULL, - extents->x, extents->y, - extents, NULL); - if (unlikely (status)) { - pixman_image_unref (mask); - return NULL; - } - - if (need_clip_surface) { - cairo_surface_t *tmp; - - tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8); - if (unlikely (tmp->status)) { - pixman_image_unref (mask); - return NULL; - } - - pixman_image_ref (mask); - - status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y); - cairo_surface_destroy (tmp); - if (unlikely (status)) { - pixman_image_unref (mask); - return NULL; - } - } - - if (clip_region != NULL) - pixman_image_set_clip_region (mask, NULL); - - return mask; -} - -/* Handles compositing with a clip surface when the operator allows - * us to combine the clip with the mask - */ -static cairo_status_t -_clip_and_composite_with_mask (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *pattern, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - pixman_image_t *mask; - - mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (pattern == NULL) { - if (dst->pixman_format == PIXMAN_a8) { - pixman_image_composite32 (_pixman_operator (op), - mask, NULL, dst->pixman_image, - 0, 0, 0, 0, - extents->x, extents->y, - extents->width, extents->height); - } else { - pixman_image_t *src; - - src = _pixman_white_image (); - if (unlikely (src == NULL)) { - pixman_image_unref (mask); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - 0, 0, 0, 0, - extents->x, extents->y, - extents->width, extents->height); - pixman_image_unref (src); - } - } else { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - pixman_image_unref (mask); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - pixman_image_unref (src); - } - - pixman_image_unref (mask); - - return CAIRO_STATUS_SUCCESS; -} - -/* Handles compositing with a clip surface when we have to do the operation - * in two pieces and combine them together. - */ -static cairo_status_t -_clip_and_composite_combine (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - pixman_image_t *tmp; - cairo_surface_t *clip_surface; - int clip_x, clip_y; - cairo_status_t status; - - tmp = pixman_image_create_bits (dst->pixman_format, - extents->width, extents->height, - NULL, 0); - if (unlikely (tmp == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (src == NULL) { - status = (*draw_func) (draw_closure, - tmp, dst->pixman_format, - CAIRO_OPERATOR_ADD, NULL, - extents->x, extents->y, - extents, NULL); - } else { - /* Initialize the temporary surface from the destination surface */ - if (! dst->base.is_clear) { - pixman_image_composite32 (PIXMAN_OP_SRC, - dst->pixman_image, NULL, tmp, - extents->x, extents->y, - 0, 0, - 0, 0, - extents->width, extents->height); - } - - status = (*draw_func) (draw_closure, - tmp, dst->pixman_format, - op, src, - extents->x, extents->y, - extents, NULL); - } - if (unlikely (status)) - goto CLEANUP_SURFACE; - - assert (clip->path != NULL); - clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - goto CLEANUP_SURFACE; - - if (! dst->base.is_clear) { -#if PIXMAN_HAS_OP_LERP - pixman_image_composite32 (PIXMAN_OP_LERP, - tmp, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - dst->pixman_image, - 0, 0, - extents->x - clip_x, - extents->y - clip_y, - extents->x, extents->y, - extents->width, extents->height); -#else - /* Punch the clip out of the destination */ - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - NULL, dst->pixman_image, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - /* Now add the two results together */ - pixman_image_composite32 (PIXMAN_OP_ADD, - tmp, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - dst->pixman_image, - 0, 0, - extents->x - clip_x, - extents->y - clip_y, - extents->x, extents->y, - extents->width, extents->height); -#endif - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - tmp, - ((cairo_image_surface_t *) clip_surface)->pixman_image, - dst->pixman_image, - 0, 0, - extents->x - clip_x, - extents->y - clip_y, - extents->x, extents->y, - extents->width, extents->height); - } - - CLEANUP_SURFACE: - pixman_image_unref (tmp); - - return status; -} - -/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's - * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) - */ -static cairo_status_t -_clip_and_composite_source (cairo_clip_t *clip, - const cairo_pattern_t *pattern, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - pixman_image_t *mask, *src; - int src_x, src_y; - - if (pattern == NULL) { - cairo_region_t *clip_region; - cairo_status_t status; - - status = draw_func (draw_closure, - dst->pixman_image, dst->pixman_format, - CAIRO_OPERATOR_SOURCE, NULL, - extents->x, extents->y, - extents, NULL); - if (unlikely (status)) - return status; - - if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED) - status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0); - - return status; - } - - /* Create a surface that is mask IN clip */ - mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents); - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - pixman_image_unref (mask); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - if (! dst->base.is_clear) { -#if PIXMAN_HAS_OP_LERP - pixman_image_composite32 (PIXMAN_OP_LERP, - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); -#else - /* Compute dest' = dest OUT (mask IN clip) */ - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - 0, 0, 0, 0, - extents->x, extents->y, - extents->width, extents->height); - - /* Now compute (src IN (mask IN clip)) ADD dest' */ - pixman_image_composite32 (PIXMAN_OP_ADD, - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); -#endif - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - src, mask, dst->pixman_image, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height); - } - - pixman_image_unref (src); - pixman_image_unref (mask); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_clip_and_composite (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - image_draw_func_t draw_func, - void *draw_closure, - cairo_composite_rectangles_t*extents, - cairo_clip_t *clip) -{ - cairo_status_t status; - cairo_region_t *clip_region = NULL; - cairo_bool_t need_clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - if (unlikely (_cairo_status_is_error (status))) - return status; - - need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - - if (clip_region != NULL) { - cairo_rectangle_int_t rect; - cairo_bool_t is_empty; - - cairo_region_get_extents (clip_region, &rect); - is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect); - if (unlikely (is_empty)) - return CAIRO_STATUS_SUCCESS; - - is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect); - if (unlikely (is_empty && extents->is_bounded)) - return CAIRO_STATUS_SUCCESS; - - if (cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - } - } - - if (clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - } - - if (reduce_alpha_op (dst, op, src)) { - op = CAIRO_OPERATOR_ADD; - src = NULL; - } - - if (op == CAIRO_OPERATOR_SOURCE) { - status = _clip_and_composite_source (clip, src, - draw_func, draw_closure, - dst, &extents->bounded); - } else { - if (op == CAIRO_OPERATOR_CLEAR) { - src = NULL; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (need_clip_surface) { - if (extents->is_bounded) { - status = _clip_and_composite_with_mask (clip, op, src, - draw_func, draw_closure, - dst, &extents->bounded); - } else { - status = _clip_and_composite_combine (clip, op, src, - draw_func, draw_closure, - dst, &extents->bounded); - } - } else { - status = draw_func (draw_closure, - dst->pixman_image, dst->pixman_format, - op, src, - 0, 0, - &extents->bounded, - clip_region); - } - } - - if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { - status = _cairo_image_surface_fixup_unbounded (dst, extents, - need_clip_surface ? clip : NULL); - } - - if (clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) -#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) - -static cairo_bool_t -_line_exceeds_16_16 (const cairo_line_t *line) -{ - return - line->p1.x <= CAIRO_FIXED_16_16_MIN || - line->p1.x >= CAIRO_FIXED_16_16_MAX || - - line->p2.x <= CAIRO_FIXED_16_16_MIN || - line->p2.x >= CAIRO_FIXED_16_16_MAX || - - line->p1.y <= CAIRO_FIXED_16_16_MIN || - line->p1.y >= CAIRO_FIXED_16_16_MAX || - - line->p2.y <= CAIRO_FIXED_16_16_MIN || - line->p2.y >= CAIRO_FIXED_16_16_MAX; -} - -static void -_project_line_x_onto_16_16 (const cairo_line_t *line, - cairo_fixed_t top, - cairo_fixed_t bottom, - pixman_line_fixed_t *out) -{ - cairo_point_double_t p1, p2; - double m; - - p1.x = _cairo_fixed_to_double (line->p1.x); - p1.y = _cairo_fixed_to_double (line->p1.y); - - p2.x = _cairo_fixed_to_double (line->p2.x); - p2.y = _cairo_fixed_to_double (line->p2.y); - - m = (p2.x - p1.x) / (p2.y - p1.y); - out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); - out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); -} - - -typedef struct { - cairo_trapezoid_t *traps; - int num_traps; - cairo_antialias_t antialias; -} composite_traps_info_t; - -static void -_pixman_image_add_traps (pixman_image_t *image, - int dst_x, int dst_y, - composite_traps_info_t *info) -{ - cairo_trapezoid_t *t = info->traps; - int num_traps = info->num_traps; - while (num_traps--) { - pixman_trapezoid_t trap; - - /* top/bottom will be clamped to surface bounds */ - trap.top = _cairo_fixed_to_16_16 (t->top); - trap.bottom = _cairo_fixed_to_16_16 (t->bottom); - - /* However, all the other coordinates will have been left untouched so - * as not to introduce numerical error. Recompute them if they - * exceed the 16.16 limits. - */ - if (unlikely (_line_exceeds_16_16 (&t->left))) { - _project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left); - trap.left.p1.y = trap.top; - trap.left.p2.y = trap.bottom; - } else { - trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x); - trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y); - trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x); - trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y); - } - - if (unlikely (_line_exceeds_16_16 (&t->right))) { - _project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right); - trap.right.p1.y = trap.top; - trap.right.p2.y = trap.bottom; - } else { - trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x); - trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y); - trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x); - trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y); - } - - pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); - - t++; - } -} - -static cairo_status_t -_composite_traps (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - composite_traps_info_t *info = closure; - pixman_image_t *src, *mask; - pixman_format_code_t format; - int src_x = 0, src_y = 0; - cairo_status_t status; - - /* Special case adding trapezoids onto a mask surface; we want to avoid - * creating an intermediate temporary mask unnecessarily. - * - * We make the assumption here that the portion of the trapezoids - * contained within the surface is bounded by [dst_x,dst_y,width,height]; - * the Cairo core code passes bounds based on the trapezoid extents. - */ - format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8; - if (dst_format == format && - (pattern == NULL || - (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern)))) - { - _pixman_image_add_traps (dst, dst_x, dst_y, info); - return CAIRO_STATUS_SUCCESS; - } - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - mask = pixman_image_create_bits (format, extents->width, extents->height, - NULL, 0); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_SOURCE; - } - - _pixman_image_add_traps (mask, extents->x, extents->y, info); - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - - pixman_image_unref (mask); - - status = CAIRO_STATUS_SUCCESS; - CLEANUP_SOURCE: - pixman_image_unref (src); - - return status; -} - -static inline uint32_t -color_to_uint32 (const cairo_color_t *color) -{ - return - (color->alpha_short >> 8 << 24) | - (color->red_short >> 8 << 16) | - (color->green_short & 0xff00) | - (color->blue_short >> 8); -} - -static inline cairo_bool_t -color_to_pixel (const cairo_color_t *color, - pixman_format_code_t format, - uint32_t *pixel) -{ - uint32_t c; - - if (!(format == PIXMAN_a8r8g8b8 || - format == PIXMAN_x8r8g8b8 || - format == PIXMAN_a8b8g8r8 || - format == PIXMAN_x8b8g8r8 || - format == PIXMAN_b8g8r8a8 || - format == PIXMAN_b8g8r8x8 || - format == PIXMAN_r5g6b5 || - format == PIXMAN_b5g6r5 || - format == PIXMAN_a8)) - { - return FALSE; - } - - c = color_to_uint32 (color); - - if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) { - c = ((c & 0xff000000) >> 0) | - ((c & 0x00ff0000) >> 16) | - ((c & 0x0000ff00) >> 0) | - ((c & 0x000000ff) << 16); - } - - if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) { - c = ((c & 0xff000000) >> 24) | - ((c & 0x00ff0000) >> 8) | - ((c & 0x0000ff00) << 8) | - ((c & 0x000000ff) << 24); - } - - if (format == PIXMAN_a8) { - c = c >> 24; - } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) { - c = ((((c) >> 3) & 0x001f) | - (((c) >> 5) & 0x07e0) | - (((c) >> 8) & 0xf800)); - } - - *pixel = c; - return TRUE; -} - -static inline cairo_bool_t -pattern_to_pixel (const cairo_solid_pattern_t *solid, - cairo_operator_t op, - pixman_format_code_t format, - uint32_t *pixel) -{ - if (op == CAIRO_OPERATOR_CLEAR) { - *pixel = 0; - return TRUE; - } - - if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID) - return FALSE; - - if (op == CAIRO_OPERATOR_OVER) { - if (solid->color.alpha_short >= 0xff00) - op = CAIRO_OPERATOR_SOURCE; - } - - if (op != CAIRO_OPERATOR_SOURCE) - return FALSE; - - return color_to_pixel (&solid->color, format, pixel); -} - -typedef struct _fill_span { - cairo_span_renderer_t base; - - uint8_t *mask_data; - pixman_image_t *src, *dst, *mask; -} fill_span_renderer_t; - -static cairo_status_t -_fill_span (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - fill_span_renderer_t *renderer = abstract_renderer; - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - row = renderer->mask_data - spans[0].x; - for (i = 0; i < num_spans - 1; i++) { - /* We implement setting the most common single pixel wide - * span case to avoid the overhead of a memset call. - * Open coding setting longer spans didn't show a - * noticeable improvement over memset. - */ - if (spans[i+1].x == spans[i].x + 1) { - row[spans[i].x] = spans[i].coverage; - } else { - memset (row + spans[i].x, - spans[i].coverage, - spans[i+1].x - spans[i].x); - } - } - - do { - pixman_image_composite32 (PIXMAN_OP_OVER, - renderer->src, renderer->mask, renderer->dst, - 0, 0, 0, 0, - spans[0].x, y++, - spans[i].x - spans[0].x, 1); - } while (--height); - - return CAIRO_STATUS_SUCCESS; -} - -/* avoid using region code to re-validate boxes */ -static cairo_status_t -_fill_unaligned_boxes (cairo_image_surface_t *dst, - const cairo_pattern_t *pattern, - uint32_t pixel, - const cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *extents) -{ - uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; - fill_span_renderer_t renderer; - cairo_rectangular_scan_converter_t converter; - const struct _cairo_boxes_chunk *chunk; - cairo_status_t status; - int i; - - /* XXX - * using composite for fill: - * spiral-box-nonalign-evenodd-fill.512 2201957 2.202 - * spiral-box-nonalign-nonzero-fill.512 336726 0.337 - * spiral-box-pixalign-evenodd-fill.512 352256 0.352 - * spiral-box-pixalign-nonzero-fill.512 147056 0.147 - * using fill: - * spiral-box-nonalign-evenodd-fill.512 3174565 3.175 - * spiral-box-nonalign-nonzero-fill.512 182710 0.183 - * spiral-box-pixalign-evenodd-fill.512 353863 0.354 - * spiral-box-pixalign-nonzero-fill.512 147402 0.147 - * - * cairo-perf-trace seems to favour using fill. - */ - - renderer.base.render_rows = _fill_span; - renderer.dst = dst->pixman_image; - - if ((unsigned) extents->bounded.width <= sizeof (buf)) { - renderer.mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, 1, - (uint32_t *) buf, - sizeof (buf)); - } else { - renderer.mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, 1, - NULL, 0); - } - if (unlikely (renderer.mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask); - - renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern); - if (unlikely (renderer.src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_MASK; - } - - _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); - - /* first blit any aligned part of the boxes */ - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_ceil (box[i].p1.x); - int y1 = _cairo_fixed_integer_ceil (box[i].p1.y); - int x2 = _cairo_fixed_integer_floor (box[i].p2.x); - int y2 = _cairo_fixed_integer_floor (box[i].p2.y); - - x1 = (x1 < 0 ? 0 : x1); - y1 = (y1 < 0 ? 0 : y1); - if (x2 > x1 && y2 > y1) { - cairo_box_t b; - - pixman_fill ((uint32_t *) dst->data, - dst->stride / sizeof (uint32_t), - PIXMAN_FORMAT_BPP (dst->pixman_format), - x1, y1, x2 - x1, y2 - y1, - pixel); - - /* top */ - if (! _cairo_fixed_is_integer (box[i].p1.y)) { - b.p1.x = box[i].p1.x; - b.p1.y = box[i].p1.y; - b.p2.x = box[i].p2.x; - b.p2.y = _cairo_fixed_from_int (y1); - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - - /* left */ - if (! _cairo_fixed_is_integer (box[i].p1.x)) { - b.p1.x = box[i].p1.x; - b.p1.y = box[i].p1.y; - b.p2.x = _cairo_fixed_from_int (x1); - b.p2.y = box[i].p2.y; - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - - /* right */ - if (! _cairo_fixed_is_integer (box[i].p2.x)) { - b.p1.x = _cairo_fixed_from_int (x2); - b.p1.y = box[i].p1.y; - b.p2.x = box[i].p2.x; - b.p2.y = box[i].p2.y; - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - - /* bottom */ - if (! _cairo_fixed_is_integer (box[i].p2.y)) { - b.p1.x = box[i].p1.x; - b.p1.y = _cairo_fixed_from_int (y2); - b.p2.x = box[i].p2.x; - b.p2.y = box[i].p2.y; - - status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - } else { - status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - } - } - } - - status = converter.base.generate (&converter.base, &renderer.base); - - CLEANUP_CONVERTER: - converter.base.destroy (&converter.base); - pixman_image_unref (renderer.src); - CLEANUP_MASK: - pixman_image_unref (renderer.mask); - - return status; -} - -typedef struct _cairo_image_surface_span_renderer { - cairo_span_renderer_t base; - - uint8_t *mask_data; - uint32_t mask_stride; -} cairo_image_surface_span_renderer_t; - -cairo_status_t -_cairo_image_surface_span (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_image_surface_span_renderer_t *renderer = abstract_renderer; - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - /* XXX will it be quicker to repeat the sparse memset, - * or perform a simpler memcpy? - * The fairly dense spiral benchmarks suggests that the sparse - * memset is a win there as well. - */ - row = renderer->mask_data + y * renderer->mask_stride; - do { - for (i = 0; i < num_spans - 1; i++) { - if (! spans[i].coverage) - continue; - - /* We implement setting rendering the most common single - * pixel wide span case to avoid the overhead of a memset - * call. Open coding setting longer spans didn't show a - * noticeable improvement over memset. */ - if (spans[i+1].x == spans[i].x + 1) { - row[spans[i].x] = spans[i].coverage; - } else { - memset (row + spans[i].x, - spans[i].coverage, - spans[i+1].x - spans[i].x); - } - } - row += renderer->mask_stride; - } while (--height); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_composite_unaligned_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *extents) -{ - uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; - cairo_image_surface_span_renderer_t renderer; - cairo_rectangular_scan_converter_t converter; - pixman_image_t *mask, *src; - cairo_status_t status; - const struct _cairo_boxes_chunk *chunk; - int i, src_x, src_y; - - i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height; - if ((unsigned) i <= sizeof (buf)) { - mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, - extents->bounded.height, - (uint32_t *) buf, - CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8)); - memset (buf, 0, i); - } else { - mask = pixman_image_create_bits (PIXMAN_a8, - extents->bounded.width, - extents->bounded.height, - NULL, 0); - } - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - renderer.base.render_rows = _cairo_image_surface_span; - renderer.mask_stride = pixman_image_get_stride (mask); - renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); - renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x; - - _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); - if (unlikely (status)) - goto CLEANUP; - } - } - - status = converter.base.generate (&converter.base, &renderer.base); - if (unlikely (status)) - goto CLEANUP; - - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y); - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - extents->bounded.x + src_x, extents->bounded.y + src_y, - 0, 0, - extents->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.height); - pixman_image_unref (src); - - CLEANUP: - converter.base.destroy (&converter.base); - pixman_image_unref (mask); - - return status; -} - -static cairo_status_t -_composite_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_boxes_t *boxes, - cairo_antialias_t antialias, - cairo_clip_t *clip, - const cairo_composite_rectangles_t *extents) -{ - cairo_region_t *clip_region = NULL; - cairo_bool_t need_clip_mask = FALSE; - cairo_status_t status; - struct _cairo_boxes_chunk *chunk; - uint32_t pixel; - int i; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED; - if (need_clip_mask && - (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - } - - if (antialias != CAIRO_ANTIALIAS_NONE) { - if (! boxes->is_pixel_aligned) { - if (need_clip_mask) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, - dst->pixman_format, &pixel)) - { - return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents); - } - else - { - return _composite_unaligned_boxes (dst, op, pattern, boxes, extents); - } - } - } - - status = CAIRO_STATUS_SUCCESS; - if (! need_clip_mask && - pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format, - &pixel)) - { - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_round_down (box[i].p1.x); - int y1 = _cairo_fixed_integer_round_down (box[i].p1.y); - int x2 = _cairo_fixed_integer_round_down (box[i].p2.x); - int y2 = _cairo_fixed_integer_round_down (box[i].p2.y); - - x1 = (x1 < 0 ? 0 : x1); - y1 = (y1 < 0 ? 0 : y1); - if (x2 <= x1 || y2 <= y1) - continue; - - pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), - PIXMAN_FORMAT_BPP (dst->pixman_format), - x1, y1, x2 - x1, y2 - y1, - pixel); - } - } - } - else - { - pixman_image_t *src = NULL, *mask = NULL; - int src_x, src_y, mask_x = 0, mask_y = 0; - pixman_op_t pixman_op = _pixman_operator (op); - - if (need_clip_mask) { - cairo_surface_t *clip_surface; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - mask_x = -clip_x; - mask_y = -clip_y; - - if (op == CAIRO_OPERATOR_CLEAR) { - pattern = NULL; - pixman_op = PIXMAN_OP_OUT_REVERSE; - } - - mask = ((cairo_image_surface_t *) clip_surface)->pixman_image; - } - - if (pattern != NULL) { - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - src = mask; - src_x = mask_x; - src_y = mask_y; - mask = NULL; - } - - for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { - const cairo_box_t *box = chunk->base; - - for (i = 0; i < chunk->count; i++) { - int x1 = _cairo_fixed_integer_round_down (box[i].p1.x); - int y1 = _cairo_fixed_integer_round_down (box[i].p1.y); - int x2 = _cairo_fixed_integer_round_down (box[i].p2.x); - int y2 = _cairo_fixed_integer_round_down (box[i].p2.y); - - if (x2 == x1 || y2 == y1) - continue; - - pixman_image_composite32 (pixman_op, - src, mask, dst->pixman_image, - x1 + src_x, y1 + src_y, - x1 + mask_x, y1 + mask_y, - x1, y1, - x2 - x1, y2 - y1); - } - } - - if (pattern != NULL) - pixman_image_unref (src); - - if (! extents->is_bounded) { - status = - _cairo_image_surface_fixup_unbounded_boxes (dst, extents, - clip_region, boxes); - } - } - - return status; -} - -static cairo_status_t -_clip_and_composite_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_boxes_t *boxes, - cairo_antialias_t antialias, - cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - cairo_traps_t traps; - cairo_status_t status; - composite_traps_info_t info; - - if (boxes->num_boxes == 0 && extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - /* Use a fast path if the boxes are pixel aligned */ - status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* Otherwise render via a mask and composite in the usual fashion. */ - status = _cairo_traps_init_boxes (&traps, boxes); - if (unlikely (status)) - return status; - - info.num_traps = traps.num_traps; - info.traps = traps.traps; - info.antialias = antialias; - status = _clip_and_composite (dst, op, src, - _composite_traps, &info, - extents, clip); - - _cairo_traps_fini (&traps); - return status; -} - -static cairo_bool_t -_mono_edge_is_vertical (const cairo_line_t *line) -{ - return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); -} - -static cairo_bool_t -_traps_are_pixel_aligned (cairo_traps_t *traps, - cairo_antialias_t antialias) -{ - int i; - - if (antialias == CAIRO_ANTIALIAS_NONE) { - for (i = 0; i < traps->num_traps; i++) { - if (! _mono_edge_is_vertical (&traps->traps[i].left) || - ! _mono_edge_is_vertical (&traps->traps[i].right)) - { - traps->maybe_region = FALSE; - return FALSE; - } - } - } else { - for (i = 0; i < traps->num_traps; i++) { - if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || - traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || - ! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return FALSE; - } - } - } - - return TRUE; -} - -static void -_boxes_for_traps (cairo_boxes_t *boxes, - cairo_traps_t *traps, - cairo_antialias_t antialias) -{ - int i; - - _cairo_boxes_init (boxes); - - boxes->num_boxes = traps->num_traps; - boxes->chunks.base = (cairo_box_t *) traps->traps; - boxes->chunks.count = traps->num_traps; - boxes->chunks.size = traps->num_traps; - - if (antialias != CAIRO_ANTIALIAS_NONE) { - for (i = 0; i < traps->num_traps; i++) { - /* Note the traps and boxes alias so we need to take the local copies first. */ - cairo_fixed_t x1 = traps->traps[i].left.p1.x; - cairo_fixed_t x2 = traps->traps[i].right.p1.x; - cairo_fixed_t y1 = traps->traps[i].top; - cairo_fixed_t y2 = traps->traps[i].bottom; - - boxes->chunks.base[i].p1.x = x1; - boxes->chunks.base[i].p1.y = y1; - boxes->chunks.base[i].p2.x = x2; - boxes->chunks.base[i].p2.y = y2; - - if (boxes->is_pixel_aligned) { - boxes->is_pixel_aligned = - _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && - _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); - } - } - } else { - boxes->is_pixel_aligned = TRUE; - - for (i = 0; i < traps->num_traps; i++) { - /* Note the traps and boxes alias so we need to take the local copies first. */ - cairo_fixed_t x1 = traps->traps[i].left.p1.x; - cairo_fixed_t x2 = traps->traps[i].right.p1.x; - cairo_fixed_t y1 = traps->traps[i].top; - cairo_fixed_t y2 = traps->traps[i].bottom; - - /* round down here to match Pixman's behavior when using traps. */ - boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); - boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); - boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); - boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); - } - } -} - -static cairo_status_t -_clip_and_composite_trapezoids (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - composite_traps_info_t info; - cairo_bool_t need_clip_surface = FALSE; - cairo_status_t status; - - if (traps->num_traps == 0 && extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - cairo_region_t *clip_region; - - status = _cairo_clip_get_region (clip, &clip_region); - need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (traps->has_intersections) { - if (traps->is_rectangular) - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - else if (traps->is_rectilinear) - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - else - status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - return status; - } - - /* Use a fast path if the trapezoids consist of a simple region, - * but we can only do this if we do not have a clip surface, or can - * substitute the mask with the clip. - */ - if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) && - (! need_clip_surface || - (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) - { - cairo_boxes_t boxes; - - _boxes_for_traps (&boxes, traps, antialias); - return _clip_and_composite_boxes (dst, op, src, - &boxes, antialias, - extents, clip); - } - - /* No fast path, exclude self-intersections and clip trapezoids. */ - /* Otherwise render the trapezoids to a mask and composite in the usual - * fashion. - */ - info.traps = traps->traps; - info.num_traps = traps->num_traps; - info.antialias = antialias; - return _clip_and_composite (dst, op, src, - _composite_traps, &info, - extents, clip); -} - -static cairo_clip_path_t * -_clip_get_single_path (cairo_clip_t *clip) -{ - if (clip->path->prev == NULL) - return clip->path; - - return NULL; -} - /* high level image interface */ - -static cairo_int_status_t -_cairo_image_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_clip_path_t *clip_path; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - /* If the clip cannot be reduced to a set of boxes, we will need to - * use a clipmask. Paint is special as it is the only operation that - * does not implicitly use a mask, so we may be able to reduce this - * operation to a fill... - */ - if (clip != NULL && - extents.is_bounded && - (clip_path = _clip_get_single_path (clip)) != NULL) - { - status = _cairo_image_surface_fill (surface, op, source, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - } - else - { - cairo_boxes_t boxes; - - _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); - status = _clip_and_composite_boxes (surface, op, source, - &boxes, CAIRO_ANTIALIAS_DEFAULT, - &extents, clip); - } - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -static cairo_status_t -_composite_mask (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - const cairo_pattern_t *mask_pattern = closure; - pixman_image_t *src, *mask = NULL; - int src_x = 0, src_y = 0; - int mask_x = 0, mask_y = 0; - - if (src_pattern != NULL) { - src = _pixman_image_for_pattern (src_pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - mask = _pixman_image_for_pattern (mask_pattern, TRUE, extents, &mask_x, &mask_y); - if (unlikely (mask == NULL)) { - pixman_image_unref (src); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - if (mask_pattern->has_component_alpha) - pixman_image_set_component_alpha (mask, TRUE); - } else { - src = _pixman_image_for_pattern (mask_pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), src, mask, dst, - extents->x + src_x, extents->y + src_y, - extents->x + mask_x, extents->y + mask_y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - - if (mask != NULL) - pixman_image_unref (mask); - pixman_image_unref (src); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_image_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - clip = _cairo_clip_init_copy (&local_clip, clip); - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) { - _cairo_clip_fini (&local_clip); - return status; - } - - have_clip = TRUE; - } - - status = _clip_and_composite (surface, op, source, - _composite_mask, (void *) mask, - &extents, clip); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -typedef struct { - cairo_polygon_t *polygon; - cairo_fill_rule_t fill_rule; - cairo_antialias_t antialias; -} composite_spans_info_t; - -//#define USE_BOTOR_SCAN_CONVERTER -static cairo_status_t -_composite_spans (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE]; - composite_spans_info_t *info = closure; - cairo_image_surface_span_renderer_t renderer; -#if USE_BOTOR_SCAN_CONVERTER - cairo_box_t box; - cairo_botor_scan_converter_t converter; -#else - cairo_scan_converter_t *converter; -#endif - pixman_image_t *mask; - cairo_status_t status; - -#if USE_BOTOR_SCAN_CONVERTER - box.p1.x = _cairo_fixed_from_int (extents->x); - box.p1.y = _cairo_fixed_from_int (extents->y); - box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); - box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); - _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); - status = converter.base.add_polygon (&converter.base, info->polygon); -#else - converter = _cairo_tor_scan_converter_create (extents->x, extents->y, - extents->x + extents->width, - extents->y + extents->height, - info->fill_rule); - status = converter->add_polygon (converter, info->polygon); -#endif - if (unlikely (status)) - goto CLEANUP_CONVERTER; - - /* TODO: support rendering to A1 surfaces (or: go add span - * compositing to pixman.) */ - - if (pattern == NULL && - dst_format == PIXMAN_a8 && - op == CAIRO_OPERATOR_SOURCE) - { - mask = dst; - dst = NULL; - } - else - { - int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8); - uint8_t *data = mask_buf; - - if (extents->height * stride <= (int) sizeof (mask_buf)) - memset (data, 0, extents->height * stride); - else - data = NULL, stride = 0; - - mask = pixman_image_create_bits (PIXMAN_a8, - extents->width, - extents->height, - (uint32_t *) data, - stride); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_CONVERTER; - } - } - - renderer.base.render_rows = _cairo_image_surface_span; - renderer.mask_stride = pixman_image_get_stride (mask); - renderer.mask_data = (uint8_t *) pixman_image_get_data (mask); - if (dst != NULL) - renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; - else - renderer.mask_data -= dst_y * renderer.mask_stride + dst_x; - -#if USE_BOTOR_SCAN_CONVERTER - status = converter.base.generate (&converter.base, &renderer.base); -#else - status = converter->generate (converter, &renderer.base); -#endif - if (unlikely (status)) - goto CLEANUP_RENDERER; - - if (dst != NULL) { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_RENDERER; - } - - pixman_image_composite32 (_pixman_operator (op), src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - pixman_image_unref (src); - } - - CLEANUP_RENDERER: - if (dst != NULL) - pixman_image_unref (mask); - CLEANUP_CONVERTER: -#if USE_BOTOR_SCAN_CONVERTER - converter.base.destroy (&converter.base); -#else - converter->destroy (converter); -#endif - return status; -} - -static cairo_status_t -_clip_and_composite_polygon (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_polygon_t *polygon, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - cairo_composite_rectangles_t *extents, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (polygon->num_edges == 0) { - cairo_traps_t traps; - - if (extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - _cairo_traps_init (&traps); - status = _clip_and_composite_trapezoids (dst, op, src, - &traps, antialias, - extents, clip); - _cairo_traps_fini (&traps); - - return status; - } - - if (_cairo_operator_bounded_by_mask(op)) { - _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask); - if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask)) - return CAIRO_STATUS_SUCCESS; - } - - if (antialias != CAIRO_ANTIALIAS_NONE) { - composite_spans_info_t info; - - info.polygon = polygon; - info.fill_rule = fill_rule; - info.antialias = antialias; - - status = _clip_and_composite (dst, op, src, - _composite_spans, &info, - extents, clip); - } else { - cairo_traps_t traps; - - _cairo_traps_init (&traps); - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_trapezoids (dst, op, src, - &traps, antialias, - extents, clip); - } - - _cairo_traps_fini (&traps); - } - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - status = CAIRO_INT_STATUS_UNSUPPORTED; - if (path->is_rectilinear) { - cairo_boxes_t boxes; - - _cairo_boxes_init (&boxes); - _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); - - status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, - style, - ctm, - &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, antialias, - &extents, clip); - } - - _cairo_boxes_fini (&boxes); - } - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - cairo_polygon_t polygon; - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_stroke_to_polygon (path, - style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_polygon (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - } - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (extents.is_bounded && clip != NULL) { - cairo_clip_path_t *clip_path; - - if (((clip_path = _clip_get_single_path (clip)) != NULL) && - _cairo_path_fixed_equal (&clip_path->path, path)) - { - clip = NULL; - } - } - - if (clip != NULL) { - clip = _cairo_clip_init_copy (&local_clip, clip); - have_clip = TRUE; - } - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) { - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; - } - - if (_cairo_path_fixed_is_rectilinear_fill (path)) { - cairo_boxes_t boxes; - - _cairo_boxes_init (&boxes); - _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); - - status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, - fill_rule, - &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, antialias, - &extents, clip); - } - - _cairo_boxes_fini (&boxes); - } else { - cairo_polygon_t polygon; - - assert (! path->is_empty_fill); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents, clip); - } - - _cairo_polygon_fini (&polygon); - } - - if (clip_boxes != boxes_stack) - free (clip_boxes); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - return status; -} - -typedef struct { - cairo_scaled_font_t *font; - cairo_glyph_t *glyphs; - int num_glyphs; -} composite_glyphs_info_t; - -static cairo_status_t -_composite_glyphs_via_mask (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - composite_glyphs_info_t *info = closure; - cairo_scaled_font_t *font = info->font; - cairo_glyph_t *glyphs = info->glyphs; - int num_glyphs = info->num_glyphs; - pixman_image_t *mask = NULL; - pixman_image_t *src; - pixman_image_t *white; - pixman_format_code_t mask_format = 0; /* silence gcc */ - cairo_status_t status; - int src_x, src_y; - int i; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - white = _pixman_white_image (); - if (unlikely (white == NULL)) { - pixman_image_unref (src); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_scaled_font_freeze_cache (font); - - for (i = 0; i < num_glyphs; i++) { - int x, y; - cairo_image_surface_t *glyph_surface; - cairo_scaled_glyph_t *scaled_glyph; - - status = _cairo_scaled_glyph_lookup (font, glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - - if (unlikely (status)) - goto CLEANUP; - - glyph_surface = scaled_glyph->surface; - - if (glyph_surface->width == 0 || glyph_surface->height == 0) - continue; - - /* To start, create the mask using the format from the first - * glyph. Later we'll deal with different formats. */ - if (mask == NULL) { - mask_format = glyph_surface->pixman_format; - mask = pixman_image_create_bits (mask_format, - extents->width, extents->height, - NULL, 0); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - if (PIXMAN_FORMAT_RGB (mask_format)) - pixman_image_set_component_alpha (mask, TRUE); - } - - /* If we have glyphs of different formats, we "upgrade" the mask - * to the wider of the formats. */ - if (glyph_surface->pixman_format != mask_format && - PIXMAN_FORMAT_BPP (mask_format) < - PIXMAN_FORMAT_BPP (glyph_surface->pixman_format)) - { - pixman_image_t *new_mask; - - mask_format = glyph_surface->pixman_format; - new_mask = pixman_image_create_bits (mask_format, - extents->width, extents->height, - NULL, 0); - if (unlikely (new_mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - white, mask, new_mask, - 0, 0, 0, 0, 0, 0, - extents->width, extents->height); - - pixman_image_unref (mask); - mask = new_mask; - if (PIXMAN_FORMAT_RGB (mask_format)) - pixman_image_set_component_alpha (mask, TRUE); - } - - /* round glyph locations to the nearest pixel */ - /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ - x = _cairo_lround (glyphs[i].x - - glyph_surface->base.device_transform.x0); - y = _cairo_lround (glyphs[i].y - - glyph_surface->base.device_transform.y0); - if (glyph_surface->pixman_format == mask_format) { - pixman_image_composite32 (PIXMAN_OP_ADD, - glyph_surface->pixman_image, NULL, mask, - 0, 0, 0, 0, - x - extents->x, y - extents->y, - glyph_surface->width, - glyph_surface->height); - } else { - pixman_image_composite32 (PIXMAN_OP_ADD, - white, glyph_surface->pixman_image, mask, - 0, 0, 0, 0, - x - extents->x, y - extents->y, - glyph_surface->width, - glyph_surface->height); - } - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - -CLEANUP: - _cairo_scaled_font_thaw_cache (font); - if (mask != NULL) - pixman_image_unref (mask); - pixman_image_unref (src); - pixman_image_unref (white); - - return status; -} - -static cairo_status_t -_composite_glyphs (void *closure, - pixman_image_t *dst, - pixman_format_code_t dst_format, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - composite_glyphs_info_t *info = closure; - cairo_scaled_glyph_t *glyph_cache[64]; - pixman_op_t pixman_op = _pixman_operator (op); - pixman_image_t *src = NULL; - int src_x = 0, src_y = 0; - cairo_status_t status; - int i; - - memset (glyph_cache, 0, sizeof (glyph_cache)); - status = CAIRO_STATUS_SUCCESS; - - _cairo_scaled_font_freeze_cache (info->font); - for (i = 0; i < info->num_glyphs; i++) { - int x, y; - cairo_image_surface_t *glyph_surface; - cairo_scaled_glyph_t *scaled_glyph; - unsigned long glyph_index = info->glyphs[i].index; - int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); - - scaled_glyph = glyph_cache[cache_index]; - if (scaled_glyph == NULL || - _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) - { - status = _cairo_scaled_glyph_lookup (info->font, glyph_index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - - if (unlikely (status)) - break; - - glyph_cache[cache_index] = scaled_glyph; - } - - glyph_surface = scaled_glyph->surface; - if (glyph_surface->width && glyph_surface->height) { - int x1, y1, x2, y2; - - /* round glyph locations to the nearest pixel */ - /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ - x = _cairo_lround (info->glyphs[i].x - - glyph_surface->base.device_transform.x0); - y = _cairo_lround (info->glyphs[i].y - - glyph_surface->base.device_transform.y0); - - x1 = x; - if (x1 < extents->x) - x1 = extents->x; - x2 = x + glyph_surface->width; - if (x2 > extents->x + extents->width) - x2 = extents->x + extents->width; - - y1 = y; - if (y1 < extents->y) - y1 = extents->y; - y2 = y + glyph_surface->height; - if (y2 > extents->y + extents->height) - y2 = extents->y + extents->height; - - if (glyph_surface->format == CAIRO_FORMAT_A8 || - glyph_surface->format == CAIRO_FORMAT_A1 || - (glyph_surface->format == CAIRO_FORMAT_ARGB32 && - pixman_image_get_component_alpha (glyph_surface->pixman_image))) - { - if (unlikely (src == NULL)) { - if (pattern != NULL) { - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - src_x -= dst_x; - src_y -= dst_y; - } else { - src = _pixman_white_image (); - } - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - } - } - - pixman_image_composite32 (pixman_op, - src, glyph_surface->pixman_image, dst, - x1 + src_x, y1 + src_y, - x1 - x, y1 - y, - x1 - dst_x, y1 - dst_y, - x2 - x1, y2 - y1); - } else { - pixman_image_composite32 (pixman_op, - glyph_surface->pixman_image, NULL, dst, - x1 - x, y1 - y, - 0, 0, - x1 - dst_x, y1 - dst_y, - x2 - x1, y2 - y1); - } - } - } - _cairo_scaled_font_thaw_cache (info->font); - - if (src != NULL) - pixman_image_unref (src); - - return status; -} - -static cairo_int_status_t -_cairo_image_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, - cairo_clip_t *clip, - int *num_remaining) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - composite_glyphs_info_t glyph_info; - cairo_clip_t local_clip; - cairo_bool_t have_clip = FALSE; -#ifdef MOZ_GFX_OPTIMIZE_MOBILE - // For performance reasons we don't want to use two passes for overlapping glyphs - // on mobile - cairo_bool_t overlap = FALSE; -#else - cairo_bool_t overlap; -#endif - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, -#ifdef MOZ_GFX_OPTIMIZE_MOBILE - NULL); -#else - &overlap); -#endif - - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_rectangle (clip, &extents.mask)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - clip = _cairo_clip_init_copy (&local_clip, clip); - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - - have_clip = TRUE; - } - - glyph_info.font = scaled_font; - glyph_info.glyphs = glyphs; - glyph_info.num_glyphs = num_glyphs; - - status = _clip_and_composite (surface, op, source, - overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs, - &glyph_info, - &extents, clip); - - if (have_clip) - _cairo_clip_fini (&local_clip); - - *num_remaining = 0; - return status; -} - -static cairo_bool_t +cairo_bool_t _cairo_image_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -4170,7 +931,102 @@ _cairo_image_surface_get_extents (void *abstract_surface, return TRUE; } -static void +cairo_int_status_t +_cairo_image_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = abstract_surface; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->base.unique_id)); + + return _cairo_compositor_paint (surface->compositor, + &surface->base, op, source, clip); +} + +cairo_int_status_t +_cairo_image_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_image_surface_t *surface = abstract_surface; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->base.unique_id)); + + return _cairo_compositor_mask (surface->compositor, + &surface->base, op, source, mask, clip); +} + +cairo_int_status_t +_cairo_image_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_image_surface_t *surface = abstract_surface; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->base.unique_id)); + + return _cairo_compositor_stroke (surface->compositor, &surface->base, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, clip); +} + +cairo_int_status_t +_cairo_image_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_image_surface_t *surface = abstract_surface; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->base.unique_id)); + + return _cairo_compositor_fill (surface->compositor, &surface->base, + op, source, path, + fill_rule, tolerance, antialias, + clip); +} + +cairo_int_status_t +_cairo_image_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_image_surface_t *surface = abstract_surface; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->base.unique_id)); + + return _cairo_compositor_glyphs (surface->compositor, &surface->base, + op, source, + glyphs, num_glyphs, scaled_font, + clip); +} + +void _cairo_image_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { @@ -4180,521 +1036,37 @@ _cairo_image_surface_get_font_options (void *abstract_surface, _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } -/* legacy interface kept for compatibility until surface-fallback is removed */ -static cairo_status_t -_cairo_image_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_image_surface_t *surface = abstract_surface; - - image_rect_out->x = 0; - image_rect_out->y = 0; - image_rect_out->width = surface->width; - image_rect_out->height = surface->height; - - *image_out = surface; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_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) -{ -} - -static cairo_status_t -_cairo_image_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_image_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_image_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *dst = abstract_dst; - cairo_composite_rectangles_t extents; - pixman_image_t *src; - int src_offset_x, src_offset_y; - cairo_status_t status; - - if (clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - } - - extents.source.x = src_x; - extents.source.y = src_y; - extents.source.width = width; - extents.source.height = height; - - extents.mask.x = mask_x; - extents.mask.y = mask_y; - extents.mask.width = width; - extents.mask.height = height; - - extents.bounded.x = dst_x; - extents.bounded.y = dst_y; - extents.bounded.width = width; - extents.bounded.height = height; - - extents.unbounded.x = 0; - extents.unbounded.y = 0; - extents.unbounded.width = dst->width; - extents.unbounded.height = dst->height; - - if (clip_region != NULL) { - cairo_rectangle_int_t rect; - - cairo_region_get_extents (clip_region, &rect); - if (! _cairo_rectangle_intersect (&extents.unbounded, &rect)) - return CAIRO_STATUS_SUCCESS; - } - - extents.is_bounded = _cairo_operator_bounded_by_either (op); - - src = _pixman_image_for_pattern (src_pattern, FALSE, &extents.source, &src_offset_x, &src_offset_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = CAIRO_STATUS_SUCCESS; - if (mask_pattern != NULL) { - pixman_image_t *mask; - int mask_offset_x, mask_offset_y; - - mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents.mask, &mask_offset_x, &mask_offset_y); - if (unlikely (mask == NULL)) { - pixman_image_unref (src); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst->pixman_image, - src_x + src_offset_x, - src_y + src_offset_y, - mask_x + mask_offset_x, - mask_y + mask_offset_y, - dst_x, dst_y, width, height); - - pixman_image_unref (mask); - } else { - pixman_image_composite32 (_pixman_operator (op), - src, NULL, dst->pixman_image, - src_x + src_offset_x, - src_y + src_offset_y, - 0, 0, - dst_x, dst_y, width, height); - } - - pixman_image_unref (src); - - if (! extents.is_bounded) - status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); - - if (clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -static cairo_int_status_t -_cairo_image_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_image_surface_t *surface = abstract_surface; - - pixman_color_t pixman_color; - pixman_box32_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; - pixman_box32_t *pixman_boxes = stack_boxes; - int i; - - cairo_int_status_t status; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - pixman_color.red = color->red_short; - pixman_color.green = color->green_short; - pixman_color.blue = color->blue_short; - pixman_color.alpha = color->alpha_short; - - if (num_rects > ARRAY_LENGTH (stack_boxes)) { - pixman_boxes = _cairo_malloc_ab (num_rects, sizeof (pixman_box32_t)); - if (unlikely (pixman_boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < num_rects; i++) { - pixman_boxes[i].x1 = rects[i].x; - pixman_boxes[i].y1 = rects[i].y; - pixman_boxes[i].x2 = rects[i].x + rects[i].width; - pixman_boxes[i].y2 = rects[i].y + rects[i].height; - } - - status = CAIRO_STATUS_SUCCESS; - if (! pixman_image_fill_boxes (_pixman_operator (op), - surface->pixman_image, - &pixman_color, - num_rects, - pixman_boxes)) - { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - if (pixman_boxes != stack_boxes) - free (pixman_boxes); - - return status; -} - -static cairo_int_status_t -_cairo_image_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_image_surface_t *dst = abstract_dst; - cairo_composite_rectangles_t extents; - cairo_pattern_union_t source_pattern; - composite_traps_info_t info; - cairo_status_t status; - - if (height == 0 || width == 0) - return CAIRO_STATUS_SUCCESS; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - extents.source.x = src_x; - extents.source.y = src_y; - extents.source.width = width; - extents.source.height = height; - - extents.mask.x = dst_x; - extents.mask.y = dst_y; - extents.mask.width = width; - extents.mask.height = height; - - extents.bounded.x = dst_x; - extents.bounded.y = dst_y; - extents.bounded.width = width; - extents.bounded.height = height; - - extents.unbounded.x = 0; - extents.unbounded.y = 0; - extents.unbounded.width = dst->width; - extents.unbounded.height = dst->height; - - if (clip_region != NULL) { - cairo_rectangle_int_t rect; - - cairo_region_get_extents (clip_region, &rect); - if (! _cairo_rectangle_intersect (&extents.unbounded, &rect)) - return CAIRO_STATUS_SUCCESS; - } - - extents.is_bounded = _cairo_operator_bounded_by_either (op); - - if (clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - return status; - } - - _cairo_pattern_init_static_copy (&source_pattern.base, pattern); - cairo_matrix_translate (&source_pattern.base.matrix, - src_x - extents.bounded.x, - src_y - extents.bounded.y); - - info.traps = traps; - info.num_traps = num_traps; - info.antialias = antialias; - status = _composite_traps (&info, - dst->pixman_image, - dst->pixman_format, - op, - &source_pattern.base, - 0, 0, - &extents.bounded, - clip_region); - - if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded) - status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL); - - if (clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -typedef struct _legacy_image_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_operator_t op; - const cairo_pattern_t *pattern; - cairo_antialias_t antialias; - cairo_region_t *clip_region; - - pixman_image_t *mask; - uint8_t *mask_data; - uint32_t mask_stride; - - cairo_image_surface_t *dst; - cairo_composite_rectangles_t composite_rectangles; -} legacy_image_surface_span_renderer_t; - -void -_cairo_image_surface_span_render_row ( - int y, - const cairo_half_open_span_t *spans, - unsigned num_spans, - uint8_t *data, - uint32_t stride) -{ - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return; - - row = data + y * stride; - for (i = 0; i < num_spans - 1; i++) { - if (! spans[i].coverage) - continue; - - /* We implement setting the most common single pixel wide - * span case to avoid the overhead of a memset call. - * Open coding setting longer spans didn't show a - * noticeable improvement over memset. - */ - if (spans[i+1].x == spans[i].x + 1) { - row[spans[i].x] = spans[i].coverage; - } else { - memset (row + spans[i].x, - spans[i].coverage, - spans[i+1].x - spans[i].x); - } - } -} - -static cairo_status_t -_cairo_image_surface_span_renderer_render_rows ( - void *abstract_renderer, - int y, - int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - legacy_image_surface_span_renderer_t *renderer = abstract_renderer; - while (height--) - _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_image_surface_span_renderer_destroy (void *abstract_renderer) -{ - legacy_image_surface_span_renderer_t *renderer = abstract_renderer; - if (renderer == NULL) - return; - - pixman_image_unref (renderer->mask); - - free (renderer); -} - -static cairo_status_t -_cairo_image_surface_span_renderer_finish (void *abstract_renderer) -{ - legacy_image_surface_span_renderer_t *renderer = abstract_renderer; - cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; - cairo_image_surface_t *dst = renderer->dst; - pixman_image_t *src; - int src_x, src_y; - cairo_status_t status; - - if (renderer->clip_region != NULL) { - status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region); - if (unlikely (status)) - return status; - } - - src = _pixman_image_for_pattern (renderer->pattern, FALSE, &rects->bounded, &src_x, &src_y); - if (src == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = CAIRO_STATUS_SUCCESS; - pixman_image_composite32 (_pixman_operator (renderer->op), - src, - renderer->mask, - dst->pixman_image, - rects->bounded.x + src_x, - rects->bounded.y + src_y, - 0, 0, - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height); - - if (! rects->is_bounded) - status = _cairo_image_surface_fixup_unbounded (dst, rects, NULL); - - if (renderer->clip_region != NULL) - _cairo_image_surface_unset_clip_region (dst); - - return status; -} - -static cairo_bool_t -_cairo_image_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias) -{ - return TRUE; - (void) op; - (void) pattern; - (void) abstract_dst; - (void) antialias; -} - -static cairo_span_renderer_t * -_cairo_image_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *dst = abstract_dst; - legacy_image_surface_span_renderer_t *renderer; - - renderer = calloc(1, sizeof(*renderer)); - if (unlikely (renderer == NULL)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - - renderer->base.destroy = _cairo_image_surface_span_renderer_destroy; - renderer->base.finish = _cairo_image_surface_span_renderer_finish; - renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows; - renderer->op = op; - renderer->pattern = pattern; - renderer->antialias = antialias; - renderer->dst = dst; - renderer->clip_region = clip_region; - - renderer->composite_rectangles = *rects; - - /* TODO: support rendering to A1 surfaces (or: go add span - * compositing to pixman.) */ - renderer->mask = pixman_image_create_bits (PIXMAN_a8, - rects->bounded.width, - rects->bounded.height, - NULL, 0); - if (renderer->mask == NULL) { - free (renderer); - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - } - - renderer->mask_stride = pixman_image_get_stride (renderer->mask); - renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride; - - return &renderer->base; -} - -/** - * _cairo_surface_is_image: - * @surface: a #cairo_surface_t - * - * Checks if a surface is an #cairo_image_surface_t - * - * Return value: %TRUE if the surface is an image surface - **/ -cairo_bool_t -_cairo_surface_is_image (const cairo_surface_t *surface) -{ - return surface->backend == &_cairo_image_surface_backend; -} - const cairo_surface_backend_t _cairo_image_surface_backend = { CAIRO_SURFACE_TYPE_IMAGE, - _cairo_image_surface_create_similar, _cairo_image_surface_finish, + + _cairo_default_context_create, + + _cairo_image_surface_create_similar, + NULL, /* create similar image */ + _cairo_image_surface_map_to_image, + _cairo_image_surface_unmap_image, + + _cairo_image_surface_source, _cairo_image_surface_acquire_source_image, _cairo_image_surface_release_source_image, - _cairo_image_surface_acquire_dest_image, - _cairo_image_surface_release_dest_image, - _cairo_image_surface_clone_similar, - _cairo_image_surface_composite, - _cairo_image_surface_fill_rectangles, - _cairo_image_surface_composite_trapezoids, - _cairo_image_surface_create_span_renderer, - _cairo_image_surface_check_span_renderer, + _cairo_image_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ + _cairo_image_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_image_surface_get_font_options, + NULL, /* flush */ - NULL, /* mark dirty */ - NULL, /* font_fini */ - NULL, /* glyph_fini */ + NULL, _cairo_image_surface_paint, _cairo_image_surface_mask, _cairo_image_surface_stroke, _cairo_image_surface_fill, + NULL, /* fill-stroke */ _cairo_image_surface_glyphs, - NULL, /* show_text_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ }; /* A convenience function for when one needs to coerce an image @@ -4704,7 +1076,6 @@ _cairo_image_surface_coerce (cairo_image_surface_t *surface) { return _cairo_image_surface_coerce_to_format (surface, _cairo_format_from_content (surface->base.content)); - } /* A convenience function for when one needs to coerce an image @@ -4744,45 +1115,246 @@ _cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, return clone; } -cairo_image_transparency_t -_cairo_image_analyze_transparency (cairo_image_surface_t *image) +cairo_image_surface_t * +_cairo_image_surface_create_from_image (cairo_image_surface_t *other, + pixman_format_code_t format, + int x, int y, + int width, int height, int stride) +{ + cairo_image_surface_t *surface; + cairo_status_t status; + pixman_image_t *image; + void *mem = NULL; + + status = other->base.status; + if (unlikely (status)) + goto cleanup; + + if (stride) { + mem = _cairo_malloc_ab (height, stride); + if (unlikely (mem == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } + } + + image = pixman_image_create_bits (format, width, height, mem, stride); + if (unlikely (image == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup_mem; + } + + surface = (cairo_image_surface_t *) + _cairo_image_surface_create_for_pixman_image (image, format); + if (unlikely (surface->base.status)) { + status = surface->base.status; + goto cleanup_image; + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + other->pixman_image, NULL, image, + x, y, + 0, 0, + 0, 0, + width, height); + surface->base.is_clear = FALSE; + surface->owns_data = mem != NULL; + + return surface; + +cleanup_image: + pixman_image_unref (image); +cleanup_mem: + free (mem); +cleanup: + return (cairo_image_surface_t *) _cairo_surface_create_in_error (status); +} + +static cairo_image_transparency_t +_cairo_image_compute_transparency (cairo_image_surface_t *image) { int x, y; - - if (image->transparency != CAIRO_IMAGE_UNKNOWN) - return image->transparency; + cairo_image_transparency_t transparency; if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) - return image->transparency = CAIRO_IMAGE_IS_OPAQUE; + return CAIRO_IMAGE_IS_OPAQUE; + + if (image->base.is_clear) + return CAIRO_IMAGE_HAS_BILEVEL_ALPHA; if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) { - if (image->format == CAIRO_FORMAT_A1) - return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; - else - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + if (image->format == CAIRO_FORMAT_A1) { + return CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + } else if (image->format == CAIRO_FORMAT_A8) { + for (y = 0; y < image->height; y++) { + uint8_t *alpha = (uint8_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, alpha++) { + if (*alpha > 0 && *alpha < 255) + return CAIRO_IMAGE_HAS_ALPHA; + } + } + return CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + } else { + return CAIRO_IMAGE_HAS_ALPHA; + } } if (image->format == CAIRO_FORMAT_RGB16_565) { - image->transparency = CAIRO_IMAGE_IS_OPAQUE; return CAIRO_IMAGE_IS_OPAQUE; } if (image->format != CAIRO_FORMAT_ARGB32) - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + return CAIRO_IMAGE_HAS_ALPHA; - image->transparency = CAIRO_IMAGE_IS_OPAQUE; + transparency = CAIRO_IMAGE_IS_OPAQUE; for (y = 0; y < image->height; y++) { uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); for (x = 0; x < image->width; x++, pixel++) { int a = (*pixel & 0xff000000) >> 24; if (a > 0 && a < 255) { - return image->transparency = CAIRO_IMAGE_HAS_ALPHA; + return CAIRO_IMAGE_HAS_ALPHA; } else if (a == 0) { - image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } } } - return image->transparency; + return transparency; +} + +cairo_image_transparency_t +_cairo_image_analyze_transparency (cairo_image_surface_t *image) +{ + if (_cairo_surface_is_snapshot (&image->base)) { + if (image->transparency == CAIRO_IMAGE_UNKNOWN) + image->transparency = _cairo_image_compute_transparency (image); + + return image->transparency; + } + + return _cairo_image_compute_transparency (image); +} + +static cairo_image_color_t +_cairo_image_compute_color (cairo_image_surface_t *image) +{ + int x, y; + cairo_image_color_t color; + + if (image->format == CAIRO_FORMAT_A1) + return CAIRO_IMAGE_IS_MONOCHROME; + + if (image->format == CAIRO_FORMAT_A8) + return CAIRO_IMAGE_IS_GRAYSCALE; + + if (image->format == CAIRO_FORMAT_ARGB32) { + color = CAIRO_IMAGE_IS_MONOCHROME; + for (y = 0; y < image->height; y++) { + uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, pixel++) { + int a = (*pixel & 0xff000000) >> 24; + int r = (*pixel & 0x00ff0000) >> 16; + int g = (*pixel & 0x0000ff00) >> 8; + int b = (*pixel & 0x000000ff); + if (a == 0) { + r = g = b = 0; + } else { + r = (r * 255 + a / 2) / a; + g = (g * 255 + a / 2) / a; + b = (b * 255 + a / 2) / a; + } + if (!(r == g && g == b)) + return CAIRO_IMAGE_IS_COLOR; + else if (r > 0 && r < 255) + color = CAIRO_IMAGE_IS_GRAYSCALE; + } + } + return color; + } + + if (image->format == CAIRO_FORMAT_RGB24) { + color = CAIRO_IMAGE_IS_MONOCHROME; + for (y = 0; y < image->height; y++) { + uint32_t *pixel = (uint32_t *) (image->data + y * image->stride); + + for (x = 0; x < image->width; x++, pixel++) { + int r = (*pixel & 0x00ff0000) >> 16; + int g = (*pixel & 0x0000ff00) >> 8; + int b = (*pixel & 0x000000ff); + if (!(r == g && g == b)) + return CAIRO_IMAGE_IS_COLOR; + else if (r > 0 && r < 255) + color = CAIRO_IMAGE_IS_GRAYSCALE; + } + } + return color; + } + + return CAIRO_IMAGE_IS_COLOR; +} + +cairo_image_color_t +_cairo_image_analyze_color (cairo_image_surface_t *image) +{ + if (_cairo_surface_is_snapshot (&image->base)) { + if (image->color == CAIRO_IMAGE_UNKNOWN_COLOR) + image->color = _cairo_image_compute_color (image); + + return image->color; + } + + return _cairo_image_compute_color (image); +} + +cairo_image_surface_t * +_cairo_image_surface_clone_subimage (cairo_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *image; + cairo_surface_pattern_t pattern; + cairo_status_t status; + + image = cairo_surface_create_similar_image (surface, + _cairo_format_from_content (surface->content), + extents->width, + extents->height); + if (image->status) + return to_image_surface (image); + + /* TODO: check me with non-identity device_transform. Should we + * clone the scaling, too? */ + cairo_surface_set_device_offset (image, + -extents->x, + -extents->y); + + _cairo_pattern_init_for_surface (&pattern, surface); + pattern.base.filter = CAIRO_FILTER_NEAREST; + + status = _cairo_surface_paint (image, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) + goto error; + + /* We use the parent as a flag during map-to-image/umap-image that the + * resultant image came from a fallback rather than as direct call + * to the backend's map_to_image(). Whilst we use it as a simple flag, + * we need to make sure the parent surface obeys the reference counting + * semantics and is consistent for all callers. + */ + _cairo_image_surface_set_parent (to_image_surface (image), + cairo_surface_reference (surface)); + + return to_image_surface (image); + +error: + cairo_surface_destroy (image); + return to_image_surface (_cairo_surface_create_in_error (status)); } diff --git a/gfx/cairo/cairo/src/cairo-line-inline.h b/gfx/cairo/cairo/src/cairo-line-inline.h new file mode 100644 index 000000000000..71cc5e7ebf42 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-line-inline.h @@ -0,0 +1,48 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2014 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. + * + */ + +#ifndef CAIRO_LINE_INLINE_H +#define CAIRO_LINE_INLINE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-fixed-private.h" +#include "cairo-line-private.h" + +static inline int +cairo_lines_equal (const cairo_line_t *a, const cairo_line_t *b) +{ + return (a->p1.x == b->p1.x && a->p1.y == b->p1.y && + a->p2.x == b->p2.x && a->p2.y == b->p2.y); +} + +#endif /* CAIRO_LINE_INLINE_H */ diff --git a/gfx/cairo/cairo/src/test-fallback-surface.h b/gfx/cairo/cairo/src/cairo-line-private.h similarity index 76% rename from gfx/cairo/cairo/src/test-fallback-surface.h rename to gfx/cairo/cairo/src/cairo-line-private.h index e70715113956..6ddcae18d6bc 100644 --- a/gfx/cairo/cairo/src/test-fallback-surface.h +++ b/gfx/cairo/cairo/src/cairo-line-private.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2005 Red Hat, Inc + * Copyright © 2014 Intel Corporation, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -27,24 +27,25 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Red Hat, Inc. + * The Initial Developer of the Original Code is University of Southern + * California. * - * Contributor(s): - * Carl Worth */ -#ifndef TEST_FALLBACK_SURFACE_H -#define TEST_FALLBACK_SURFACE_H +#ifndef CAIRO_LINE_PRIVATE_H +#define CAIRO_LINE_PRIVATE_H -#include "cairo.h" +#include "cairo-types-private.h" +#include "cairo-error-private.h" +#include "cairo-compiler-private.h" CAIRO_BEGIN_DECLS -cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height); +cairo_private int +_cairo_lines_compare_at_y (const cairo_line_t *a, + const cairo_line_t *b, + int y); CAIRO_END_DECLS -#endif /* TEST_FALLBACK_SURFACE_H */ +#endif /* CAIRO_LINE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-line.c b/gfx/cairo/cairo/src/cairo-line.c new file mode 100644 index 000000000000..2c1768eeb16d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-line.c @@ -0,0 +1,307 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2008 Chris Wilson + * Copyright © 2014 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 Keith Packard + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + * + */ + +#include "cairoint.h" + +#include "cairo-line-inline.h" +#include "cairo-slope-private.h" + +static int +line_compare_for_y_against_x (const cairo_line_t *a, + int32_t y, + int32_t x) +{ + int32_t adx, ady; + int32_t dx, dy; + cairo_int64_t L, R; + + if (x < a->p1.x && x < a->p2.x) + return 1; + if (x > a->p1.x && x > a->p2.x) + return -1; + + adx = a->p2.x - a->p1.x; + dx = x - a->p1.x; + + if (adx == 0) + return -dx; + if (dx == 0 || (adx ^ dx) < 0) + return adx; + + dy = y - a->p1.y; + ady = a->p2.y - a->p1.y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +/* + * We need to compare the x-coordinates of a pair of lines for a particular y, + * without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy + * - (Y - A_y) * A_dx * B_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 128 bit arithmetic. For certain, but common, + * input we can reduce this down to a single 32 bit compare by inspecting the + * deltas. + * + * (And put the burden of the work on developing fast 128 bit ops, which are + * required throughout the tessellator.) + * + * See the similar discussion for _slope_compare(). + */ +static int +lines_compare_x_for_y_general (const cairo_line_t *a, + const cairo_line_t *b, + int32_t y) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t dx = 0; + int32_t adx = 0, ady = 0; + int32_t bdx = 0, bdy = 0; + enum { + HAVE_NONE = 0x0, + HAVE_DX = 0x1, + HAVE_ADX = 0x2, + HAVE_DX_ADX = HAVE_DX | HAVE_ADX, + HAVE_BDX = 0x4, + HAVE_DX_BDX = HAVE_DX | HAVE_BDX, + HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, + HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX + } have_dx_adx_bdx = HAVE_ALL; + + ady = a->p2.y - a->p1.y; + adx = a->p2.x - a->p1.x; + if (adx == 0) + have_dx_adx_bdx &= ~HAVE_ADX; + + bdy = b->p2.y - b->p1.y; + bdx = b->p2.x - b->p1.x; + if (bdx == 0) + have_dx_adx_bdx &= ~HAVE_BDX; + + dx = a->p1.x - b->p1.x; + if (dx == 0) + have_dx_adx_bdx &= ~HAVE_DX; + +#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) +#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->p1.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->p1.y) + switch (have_dx_adx_bdx) { + default: + case HAVE_NONE: + return 0; + case HAVE_DX: + /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ + return dx; /* ady * bdy is positive definite */ + case HAVE_ADX: + /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ + return adx; /* bdy * (y - a->top.y) is positive definite */ + case HAVE_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy */ + return -bdx; /* ady * (y - b->top.y) is positive definite */ + case HAVE_ADX_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ + if ((adx ^ bdx) < 0) { + return adx; + } else if (a->p1.y == b->p1.y) { /* common origin */ + cairo_int64_t adx_bdy, bdx_ady; + + /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ + + adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } else + return _cairo_int128_cmp (A, B); + case HAVE_DX_ADX: + /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ + if ((-adx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t ady_dx, dy_adx; + + ady_dx = _cairo_int32x32_64_mul (ady, dx); + dy_adx = _cairo_int32x32_64_mul (a->p1.y - y, adx); + + return _cairo_int64_cmp (ady_dx, dy_adx); + } + case HAVE_DX_BDX: + /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ + if ((bdx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t bdy_dx, dy_bdx; + + bdy_dx = _cairo_int32x32_64_mul (bdy, dx); + dy_bdx = _cairo_int32x32_64_mul (y - b->p1.y, bdx); + + return _cairo_int64_cmp (bdy_dx, dy_bdx); + } + case HAVE_ALL: + /* XXX try comparing (a->p2.x - b->p2.x) et al */ + return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); + } +#undef B +#undef A +#undef L +} + +static int +lines_compare_x_for_y (const cairo_line_t *a, + const cairo_line_t *b, + int32_t y) +{ + /* If the sweep-line is currently on an end-point of a line, + * then we know its precise x value (and considering that we often need to + * compare events at end-points, this happens frequently enough to warrant + * special casing). + */ + enum { + HAVE_NEITHER = 0x0, + HAVE_AX = 0x1, + HAVE_BX = 0x2, + HAVE_BOTH = HAVE_AX | HAVE_BX + } have_ax_bx = HAVE_BOTH; + int32_t ax = 0, bx = 0; + + if (y == a->p1.y) + ax = a->p1.x; + else if (y == a->p2.y) + ax = a->p2.x; + else + have_ax_bx &= ~HAVE_AX; + + if (y == b->p1.y) + bx = b->p1.x; + else if (y == b->p2.y) + bx = b->p2.x; + else + have_ax_bx &= ~HAVE_BX; + + switch (have_ax_bx) { + default: + case HAVE_NEITHER: + return lines_compare_x_for_y_general (a, b, y); + case HAVE_AX: + return -line_compare_for_y_against_x (b, y, ax); + case HAVE_BX: + return line_compare_for_y_against_x (a, y, bx); + case HAVE_BOTH: + return ax - bx; + } +} + +static int bbox_compare (const cairo_line_t *a, + const cairo_line_t *b) +{ + int32_t amin, amax; + int32_t bmin, bmax; + + if (a->p1.x < a->p2.x) { + amin = a->p1.x; + amax = a->p2.x; + } else { + amin = a->p2.x; + amax = a->p1.x; + } + + if (b->p1.x < b->p2.x) { + bmin = b->p1.x; + bmax = b->p2.x; + } else { + bmin = b->p2.x; + bmax = b->p1.x; + } + + if (amax < bmin) + return -1; + + if (amin > bmax) + return +1; + + return 0; +} + +int +_cairo_lines_compare_at_y (const cairo_line_t *a, + const cairo_line_t *b, + int y) +{ + cairo_slope_t sa, sb; + int ret; + + if (cairo_lines_equal (a, b)) + return 0; + + /* Don't bother solving for abscissa if the edges' bounding boxes + * can be used to order them. + */ + ret = bbox_compare (a, b); + if (ret) + return ret; + + ret = lines_compare_x_for_y (a, b, y); + if (ret) + return ret; + + _cairo_slope_init (&sa, &a->p1, &a->p2); + _cairo_slope_init (&sb, &b->p1, &b->p2); + + return _cairo_slope_compare (&sb, &sa); +} diff --git a/gfx/cairo/cairo/src/cairo-list-inline.h b/gfx/cairo/cairo/src/cairo-list-inline.h new file mode 100644 index 000000000000..0955178d24fd --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-list-inline.h @@ -0,0 +1,215 @@ +/* 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 + * + */ + +#ifndef CAIRO_LIST_INLINE_H +#define CAIRO_LIST_INLINE_H + +#include "cairo-list-private.h" + +#define cairo_list_entry(ptr, type, member) \ + cairo_container_of(ptr, type, member) + +#define cairo_list_first_entry(ptr, type, member) \ + cairo_list_entry((ptr)->next, type, member) + +#define cairo_list_last_entry(ptr, type, member) \ + cairo_list_entry((ptr)->prev, type, member) + +#define cairo_list_foreach(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define cairo_list_foreach_entry(pos, type, head, member) \ + for (pos = cairo_list_entry((head)->next, type, member);\ + &pos->member != (head); \ + pos = cairo_list_entry(pos->member.next, type, member)) + +#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \ + for (pos = cairo_list_entry ((head)->next, type, member),\ + n = cairo_list_entry (pos->member.next, type, member);\ + &pos->member != (head); \ + pos = n, n = cairo_list_entry (n->member.next, type, member)) + +#define cairo_list_foreach_entry_reverse(pos, type, head, member) \ + for (pos = cairo_list_entry((head)->prev, type, member);\ + &pos->member != (head); \ + pos = cairo_list_entry(pos->member.prev, type, member)) + +#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \ + for (pos = cairo_list_entry((head)->prev, type, member),\ + n = cairo_list_entry (pos->member.prev, type, member);\ + &pos->member != (head); \ + pos = n, n = cairo_list_entry (n->member.prev, type, member)) + +#ifdef CAIRO_LIST_DEBUG +static inline void +_cairo_list_validate (const cairo_list_t *link) +{ + assert (link->next->prev == link); + assert (link->prev->next == link); +} +static inline void +cairo_list_validate (const cairo_list_t *head) +{ + cairo_list_t *link; + + cairo_list_foreach (link, head) + _cairo_list_validate (link); +} +static inline cairo_bool_t +cairo_list_is_empty (const cairo_list_t *head); +static inline void +cairo_list_validate_is_empty (const cairo_list_t *head) +{ + assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev)); +} +#else +#define _cairo_list_validate(link) +#define cairo_list_validate(head) +#define cairo_list_validate_is_empty(head) +#endif + +static inline void +cairo_list_init (cairo_list_t *entry) +{ + entry->next = entry; + entry->prev = entry; +} + +static inline void +__cairo_list_add (cairo_list_t *entry, + cairo_list_t *prev, + cairo_list_t *next) +{ + next->prev = entry; + entry->next = next; + entry->prev = prev; + prev->next = entry; +} + +static inline void +cairo_list_add (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + cairo_list_validate_is_empty (entry); + __cairo_list_add (entry, head, head->next); + cairo_list_validate (head); +} + +static inline void +cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + cairo_list_validate_is_empty (entry); + __cairo_list_add (entry, head->prev, head); + cairo_list_validate (head); +} + +static inline void +__cairo_list_del (cairo_list_t *prev, cairo_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void +_cairo_list_del (cairo_list_t *entry) +{ + __cairo_list_del (entry->prev, entry->next); +} + +static inline void +cairo_list_del (cairo_list_t *entry) +{ + _cairo_list_del (entry); + cairo_list_init (entry); +} + +static inline void +cairo_list_move (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + __cairo_list_del (entry->prev, entry->next); + __cairo_list_add (entry, head, head->next); + cairo_list_validate (head); +} + +static inline void +cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head) +{ + cairo_list_validate (head); + __cairo_list_del (entry->prev, entry->next); + __cairo_list_add (entry, head->prev, head); + cairo_list_validate (head); +} + +static inline void +cairo_list_swap (cairo_list_t *entry, cairo_list_t *other) +{ + __cairo_list_add (entry, other->prev, other->next); + cairo_list_init (other); +} + +static inline cairo_bool_t +cairo_list_is_first (const cairo_list_t *entry, + const cairo_list_t *head) +{ + cairo_list_validate (head); + return entry->prev == head; +} + +static inline cairo_bool_t +cairo_list_is_last (const cairo_list_t *entry, + const cairo_list_t *head) +{ + cairo_list_validate (head); + return entry->next == head; +} + +static inline cairo_bool_t +cairo_list_is_empty (const cairo_list_t *head) +{ + cairo_list_validate (head); + return head->next == head; +} + +static inline cairo_bool_t +cairo_list_is_singular (const cairo_list_t *head) +{ + cairo_list_validate (head); + return head->next == head || head->next == head->prev; +} + +#endif /* CAIRO_LIST_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-list-private.h b/gfx/cairo/cairo/src/cairo-list-private.h index ddfd0a4c6d44..9f39b668f82d 100644 --- a/gfx/cairo/cairo/src/cairo-list-private.h +++ b/gfx/cairo/cairo/src/cairo-list-private.h @@ -45,171 +45,4 @@ typedef struct _cairo_list { struct _cairo_list *next, *prev; } cairo_list_t; -#define cairo_list_entry(ptr, type, member) \ - cairo_container_of(ptr, type, member) - -#define cairo_list_first_entry(ptr, type, member) \ - cairo_list_entry((ptr)->next, type, member) - -#define cairo_list_last_entry(ptr, type, member) \ - cairo_list_entry((ptr)->prev, type, member) - -#define cairo_list_foreach(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -#define cairo_list_foreach_entry(pos, type, head, member) \ - for (pos = cairo_list_entry((head)->next, type, member);\ - &pos->member != (head); \ - pos = cairo_list_entry(pos->member.next, type, member)) - -#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \ - for (pos = cairo_list_entry ((head)->next, type, member),\ - n = cairo_list_entry (pos->member.next, type, member);\ - &pos->member != (head); \ - pos = n, n = cairo_list_entry (n->member.next, type, member)) - -#define cairo_list_foreach_entry_reverse(pos, type, head, member) \ - for (pos = cairo_list_entry((head)->prev, type, member);\ - &pos->member != (head); \ - pos = cairo_list_entry(pos->member.prev, type, member)) - -#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \ - for (pos = cairo_list_entry((head)->prev, type, member),\ - n = cairo_list_entry (pos->member.prev, type, member);\ - &pos->member != (head); \ - pos = n, n = cairo_list_entry (n->member.prev, type, member)) - -#ifdef CAIRO_LIST_DEBUG -static inline void -_cairo_list_validate (const cairo_list_t *link) -{ - assert (link->next->prev == link); - assert (link->prev->next == link); -} -static inline void -cairo_list_validate (const cairo_list_t *head) -{ - cairo_list_t *link; - - cairo_list_foreach (link, head) - _cairo_list_validate (link); -} -static inline cairo_bool_t -cairo_list_is_empty (const cairo_list_t *head); -static inline void -cairo_list_validate_is_empty (const cairo_list_t *head) -{ - assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev)); -} -#else -#define _cairo_list_validate(link) -#define cairo_list_validate(head) -#define cairo_list_validate_is_empty(head) -#endif - -static inline void -cairo_list_init (cairo_list_t *entry) -{ - entry->next = entry; - entry->prev = entry; -} - -static inline void -__cairo_list_add (cairo_list_t *entry, - cairo_list_t *prev, - cairo_list_t *next) -{ - next->prev = entry; - entry->next = next; - entry->prev = prev; - prev->next = entry; -} - -static inline void -cairo_list_add (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - cairo_list_validate_is_empty (entry); - __cairo_list_add (entry, head, head->next); - cairo_list_validate (head); -} - -static inline void -cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - cairo_list_validate_is_empty (entry); - __cairo_list_add (entry, head->prev, head); - cairo_list_validate (head); -} - -static inline void -__cairo_list_del (cairo_list_t *prev, cairo_list_t *next) -{ - next->prev = prev; - prev->next = next; -} - -static inline void -cairo_list_del (cairo_list_t *entry) -{ - __cairo_list_del (entry->prev, entry->next); - cairo_list_init (entry); -} - -static inline void -cairo_list_move (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - __cairo_list_del (entry->prev, entry->next); - __cairo_list_add (entry, head, head->next); - cairo_list_validate (head); -} - -static inline void -cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head) -{ - cairo_list_validate (head); - __cairo_list_del (entry->prev, entry->next); - __cairo_list_add (entry, head->prev, head); - cairo_list_validate (head); -} - -static inline void -cairo_list_swap (cairo_list_t *entry, cairo_list_t *other) -{ - __cairo_list_add (entry, other->prev, other->next); - cairo_list_init (other); -} - -static inline cairo_bool_t -cairo_list_is_first (const cairo_list_t *entry, - const cairo_list_t *head) -{ - cairo_list_validate (head); - return entry->prev == head; -} - -static inline cairo_bool_t -cairo_list_is_last (const cairo_list_t *entry, - const cairo_list_t *head) -{ - cairo_list_validate (head); - return entry->next == head; -} - -static inline cairo_bool_t -cairo_list_is_empty (const cairo_list_t *head) -{ - cairo_list_validate (head); - return head->next == head; -} - -static inline cairo_bool_t -cairo_list_is_singular (const cairo_list_t *head) -{ - cairo_list_validate (head); - return head->next == head || head->next == head->prev; -} - #endif /* CAIRO_LIST_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-lzw.c b/gfx/cairo/cairo/src/cairo-lzw.c index de7f99983763..f27b3c33842d 100644 --- a/gfx/cairo/cairo/src/cairo-lzw.c +++ b/gfx/cairo/cairo/src/cairo-lzw.c @@ -73,7 +73,7 @@ _lzw_buf_init (lzw_buf_t *buf, int size) buf->pending = 0; buf->pending_bits = 0; - buf->data = malloc (size); + buf->data = _cairo_malloc (size); if (unlikely (buf->data == NULL)) { buf->data_size = 0; buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); diff --git a/gfx/cairo/cairo/src/cairo-malloc-private.h b/gfx/cairo/cairo/src/cairo-malloc-private.h index e5776abd0c34..570f7cb0ee72 100644 --- a/gfx/cairo/cairo/src/cairo-malloc-private.h +++ b/gfx/cairo/cairo/src/cairo-malloc-private.h @@ -38,6 +38,7 @@ #define CAIRO_MALLOC_PRIVATE_H #include "cairo-wideint-private.h" +#include #if HAVE_MEMFAULT #include @@ -56,17 +57,17 @@ * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or size is 0. - */ + **/ #define _cairo_malloc(size) \ - ((size) ? malloc((unsigned) (size)) : NULL) + ((size) > 0 ? malloc((unsigned) (size)) : NULL) /** * _cairo_malloc_ab: - * @n: number of elements to allocate + * @a: number of elements to allocate * @size: size of each element * - * Allocates @n*@size memory using _cairo_malloc(), taking care to not + * Allocates @a*@size memory using _cairo_malloc(), taking care to not * overflow when doing the multiplication. Behaves much like * calloc(), except that the returned memory is not set to zero. * The memory should be freed using free(). @@ -76,7 +77,7 @@ * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or overflow. - */ + **/ #define _cairo_malloc_ab(a, size) \ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ @@ -85,10 +86,10 @@ /** * _cairo_realloc_ab: * @ptr: original pointer to block of memory to be resized - * @n: number of elements to allocate + * @a: number of elements to allocate * @size: size of each element * - * Reallocates @ptr a block of @n*@size memory using realloc(), taking + * Reallocates @ptr a block of @a*@size memory using realloc(), taking * care to not overflow when doing the multiplication. The memory * should be freed using free(). * @@ -98,7 +99,7 @@ * Return value: A pointer to the newly allocated memory, or %NULL in * case of realloc() failure or overflow (whereupon the original block * of memory * is left untouched). - */ + **/ #define _cairo_realloc_ab(ptr, a, size) \ ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ @@ -106,11 +107,11 @@ /** * _cairo_malloc_abc: - * @n: first factor of number of elements to allocate + * @a: first factor of number of elements to allocate * @b: second factor of number of elements to allocate * @size: size of each element * - * Allocates @n*@b*@size memory using _cairo_malloc(), taking care to not + * Allocates @a*@b*@size memory using _cairo_malloc(), taking care to not * overflow when doing the multiplication. Behaves like * _cairo_malloc_ab(). The memory should be freed using free(). * @@ -119,7 +120,7 @@ * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or overflow. - */ + **/ #define _cairo_malloc_abc(a, b, size) \ ((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \ @@ -128,21 +129,21 @@ /** * _cairo_malloc_ab_plus_c: - * @n: number of elements to allocate + * @a: number of elements to allocate * @size: size of each element - * @k: additional size to allocate + * @c: additional size to allocate * - * Allocates @n*@ksize+@k memory using _cairo_malloc(), taking care to not - * overflow when doing the arithmetic. Behaves like + * Allocates @a*@size+@c memory using _cairo_malloc(), taking care to not + * overflow when doing the arithmetic. Behaves similar to * _cairo_malloc_ab(). The memory should be freed using free(). * * Return value: A pointer to the newly allocated memory, or %NULL in * case of malloc() failure or overflow. - */ + **/ -#define _cairo_malloc_ab_plus_c(n, size, k) \ - ((size) && (unsigned) (n) >= INT32_MAX / (unsigned) (size) ? NULL : \ - (unsigned) (k) >= INT32_MAX - (unsigned) (n) * (unsigned) (size) ? NULL : \ - _cairo_malloc((unsigned) (n) * (unsigned) (size) + (unsigned) (k))) +#define _cairo_malloc_ab_plus_c(a, size, c) \ + ((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \ + (unsigned) (c) >= INT32_MAX - (unsigned) (a) * (unsigned) (size) ? NULL : \ + _cairo_malloc((unsigned) (a) * (unsigned) (size) + (unsigned) (c))) #endif /* CAIRO_MALLOC_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-mask-compositor.c b/gfx/cairo/cairo/src/cairo-mask-compositor.c new file mode 100644 index 000000000000..4d6b118dda58 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mask-compositor.c @@ -0,0 +1,1481 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson + */ + +/* This compositor renders the shape to a mask using an image surface + * then calls composite. + */ + +#include "cairoint.h" + +#include "cairo-clip-inline.h" +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-inline.h" +#include "cairo-region-private.h" +#include "cairo-surface-observer-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-subsurface-private.h" + +typedef cairo_int_status_t +(*draw_func_t) (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *src, + const cairo_rectangle_int_t *src_sample, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip); + +static void do_unaligned_row(void (*blt)(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage), + void *closure, + const cairo_box_t *b, + int tx, int y, int h, + uint16_t coverage) +{ + int x1 = _cairo_fixed_integer_part (b->p1.x) - tx; + int x2 = _cairo_fixed_integer_part (b->p2.x) - tx; + if (x2 > x1) { + if (! _cairo_fixed_is_integer (b->p1.x)) { + blt(closure, x1, y, 1, h, + coverage * (256 - _cairo_fixed_fractional_part (b->p1.x))); + x1++; + } + + if (x2 > x1) + blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8)); + + if (! _cairo_fixed_is_integer (b->p2.x)) + blt(closure, x2, y, 1, h, + coverage * _cairo_fixed_fractional_part (b->p2.x)); + } else + blt(closure, x1, y, 1, h, + coverage * (b->p2.x - b->p1.x)); +} + +static void do_unaligned_box(void (*blt)(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage), + void *closure, + const cairo_box_t *b, int tx, int ty) +{ + int y1 = _cairo_fixed_integer_part (b->p1.y) - ty; + int y2 = _cairo_fixed_integer_part (b->p2.y) - ty; + if (y2 > y1) { + if (! _cairo_fixed_is_integer (b->p1.y)) { + do_unaligned_row(blt, closure, b, tx, y1, 1, + 256 - _cairo_fixed_fractional_part (b->p1.y)); + y1++; + } + + if (y2 > y1) + do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256); + + if (! _cairo_fixed_is_integer (b->p2.y)) + do_unaligned_row(blt, closure, b, tx, y2, 1, + _cairo_fixed_fractional_part (b->p2.y)); + } else + do_unaligned_row(blt, closure, b, tx, y1, 1, + b->p2.y - b->p1.y); +} + +struct blt_in { + const cairo_mask_compositor_t *compositor; + cairo_surface_t *dst; +}; + +static void blt_in(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct blt_in *info = closure; + cairo_color_t color; + cairo_rectangle_int_t rect; + + if (coverage == 0xffff) + return; + + rect.x = x; + rect.y = y; + rect.width = w; + rect.height = h; + + _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff); + info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN, + &color, &rect, 1); +} + +static cairo_surface_t * +create_composite_mask (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + void *draw_closure, + draw_func_t draw_func, + draw_func_t mask_func, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *surface; + cairo_int_status_t status; + struct blt_in info; + int i; + + surface = _cairo_surface_create_scratch (dst, CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (surface->status)) + return surface; + + status = compositor->acquire (surface); + if (unlikely (status)) { + cairo_surface_destroy (surface); + return _cairo_int_surface_create_in_error (status); + } + + if (!surface->is_clear) { + cairo_rectangle_int_t rect; + + rect.x = rect.y = 0; + rect.width = extents->bounded.width; + rect.height = extents->bounded.height; + + status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &rect, 1); + if (unlikely (status)) + goto error; + } + + if (mask_func) { + status = mask_func (compositor, surface, draw_closure, + CAIRO_OPERATOR_SOURCE, NULL, NULL, + extents->bounded.x, extents->bounded.y, + &extents->bounded, extents->clip); + if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + goto out; + } + + /* Is it worth setting the clip region here? */ + status = draw_func (compositor, surface, draw_closure, + CAIRO_OPERATOR_ADD, NULL, NULL, + extents->bounded.x, extents->bounded.y, + &extents->bounded, NULL); + if (unlikely (status)) + goto error; + + info.compositor = compositor; + info.dst = surface; + for (i = 0; i < extents->clip->num_boxes; i++) { + cairo_box_t *b = &extents->clip->boxes[i]; + + if (! _cairo_fixed_is_integer (b->p1.x) || + ! _cairo_fixed_is_integer (b->p1.y) || + ! _cairo_fixed_is_integer (b->p2.x) || + ! _cairo_fixed_is_integer (b->p2.y)) + { + do_unaligned_box(blt_in, &info, b, + extents->bounded.x, + extents->bounded.y); + } + } + + if (extents->clip->path != NULL) { + status = _cairo_clip_combine_with_surface (extents->clip, surface, + extents->bounded.x, + extents->bounded.y); + if (unlikely (status)) + goto error; + } + +out: + compositor->release (surface); + surface->is_clear = FALSE; + return surface; + +error: + compositor->release (surface); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + cairo_surface_destroy (surface); + surface = _cairo_int_surface_create_in_error (status); + } + return surface; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +clip_and_composite_with_mask (const cairo_mask_compositor_t *compositor, + void *draw_closure, + draw_func_t draw_func, + draw_func_t mask_func, + cairo_operator_t op, + cairo_pattern_t *pattern, + const cairo_composite_rectangles_t*extents) +{ + cairo_surface_t *dst = extents->surface; + cairo_surface_t *mask, *src; + int src_x, src_y; + + mask = create_composite_mask (compositor, dst, draw_closure, + draw_func, mask_func, + extents); + if (unlikely (mask->status)) + return mask->status; + + if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) { + src = compositor->pattern_to_surface (dst, + &extents->source_pattern.base, + FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (unlikely (src->status)) { + cairo_surface_destroy (mask); + return src->status; + } + + compositor->composite (dst, op, src, mask, + extents->bounded.x + src_x, + extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + cairo_surface_destroy (src); + } else { + compositor->composite (dst, op, mask, NULL, + 0, 0, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } + cairo_surface_destroy (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +get_clip_source (const cairo_mask_compositor_t *compositor, + cairo_clip_t *clip, + cairo_surface_t *dst, + const cairo_rectangle_int_t *bounds, + int *out_x, int *out_y) +{ + cairo_surface_pattern_t pattern; + cairo_rectangle_int_t r; + cairo_surface_t *surface; + + surface = _cairo_clip_get_image (clip, dst, bounds); + if (unlikely (surface->status)) + return surface; + + _cairo_pattern_init_for_surface (&pattern, surface); + pattern.base.filter = CAIRO_FILTER_NEAREST; + cairo_surface_destroy (surface); + + r.x = r.y = 0; + r.width = bounds->width; + r.height = bounds->height; + + surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE, + &r, &r, out_x, out_y); + _cairo_pattern_fini (&pattern.base); + + *out_x += -bounds->x; + *out_y += -bounds->y; + return surface; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +clip_and_composite_combine (const cairo_mask_compositor_t *compositor, + void *draw_closure, + draw_func_t draw_func, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_composite_rectangles_t*extents) +{ + cairo_surface_t *dst = extents->surface; + cairo_surface_t *tmp, *clip; + cairo_status_t status; + int clip_x, clip_y; + + tmp = _cairo_surface_create_scratch (dst, dst->content, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (tmp->status)) + return tmp->status; + + compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL, + extents->bounded.x, extents->bounded.y, + 0, 0, + 0, 0, + extents->bounded.width, extents->bounded.height); + + status = draw_func (compositor, tmp, draw_closure, op, + pattern, &extents->source_sample_area, + extents->bounded.x, extents->bounded.y, + &extents->bounded, NULL); + if (unlikely (status)) + goto cleanup; + + clip = get_clip_source (compositor, + extents->clip, dst, &extents->bounded, + &clip_x, &clip_y); + if (unlikely ((status = clip->status))) + goto cleanup; + + if (dst->is_clear) { + compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip, + 0, 0, + clip_x, clip_y, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } else { + /* Punch the clip out of the destination */ + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL, + clip_x, clip_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + /* Now add the two results together */ + compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip, + 0, 0, + clip_x, clip_y, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } + cairo_surface_destroy (clip); + +cleanup: + cairo_surface_destroy (tmp); + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +clip_and_composite_source (const cairo_mask_compositor_t *compositor, + void *draw_closure, + draw_func_t draw_func, + draw_func_t mask_func, + cairo_pattern_t *pattern, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *dst = extents->surface; + cairo_surface_t *mask, *src; + int src_x, src_y; + + /* Create a surface that is mask IN clip */ + mask = create_composite_mask (compositor, dst, draw_closure, + draw_func, mask_func, + extents); + if (unlikely (mask->status)) + return mask->status; + + src = compositor->pattern_to_surface (dst, + pattern, + FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (unlikely (src->status)) { + cairo_surface_destroy (mask); + return src->status; + } + + if (dst->is_clear) { + compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } else { + /* Compute dest' = dest OUT (mask IN clip) */ + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + 0, 0, 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } + + cairo_surface_destroy (src); + cairo_surface_destroy (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +can_reduce_alpha_op (cairo_operator_t op) +{ + int iop = op; + switch (iop) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + return TRUE; + default: + return FALSE; + } +} + +static cairo_bool_t +reduce_alpha_op (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return dst->is_clear && + dst->content == CAIRO_CONTENT_ALPHA && + _cairo_pattern_is_opaque_solid (pattern) && + can_reduce_alpha_op (op); +} + +static cairo_status_t +fixup_unbounded (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + const cairo_composite_rectangles_t *extents) +{ + cairo_rectangle_int_t rects[4]; + int n; + + if (extents->bounded.width == extents->unbounded.width && + extents->bounded.height == extents->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + + n = 0; + if (extents->bounded.width == 0 || extents->bounded.height == 0) { + rects[n].x = extents->unbounded.x; + rects[n].width = extents->unbounded.width; + rects[n].y = extents->unbounded.y; + rects[n].height = extents->unbounded.height; + n++; + } else { + /* top */ + if (extents->bounded.y != extents->unbounded.y) { + rects[n].x = extents->unbounded.x; + rects[n].width = extents->unbounded.width; + rects[n].y = extents->unbounded.y; + rects[n].height = extents->bounded.y - extents->unbounded.y; + n++; + } + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + rects[n].x = extents->unbounded.x; + rects[n].width = extents->bounded.x - extents->unbounded.x; + rects[n].y = extents->bounded.y; + rects[n].height = extents->bounded.height; + n++; + } + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + rects[n].x = extents->bounded.x + extents->bounded.width; + rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x; + rects[n].y = extents->bounded.y; + rects[n].height = extents->bounded.height; + n++; + } + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + rects[n].x = extents->unbounded.x; + rects[n].width = extents->unbounded.width; + rects[n].y = extents->bounded.y + extents->bounded.height; + rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y; + n++; + } + } + + return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + rects, n); +} + +static cairo_status_t +fixup_unbounded_with_mask (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *mask; + int mask_x, mask_y; + + mask = get_clip_source (compositor, + extents->clip, dst, &extents->unbounded, + &mask_x, &mask_y); + if (unlikely (mask->status)) + return mask->status; + + /* top */ + if (extents->bounded.y != extents->unbounded.y) { + int x = extents->unbounded.x; + int y = extents->unbounded.y; + int width = extents->unbounded.width; + int height = extents->bounded.y - y; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + int x = extents->unbounded.x; + int y = extents->bounded.y; + int width = extents->bounded.x - x; + int height = extents->bounded.height; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + int x = extents->bounded.x + extents->bounded.width; + int y = extents->bounded.y; + int width = extents->unbounded.x + extents->unbounded.width - x; + int height = extents->bounded.height; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + int x = extents->unbounded.x; + int y = extents->bounded.y + extents->bounded.height; + int width = extents->unbounded.width; + int height = extents->unbounded.y + extents->unbounded.height - y; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + x + mask_x, y + mask_y, + 0, 0, + x, y, + width, height); + } + + cairo_surface_destroy (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +fixup_unbounded_boxes (const cairo_mask_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + cairo_boxes_t clear; + cairo_region_t *clip_region; + cairo_box_t box; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + int i; + + assert (boxes->is_pixel_aligned); + + clip_region = NULL; + if (_cairo_clip_is_region (extents->clip) && + (clip_region = _cairo_clip_get_region (extents->clip)) && + cairo_region_contains_rectangle (clip_region, + &extents->bounded) == CAIRO_REGION_OVERLAP_IN) + clip_region = NULL; + + + if (boxes->num_boxes <= 1 && clip_region == NULL) + return fixup_unbounded (compositor, dst, extents); + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, + CAIRO_ANTIALIAS_DEFAULT, + &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = compositor->fill_boxes (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + } + + _cairo_boxes_fini (&clear); + + return status; +} + +enum { + NEED_CLIP_REGION = 0x1, + NEED_CLIP_SURFACE = 0x2, + FORCE_CLIP_REGION = 0x4, +}; + +static cairo_bool_t +need_bounded_clip (cairo_composite_rectangles_t *extents) +{ + unsigned int flags = NEED_CLIP_REGION; + if (! _cairo_clip_is_region (extents->clip)) + flags |= NEED_CLIP_SURFACE; + return flags; +} + +static cairo_bool_t +need_unbounded_clip (cairo_composite_rectangles_t *extents) +{ + unsigned int flags = 0; + if (! extents->is_bounded) { + flags |= NEED_CLIP_REGION; + if (! _cairo_clip_is_region (extents->clip)) + flags |= NEED_CLIP_SURFACE; + } + if (extents->clip->path != NULL) + flags |= NEED_CLIP_SURFACE; + return flags; +} + +static cairo_status_t +clip_and_composite (const cairo_mask_compositor_t *compositor, + draw_func_t draw_func, + draw_func_t mask_func, + void *draw_closure, + cairo_composite_rectangles_t*extents, + unsigned int need_clip) +{ + cairo_surface_t *dst = extents->surface; + cairo_operator_t op = extents->op; + cairo_pattern_t *src = &extents->source_pattern.base; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + compositor->acquire (dst); + + if (need_clip & NEED_CLIP_REGION) { + clip_region = _cairo_clip_get_region (extents->clip); + if ((need_clip & FORCE_CLIP_REGION) == 0 && + _cairo_composite_rectangles_can_reduce_clip (extents, + extents->clip)) + clip_region = NULL; + if (clip_region != NULL) { + status = compositor->set_clip_region (dst, clip_region); + if (unlikely (status)) { + compositor->release (dst); + return status; + } + } + } + + if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) { + op = CAIRO_OPERATOR_ADD; + src = NULL; + } + + if (op == CAIRO_OPERATOR_SOURCE) { + status = clip_and_composite_source (compositor, + draw_closure, draw_func, mask_func, + src, extents); + } else { + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + src = NULL; + } + + if (need_clip & NEED_CLIP_SURFACE) { + if (extents->is_bounded) { + status = clip_and_composite_with_mask (compositor, + draw_closure, + draw_func, + mask_func, + op, src, extents); + } else { + status = clip_and_composite_combine (compositor, + draw_closure, + draw_func, + op, src, extents); + } + } else { + status = draw_func (compositor, + dst, draw_closure, + op, src, &extents->source_sample_area, + 0, 0, + &extents->bounded, + extents->clip); + } + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + if (need_clip & NEED_CLIP_SURFACE) + status = fixup_unbounded_with_mask (compositor, dst, extents); + else + status = fixup_unbounded (compositor, dst, extents); + } + + if (clip_region) + compositor->set_clip_region (dst, NULL); + + compositor->release (dst); + + return status; +} + +static cairo_int_status_t +trim_extents_to_boxes (cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_box_t box; + + _cairo_boxes_extents (boxes, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_status_t +upload_boxes (const cairo_mask_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + const cairo_pattern_t *source = &extents->source_pattern.base; + cairo_surface_t *src; + cairo_rectangle_int_t limit; + cairo_int_status_t status; + int tx, ty; + + src = _cairo_pattern_get_source ((cairo_surface_pattern_t *)source, &limit); + if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that the data is entirely within the image */ + if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || + extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + tx += limit.x; + ty += limit.y; + + if (src->type == CAIRO_SURFACE_TYPE_IMAGE) + status = compositor->draw_image_boxes (dst, + (cairo_image_surface_t *)src, + boxes, tx, ty); + else + status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, + tx, ty); + + return status; +} + +static cairo_status_t +composite_boxes (const cairo_mask_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + cairo_operator_t op = extents->op; + const cairo_pattern_t *source = &extents->source_pattern.base; + cairo_bool_t need_clip_mask = extents->clip->path != NULL; + cairo_status_t status; + + if (need_clip_mask && + (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = compositor->acquire (dst); + if (unlikely (status)) + return status; + + if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_color_t *color; + + color = &((cairo_solid_pattern_t *) source)->color; + status = compositor->fill_boxes (dst, op, color, boxes); + } else { + cairo_surface_t *src, *mask = NULL; + int src_x, src_y; + int mask_x = 0, mask_y = 0; + + if (need_clip_mask) { + mask = get_clip_source (compositor, + extents->clip, dst, &extents->bounded, + &mask_x, &mask_y); + if (unlikely (mask->status)) + return mask->status; + + if (op == CAIRO_OPERATOR_CLEAR) { + source = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + } + + if (source || mask == NULL) { + src = compositor->pattern_to_surface (dst, source, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + } else { + src = mask; + src_x = mask_x; + src_y = mask_y; + mask = NULL; + } + + status = compositor->composite_boxes (dst, op, src, mask, + src_x, src_y, + mask_x, mask_y, + 0, 0, + boxes, &extents->bounded); + + cairo_surface_destroy (src); + cairo_surface_destroy (mask); + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) + status = fixup_unbounded_boxes (compositor, extents, boxes); + + compositor->release (dst); + + return status; +} + +static cairo_status_t +clip_and_composite_boxes (const cairo_mask_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + cairo_int_status_t status; + + if (boxes->num_boxes == 0) { + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + return fixup_unbounded_boxes (compositor, extents, boxes); + } + + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = trim_extents_to_boxes (extents, boxes); + if (unlikely (status)) + return status; + + if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && + extents->clip->path == NULL && + (extents->op == CAIRO_OPERATOR_SOURCE || + (dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER || + extents->op == CAIRO_OPERATOR_ADD)))) + { + status = upload_boxes (compositor, extents, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + return composite_boxes (compositor, extents, boxes); +} + +/* high-level compositor interface */ + +static cairo_int_status_t +_cairo_mask_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; + cairo_boxes_t boxes; + cairo_int_status_t status; + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + _cairo_clip_steal_boxes (extents->clip, &boxes); + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_clip_unsteal_boxes (extents->clip, &boxes); + + return status; +} + +struct composite_opacity_info { + const cairo_mask_compositor_t *compositor; + uint8_t op; + cairo_surface_t *dst; + cairo_surface_t *src; + int src_x, src_y; + double opacity; +}; + +static void composite_opacity(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct composite_opacity_info *info = closure; + const cairo_mask_compositor_t *compositor = info->compositor; + cairo_surface_t *mask; + int mask_x, mask_y; + cairo_color_t color; + cairo_solid_pattern_t solid; + + _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage); + _cairo_pattern_init_solid (&solid, &color); + mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE, + &_cairo_unbounded_rectangle, + &_cairo_unbounded_rectangle, + &mask_x, &mask_y); + if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { + if (info->src) { + compositor->composite (info->dst, info->op, info->src, mask, + x + info->src_x, y + info->src_y, + mask_x, mask_y, + x, y, + w, h); + } else { + compositor->composite (info->dst, info->op, mask, NULL, + mask_x, mask_y, + 0, 0, + x, y, + w, h); + } + } + + cairo_surface_destroy (mask); +} + +static cairo_int_status_t +composite_opacity_boxes (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *src_sample, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + const cairo_solid_pattern_t *mask_pattern = closure; + struct composite_opacity_info info; + int i; + + assert (clip); + + info.compositor = compositor; + info.op = op; + info.dst = dst; + + if (src_pattern != NULL) { + info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE, + extents, src_sample, + &info.src_x, &info.src_y); + if (unlikely (info.src->status)) + return info.src->status; + } else + info.src = NULL; + + info.opacity = mask_pattern->color.alpha / (double) 0xffff; + + /* XXX for lots of boxes create a clip region for the fully opaque areas */ + for (i = 0; i < clip->num_boxes; i++) + do_unaligned_box(composite_opacity, &info, + &clip->boxes[i], dst_x, dst_y); + cairo_surface_destroy (info.src); + + return CAIRO_STATUS_SUCCESS; +} + +struct composite_box_info { + const cairo_mask_compositor_t *compositor; + cairo_surface_t *dst; + cairo_surface_t *src; + int src_x, src_y; + uint8_t op; +}; + +static void composite_box(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct composite_box_info *info = closure; + const cairo_mask_compositor_t *compositor = info->compositor; + + if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) { + cairo_surface_t *mask; + cairo_color_t color; + cairo_solid_pattern_t solid; + int mask_x, mask_y; + + _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff); + _cairo_pattern_init_solid (&solid, &color); + + mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE, + &_cairo_unbounded_rectangle, + &_cairo_unbounded_rectangle, + &mask_x, &mask_y); + + if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { + compositor->composite (info->dst, info->op, info->src, mask, + x + info->src_x, y + info->src_y, + mask_x, mask_y, + x, y, + w, h); + } + + cairo_surface_destroy (mask); + } else { + compositor->composite (info->dst, info->op, info->src, NULL, + x + info->src_x, y + info->src_y, + 0, 0, + x, y, + w, h); + } +} + +static cairo_int_status_t +composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *src_sample, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t *composite = closure; + struct composite_box_info info; + int i; + + assert (src_pattern == NULL); + assert (op == CAIRO_OPERATOR_SOURCE); + + info.compositor = compositor; + info.op = CAIRO_OPERATOR_SOURCE; + info.dst = dst; + info.src = compositor->pattern_to_surface (dst, + &composite->mask_pattern.base, + FALSE, extents, + &composite->mask_sample_area, + &info.src_x, &info.src_y); + if (unlikely (info.src->status)) + return info.src->status; + + info.src_x += dst_x; + info.src_y += dst_y; + + for (i = 0; i < clip->num_boxes; i++) + do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y); + + cairo_surface_destroy (info.src); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +composite_mask (const cairo_mask_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *src_sample, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + cairo_composite_rectangles_t *composite = closure; + cairo_surface_t *src, *mask; + int src_x, src_y; + int mask_x, mask_y; + + if (src_pattern != NULL) { + src = compositor->pattern_to_surface (dst, src_pattern, FALSE, + extents, src_sample, + &src_x, &src_y); + if (unlikely (src->status)) + return src->status; + + mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE, + extents, &composite->mask_sample_area, + &mask_x, &mask_y); + if (unlikely (mask->status)) { + cairo_surface_destroy (src); + return mask->status; + } + + compositor->composite (dst, op, src, mask, + extents->x + src_x, extents->y + src_y, + extents->x + mask_x, extents->y + mask_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + cairo_surface_destroy (mask); + cairo_surface_destroy (src); + } else { + src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE, + extents, &composite->mask_sample_area, + &src_x, &src_y); + if (unlikely (src->status)) + return src->status; + + compositor->composite (dst, op, src, NULL, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + cairo_surface_destroy (src); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_mask_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && + extents->clip->path == NULL && + _cairo_clip_is_region (extents->clip)) { + status = clip_and_composite (compositor, + composite_opacity_boxes, + composite_opacity_boxes, + &extents->mask_pattern.solid, + extents, need_unbounded_clip (extents)); + } else { + status = clip_and_composite (compositor, + composite_mask, + extents->clip->path == NULL ? composite_mask_clip_boxes : NULL, + extents, + extents, need_bounded_clip (extents)); + } + + return status; +} + +static cairo_int_status_t +_cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; + cairo_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents->clip); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + mask = cairo_surface_create_similar_image (extents->surface, + CAIRO_FORMAT_A8, + extents->bounded.width, + extents->bounded.height); + if (unlikely (mask->status)) + return mask->status; + + status = _cairo_surface_offset_stroke (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, style, ctm, ctm_inverse, + tolerance, antialias, + extents->clip); + if (unlikely (status)) { + cairo_surface_destroy (mask); + return status; + } + + _cairo_pattern_init_for_surface (&pattern, mask); + cairo_surface_destroy (mask); + + cairo_matrix_init_translate (&pattern.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + pattern.base.extend = CAIRO_EXTEND_NONE; + status = _cairo_surface_mask (extents->surface, + extents->op, + &extents->source_pattern.base, + &pattern.base, + extents->clip); + _cairo_pattern_fini (&pattern.base); + } + + return status; +} + +static cairo_int_status_t +_cairo_mask_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; + cairo_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents->clip); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + mask = cairo_surface_create_similar_image (extents->surface, + CAIRO_FORMAT_A8, + extents->bounded.width, + extents->bounded.height); + if (unlikely (mask->status)) + return mask->status; + + status = _cairo_surface_offset_fill (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, fill_rule, tolerance, antialias, + extents->clip); + if (unlikely (status)) { + cairo_surface_destroy (mask); + return status; + } + + _cairo_pattern_init_for_surface (&pattern, mask); + cairo_surface_destroy (mask); + + cairo_matrix_init_translate (&pattern.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + pattern.base.extend = CAIRO_EXTEND_NONE; + status = _cairo_surface_mask (extents->surface, + extents->op, + &extents->source_pattern.base, + &pattern.base, + extents->clip); + _cairo_pattern_fini (&pattern.base); + } + + return status; +} + +static cairo_int_status_t +_cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor; + cairo_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status; + + status = compositor->check_composite (extents); + if (unlikely (status)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + mask = cairo_surface_create_similar_image (extents->surface, + CAIRO_FORMAT_A8, + extents->bounded.width, + extents->bounded.height); + if (unlikely (mask->status)) + return mask->status; + + status = _cairo_surface_offset_glyphs (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + scaled_font, glyphs, num_glyphs, + extents->clip); + if (unlikely (status)) { + cairo_surface_destroy (mask); + return status; + } + + _cairo_pattern_init_for_surface (&pattern, mask); + cairo_surface_destroy (mask); + + cairo_matrix_init_translate (&pattern.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + pattern.base.extend = CAIRO_EXTEND_NONE; + status = _cairo_surface_mask (extents->surface, + extents->op, + &extents->source_pattern.base, + &pattern.base, + extents->clip); + _cairo_pattern_fini (&pattern.base); + + return status; +} + +void +_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor, + const cairo_compositor_t *delegate) +{ + compositor->base.delegate = delegate; + + compositor->base.paint = _cairo_mask_compositor_paint; + compositor->base.mask = _cairo_mask_compositor_mask; + compositor->base.fill = _cairo_mask_compositor_fill; + compositor->base.stroke = _cairo_mask_compositor_stroke; + compositor->base.glyphs = _cairo_mask_compositor_glyphs; +} diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c index 583a7a649ce2..f3cf684c9d6e 100644 --- a/gfx/cairo/cairo/src/cairo-matrix.c +++ b/gfx/cairo/cairo/src/cairo-matrix.c @@ -36,12 +36,9 @@ #include "cairoint.h" #include "cairo-error-private.h" +#include -#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) -#define ISFINITE(x) isfinite (x) -#else -#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ -#endif +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ /** * SECTION:cairo-matrix @@ -64,7 +61,7 @@ * #cairo_matrix_t, defines the transformation from user-space * coordinates to device-space coordinates. See cairo_get_matrix() and * cairo_set_matrix(). - */ + **/ static void _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); @@ -77,6 +74,8 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); * @matrix: a #cairo_matrix_t * * Modifies @matrix to be an identity transformation. + * + * Since: 1.0 **/ void cairo_matrix_init_identity (cairo_matrix_t *matrix) @@ -105,6 +104,8 @@ slim_hidden_def(cairo_matrix_init_identity); * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * + * + * Since: 1.0 **/ void cairo_matrix_init (cairo_matrix_t *matrix, @@ -165,6 +166,8 @@ _cairo_matrix_get_affine (const cairo_matrix_t *matrix, * * Initializes @matrix to a transformation that translates by @tx and * @ty in the X and Y dimensions, respectively. + * + * Since: 1.0 **/ void cairo_matrix_init_translate (cairo_matrix_t *matrix, @@ -187,6 +190,8 @@ slim_hidden_def(cairo_matrix_init_translate); * @matrix. The effect of the new transformation is to first translate * the coordinates by @tx and @ty, then apply the original transformation * to the coordinates. + * + * Since: 1.0 **/ void cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) @@ -207,6 +212,8 @@ slim_hidden_def (cairo_matrix_translate); * * Initializes @matrix to a transformation that scales by @sx and @sy * in the X and Y dimensions, respectively. + * + * Since: 1.0 **/ void cairo_matrix_init_scale (cairo_matrix_t *matrix, @@ -228,6 +235,8 @@ slim_hidden_def(cairo_matrix_init_scale); * Applies scaling by @sx, @sy to the transformation in @matrix. The * effect of the new transformation is to first scale the coordinates * by @sx and @sy, then apply the original transformation to the coordinates. + * + * Since: 1.0 **/ void cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) @@ -250,6 +259,8 @@ slim_hidden_def(cairo_matrix_scale); * direction. * * Initialized @matrix to a transformation that rotates by @radians. + * + * Since: 1.0 **/ void cairo_matrix_init_rotate (cairo_matrix_t *matrix, @@ -281,6 +292,8 @@ slim_hidden_def(cairo_matrix_init_rotate); * @matrix. The effect of the new transformation is to first rotate the * coordinates by @radians, then apply the original transformation * to the coordinates. + * + * Since: 1.0 **/ void cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) @@ -305,6 +318,8 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) * coordinates. * * It is allowable for @result to be identical to either @a or @b. + * + * Since: 1.0 **/ /* * XXX: The ordering of the arguments to this function corresponds @@ -330,6 +345,21 @@ cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const ca } slim_hidden_def(cairo_matrix_multiply); +void +_cairo_matrix_multiply (cairo_matrix_t *r, + const cairo_matrix_t *a, + const cairo_matrix_t *b) +{ + r->xx = a->xx * b->xx + a->yx * b->xy; + r->yx = a->xx * b->yx + a->yx * b->yy; + + r->xy = a->xy * b->xx + a->yy * b->xy; + r->yy = a->xy * b->yx + a->yy * b->yy; + + r->x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0; + r->y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0; +} + /** * cairo_matrix_transform_distance: * @matrix: a #cairo_matrix_t @@ -350,6 +380,8 @@ slim_hidden_def(cairo_matrix_multiply); * always transforms to the same vector. If (@x1,@y1) transforms * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. + * + * Since: 1.0 **/ void cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy) @@ -371,6 +403,8 @@ slim_hidden_def(cairo_matrix_transform_distance); * @y: Y position. An in/out parameter * * Transforms the point (@x, @y) by @matrix. + * + * Since: 1.0 **/ void cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y) @@ -545,6 +579,8 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) * Returns: If @matrix has an inverse, modifies @matrix to * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, * returns %CAIRO_STATUS_INVALID_MATRIX. + * + * Since: 1.0 **/ cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix) @@ -682,21 +718,6 @@ _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, return CAIRO_STATUS_SUCCESS; } -cairo_bool_t -_cairo_matrix_is_identity (const cairo_matrix_t *matrix) -{ - return (matrix->xx == 1.0 && matrix->yx == 0.0 && - matrix->xy == 0.0 && matrix->yy == 1.0 && - matrix->x0 == 0.0 && matrix->y0 == 0.0); -} - -cairo_bool_t -_cairo_matrix_is_translation (const cairo_matrix_t *matrix) -{ - return (matrix->xx == 1.0 && matrix->yx == 0.0 && - matrix->xy == 0.0 && matrix->yy == 1.0); -} - cairo_bool_t _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, int *itx, int *ity) @@ -721,23 +742,32 @@ _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, return FALSE; } +#define SCALING_EPSILON _cairo_fixed_to_double(1) + +/* This only returns true if the matrix is 90 degree rotations or + * flips. It appears calling code is relying on this. It will return + * false for other rotations even if the scale is one. Approximations + * are allowed to handle matricies filled in using trig functions + * such as sin(M_PI_2). + */ cairo_bool_t _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix) { - if (matrix->xy == 0.0 && matrix->yx == 0.0) { - if (! (matrix->xx == 1.0 || matrix->xx == -1.0)) - return FALSE; - if (! (matrix->yy == 1.0 || matrix->yy == -1.0)) - return FALSE; - } else if (matrix->xx == 0.0 && matrix->yy == 0.0) { - if (! (matrix->xy == 1.0 || matrix->xy == -1.0)) - return FALSE; - if (! (matrix->yx == 1.0 || matrix->yx == -1.0)) - return FALSE; - } else - return FALSE; - - return TRUE; + /* check that the determinant is near +/-1 */ + double det = _cairo_matrix_compute_determinant (matrix); + if (fabs (det * det - 1.0) < SCALING_EPSILON) { + /* check that one axis is close to zero */ + if (fabs (matrix->xy) < SCALING_EPSILON && + fabs (matrix->yx) < SCALING_EPSILON) + return TRUE; + if (fabs (matrix->xx) < SCALING_EPSILON && + fabs (matrix->yy) < SCALING_EPSILON) + return TRUE; + /* If rotations are allowed then it must instead test for + * orthogonality. This is xx*xy+yx*yy ~= 0. + */ + } + return FALSE; } /* By pixel exact here, we mean a matrix that is composed only of @@ -878,15 +908,16 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) see doc/tutorial/src/singular.c. */ -/* determine the length of the major and minor axes of a circle of the given - radius after applying the transformation matrix. */ -void -_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, - double radius, - double *major, - double *minor) +/* determine the length of the major axis of a circle of the given radius + after applying the transformation matrix. */ +double +_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, + double radius) { - double a, b, c, d, f, g, h, i, j, k; + double a, b, c, d, f, g, h, i, j; + + if (_cairo_matrix_has_unity_scale (matrix)) + return radius; _cairo_matrix_get_affine (matrix, &a, &b, @@ -895,112 +926,281 @@ _cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, i = a*a + b*b; j = c*c + d*d; - k = a*c + b*d; f = 0.5 * (i + j); g = 0.5 * (i - j); - h = hypot (g, k); + h = a*c + b*d; - if (major) - *major = radius * sqrt (f + h); - if (minor) - *minor = radius * sqrt (f - h); + return radius * sqrt (f + hypot (g, h)); + + /* + * we don't need the minor axis length, which is + * double min = radius * sqrt (f - sqrt (g*g+h*h)); + */ } -/* determine the length of the major axis of a circle of the given radius - after applying the transformation matrix. */ -double -_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, - double radius) -{ - double major; - - _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL); - - return major; -} - -void -_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform, - double xc, - double yc) -{ - static const pixman_transform_t pixman_identity_transform = {{ +static const pixman_transform_t pixman_identity_transform = {{ {1 << 16, 0, 0}, { 0, 1 << 16, 0}, { 0, 0, 1 << 16} }}; - if (_cairo_matrix_is_identity (matrix)) { - *pixman_transform = pixman_identity_transform; - } else { - cairo_matrix_t inv; - unsigned max_iterations; +static cairo_status_t +_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, + pixman_transform_t *pixman_transform, + double xc, + double yc) +{ + cairo_matrix_t inv; + unsigned max_iterations; - pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); - pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); - pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); + pixman_transform->matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx); + pixman_transform->matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy); + pixman_transform->matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0); - pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); - pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); - pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); + pixman_transform->matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx); + pixman_transform->matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy); + pixman_transform->matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0); - pixman_transform->matrix[2][0] = 0; - pixman_transform->matrix[2][1] = 0; - pixman_transform->matrix[2][2] = 1 << 16; + pixman_transform->matrix[2][0] = 0; + pixman_transform->matrix[2][1] = 0; + pixman_transform->matrix[2][2] = 1 << 16; - /* The conversion above breaks cairo's translation invariance: - * a translation of (a, b) in device space translates to - * a translation of (xx * a + xy * b, yx * a + yy * b) - * for cairo, while pixman uses rounded versions of xx ... yy. - * This error increases as a and b get larger. - * - * To compensate for this, we fix the point (xc, yc) in pattern - * space and adjust pixman's transform to agree with cairo's at - * that point. + /* The conversion above breaks cairo's translation invariance: + * a translation of (a, b) in device space translates to + * a translation of (xx * a + xy * b, yx * a + yy * b) + * for cairo, while pixman uses rounded versions of xx ... yy. + * This error increases as a and b get larger. + * + * To compensate for this, we fix the point (xc, yc) in pattern + * space and adjust pixman's transform to agree with cairo's at + * that point. + */ + + if (_cairo_matrix_has_unity_scale (matrix)) + return CAIRO_STATUS_SUCCESS; + + if (unlikely (fabs (matrix->xx) > PIXMAN_MAX_INT || + fabs (matrix->xy) > PIXMAN_MAX_INT || + fabs (matrix->x0) > PIXMAN_MAX_INT || + fabs (matrix->yx) > PIXMAN_MAX_INT || + fabs (matrix->yy) > PIXMAN_MAX_INT || + fabs (matrix->y0) > PIXMAN_MAX_INT)) + { + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + } + + /* Note: If we can't invert the transformation, skip the adjustment. */ + inv = *matrix; + if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + + /* find the pattern space coordinate that maps to (xc, yc) */ + max_iterations = 5; + do { + double x,y; + pixman_vector_t vector; + cairo_fixed_16_16_t dx, dy; + + vector.vector[0] = _cairo_fixed_16_16_from_double (xc); + vector.vector[1] = _cairo_fixed_16_16_from_double (yc); + vector.vector[2] = 1 << 16; + + /* If we can't transform the reference point, skip the adjustment. */ + if (! pixman_transform_point_3d (pixman_transform, &vector)) + return CAIRO_STATUS_SUCCESS; + + x = pixman_fixed_to_double (vector.vector[0]); + y = pixman_fixed_to_double (vector.vector[1]); + cairo_matrix_transform_point (&inv, &x, &y); + + /* Ideally, the vector should now be (xc, yc). + * We can now compensate for the resulting error. */ + x -= xc; + y -= yc; + cairo_matrix_transform_distance (matrix, &x, &y); + dx = _cairo_fixed_16_16_from_double (x); + dy = _cairo_fixed_16_16_from_double (y); + pixman_transform->matrix[0][2] -= dx; + pixman_transform->matrix[1][2] -= dy; - if (_cairo_matrix_has_unity_scale (matrix)) - return; + if (dx == 0 && dy == 0) + return CAIRO_STATUS_SUCCESS; + } while (--max_iterations); - /* Note: If we can't invert the transformation, skip the adjustment. */ - inv = *matrix; - if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) - return; + /* We didn't find an exact match between cairo and pixman, but + * the matrix should be mostly correct */ + return CAIRO_STATUS_SUCCESS; +} - /* find the pattern space coordinate that maps to (xc, yc) */ - xc += .5; yc += .5; /* offset for the pixel centre */ - max_iterations = 5; - do { - double x,y; - pixman_vector_t vector; - cairo_fixed_16_16_t dx, dy; +static inline double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} - vector.vector[0] = _cairo_fixed_16_16_from_double (xc); - vector.vector[1] = _cairo_fixed_16_16_from_double (yc); - vector.vector[2] = 1 << 16; +/** + * _cairo_matrix_is_pixman_translation: + * @matrix: a matrix + * @filter: the filter to be used on the pattern transformed by @matrix + * @x_offset: the translation in the X direction + * @y_offset: the translation in the Y direction + * + * Checks if @matrix translated by (x_offset, y_offset) can be + * represented using just an offset (within the range pixman can + * accept) and an identity matrix. + * + * Passing a non-zero value in x_offset/y_offset has the same effect + * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and + * setting x_offset and y_offset to 0. + * + * Upon return x_offset and y_offset contain the translation vector if + * the return value is %TRUE. If the return value is %FALSE, they will + * not be modified. + * + * Return value: %TRUE if @matrix can be represented as a pixman + * translation, %FALSE otherwise. + **/ +cairo_bool_t +_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, + cairo_filter_t filter, + int *x_offset, + int *y_offset) +{ + double tx, ty; - if (! pixman_transform_point_3d (pixman_transform, &vector)) - return; + if (!_cairo_matrix_is_translation (matrix)) + return FALSE; - x = pixman_fixed_to_double (vector.vector[0]); - y = pixman_fixed_to_double (vector.vector[1]); - cairo_matrix_transform_point (&inv, &x, &y); + if (matrix->x0 == 0. && matrix->y0 == 0.) + return TRUE; - /* Ideally, the vector should now be (xc, yc). - * We can now compensate for the resulting error. + tx = matrix->x0 + *x_offset; + ty = matrix->y0 + *y_offset; + + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { + tx = _pixman_nearest_sample (tx); + ty = _pixman_nearest_sample (ty); + } else if (tx != floor (tx) || ty != floor (ty)) { + return FALSE; + } + + if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT) + return FALSE; + + *x_offset = _cairo_lround (tx); + *y_offset = _cairo_lround (ty); + return TRUE; +} + +/** + * _cairo_matrix_to_pixman_matrix_offset: + * @matrix: a matrix + * @filter: the filter to be used on the pattern transformed by @matrix + * @xc: the X coordinate of the point to fix in pattern space + * @yc: the Y coordinate of the point to fix in pattern space + * @out_transform: the transformation which best approximates @matrix + * @x_offset: the translation in the X direction + * @y_offset: the translation in the Y direction + * + * This function tries to represent @matrix translated by (x_offset, + * y_offset) as a %pixman_transform_t and an translation. + * + * Passing a non-zero value in x_offset/y_offset has the same effect + * as applying cairo_matrix_translate(matrix, x_offset, y_offset) and + * setting x_offset and y_offset to 0. + * + * If it is possible to represent the matrix with an identity + * %pixman_transform_t and a translation within the valid range for + * pixman, this function will set @out_transform to be the identity, + * @x_offset and @y_offset to be the translation vector and will + * return %CAIRO_INT_STATUS_NOTHING_TO_DO. Otherwise it will try to + * evenly divide the translational component of @matrix between + * @out_transform and (@x_offset, @y_offset). + * + * Upon return x_offset and y_offset contain the translation vector. + * + * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the out_transform + * is the identity, %CAIRO_STATUS_INVALID_MATRIX if it was not + * possible to represent @matrix as a pixman_transform_t without + * overflows, %CAIRO_STATUS_SUCCESS otherwise. + **/ +cairo_status_t +_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, + double yc, + pixman_transform_t *out_transform, + int *x_offset, + int *y_offset) +{ + cairo_bool_t is_pixman_translation; + + is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix, + filter, + x_offset, + y_offset); + + if (is_pixman_translation) { + *out_transform = pixman_identity_transform; + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } else { + cairo_matrix_t m; + + m = *matrix; + cairo_matrix_translate (&m, *x_offset, *y_offset); + if (m.x0 != 0.0 || m.y0 != 0.0) { + double tx, ty, norm; + int i, j; + + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + * + * To do this, find the solutions of: + * |x| = |x*m.xx + y*m.xy + m.x0| + * |y| = |x*m.yx + y*m.yy + m.y0| + * + * and select the one whose maximum norm is smallest. */ - x -= xc; - y -= yc; - cairo_matrix_transform_distance (matrix, &x, &y); - dx = _cairo_fixed_16_16_from_double (x); - dy = _cairo_fixed_16_16_from_double (y); - pixman_transform->matrix[0][2] -= dx; - pixman_transform->matrix[1][2] -= dy; + tx = m.x0; + ty = m.y0; + norm = MAX (fabs (tx), fabs (ty)); - if (dx == 0 && dy == 0) - break; - } while (--max_iterations); + for (i = -1; i < 2; i+=2) { + for (j = -1; j < 2; j+=2) { + double x, y, den, new_norm; + + den = (m.xx + i) * (m.yy + j) - m.xy * m.yx; + if (fabs (den) < DBL_EPSILON) + continue; + + x = m.y0 * m.xy - m.x0 * (m.yy + j); + y = m.x0 * m.yx - m.y0 * (m.xx + i); + + den = 1 / den; + x *= den; + y *= den; + + new_norm = MAX (fabs (x), fabs (y)); + if (norm > new_norm) { + norm = new_norm; + tx = x; + ty = y; + } + } + } + + tx = floor (tx); + ty = floor (ty); + *x_offset = -tx; + *y_offset = -ty; + cairo_matrix_translate (&m, tx, ty); + } else { + *x_offset = 0; + *y_offset = 0; + } + + return _cairo_matrix_to_pixman_matrix (&m, out_transform, xc, yc); } } diff --git a/gfx/cairo/cairo/src/cairo-mempool-private.h b/gfx/cairo/cairo/src/cairo-mempool-private.h new file mode 100644 index 000000000000..a09f6ce51f6a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mempool-private.h @@ -0,0 +1,85 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Chris Wilson + * Copyright © 2009 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. + * + * Contributors(s): + * Chris Wilson + */ + +#ifndef CAIRO_MEMPOOL_PRIVATE_H +#define CAIRO_MEMPOOL_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-error-private.h" + +#include /* for size_t */ + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_mempool cairo_mempool_t; + +struct _cairo_mempool { + char *base; + struct _cairo_memblock { + int bits; + cairo_list_t link; + } *blocks; + cairo_list_t free[32]; + unsigned char *map; + + unsigned int num_blocks; + int min_bits; /* Minimum block size is 1 << min_bits */ + int num_sizes; + int max_free_bits; + + size_t free_bytes; + size_t max_bytes; +}; + +cairo_private cairo_status_t +_cairo_mempool_init (cairo_mempool_t *pool, + void *base, + size_t bytes, + int min_bits, + int num_sizes); + +cairo_private void * +_cairo_mempool_alloc (cairo_mempool_t *pi, size_t bytes); + +cairo_private void +_cairo_mempool_free (cairo_mempool_t *pi, void *storage); + +cairo_private void +_cairo_mempool_fini (cairo_mempool_t *pool); + +CAIRO_END_DECLS + +#endif /* CAIRO_MEMPOOL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-mempool.c b/gfx/cairo/cairo/src/cairo-mempool.c new file mode 100644 index 000000000000..ce40ce521ffc --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mempool.c @@ -0,0 +1,369 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Chris Wilson + * Copyright © 2009 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 recipoolent 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. + * + * Contributors(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-mempool-private.h" +#include "cairo-list-inline.h" + +/* a simple buddy allocator for memory pools + * XXX fragmentation? use Doug Lea's malloc? + */ + +#define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7))) +#define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7))) +#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7))) + +static void +clear_bits (cairo_mempool_t *pool, size_t first, size_t last) +{ + size_t i, n = last; + size_t first_full = (first + 7) & ~7; + size_t past_full = last & ~7; + size_t bytes; + + if (n > first_full) + n = first_full; + for (i = first; i < n; i++) + BITCLEAR (pool, i); + + if (past_full > first_full) { + bytes = past_full - first_full; + bytes = bytes >> 3; + memset (pool->map + (first_full >> 3), 0, bytes); + } + + if (past_full < n) + past_full = n; + for (i = past_full; i < last; i++) + BITCLEAR (pool, i); +} + +static void +free_bits (cairo_mempool_t *pool, size_t start, int bits, cairo_bool_t clear) +{ + struct _cairo_memblock *block; + + if (clear) + clear_bits (pool, start, start + (1 << bits)); + + block = pool->blocks + start; + block->bits = bits; + + cairo_list_add (&block->link, &pool->free[bits]); + + pool->free_bytes += 1 << (bits + pool->min_bits); + if (bits > pool->max_free_bits) + pool->max_free_bits = bits; +} + +/* Add a chunk to the free list */ +static void +free_blocks (cairo_mempool_t *pool, + size_t first, + size_t last, + cairo_bool_t clear) +{ + size_t i, len; + int bits = 0; + + for (i = first, len = 1; i < last; i += len) { + /* To avoid cost quadratic in the number of different + * blocks produced from this chunk of store, we have to + * use the size of the previous block produced from this + * chunk as the starting point to work out the size of the + * next block we can produce. If you look at the binary + * representation of the starting points of the blocks + * produced, you can see that you first of all increase the + * size of the blocks produced up to some maximum as the + * address dealt with gets offsets added on which zap out + * low order bits, then decrease as the low order bits of the + * final block produced get added in. E.g. as you go from + * 001 to 0111 you generate blocks + * of size 001 at 001 taking you to 010 + * of size 010 at 010 taking you to 100 + * of size 010 at 100 taking you to 110 + * of size 001 at 110 taking you to 111 + * So the maximum total cost of the loops below this comment + * is one trip from the lowest blocksize to the highest and + * back again. + */ + while (bits < pool->num_sizes - 1) { + size_t next_bits = bits + 1; + size_t next_len = len << 1; + + if (i + next_bits > last) { + /* off end of chunk to be freed */ + break; + } + + if (i & (next_len - 1)) /* block would not be on boundary */ + break; + + bits = next_bits; + len = next_len; + } + + do { + if (i + len <= last && /* off end of chunk to be freed */ + (i & (len - 1)) == 0) /* block would not be on boundary */ + break; + + bits--; len >>=1; + } while (len); + + if (len == 0) + break; + + free_bits (pool, i, bits, clear); + } +} + +static struct _cairo_memblock * +get_buddy (cairo_mempool_t *pool, size_t offset, int bits) +{ + struct _cairo_memblock *block; + + if (offset + (1 << bits) >= pool->num_blocks) + return NULL; /* invalid */ + + if (BITTEST (pool, offset + (1 << bits) - 1)) + return NULL; /* buddy is allocated */ + + block = pool->blocks + offset; + if (block->bits != bits) + return NULL; /* buddy is partially allocated */ + + return block; +} + +static void +merge_buddies (cairo_mempool_t *pool, + struct _cairo_memblock *block, + int max_bits) +{ + size_t block_offset = block - pool->blocks; + int bits = block->bits; + + while (bits < max_bits - 1) { + /* while you can, merge two blocks and get a legal block size */ + size_t buddy_offset = block_offset ^ (1 << bits); + + block = get_buddy (pool, buddy_offset, bits); + if (block == NULL) + break; + + cairo_list_del (&block->link); + + /* Merged block starts at buddy */ + if (buddy_offset < block_offset) + block_offset = buddy_offset; + + bits++; + } + + block = pool->blocks + block_offset; + block->bits = bits; + cairo_list_add (&block->link, &pool->free[bits]); + + if (bits > pool->max_free_bits) + pool->max_free_bits = bits; +} + +/* attempt to merge all available buddies up to a particular size */ +static int +merge_bits (cairo_mempool_t *pool, int max_bits) +{ + struct _cairo_memblock *block, *buddy, *next; + int bits; + + for (bits = 0; bits < max_bits - 1; bits++) { + cairo_list_foreach_entry_safe (block, next, + struct _cairo_memblock, + &pool->free[bits], + link) + { + size_t buddy_offset = (block - pool->blocks) ^ (1 << bits); + + buddy = get_buddy (pool, buddy_offset, bits); + if (buddy == NULL) + continue; + + if (buddy == next) { + next = cairo_container_of (buddy->link.next, + struct _cairo_memblock, + link); + } + + cairo_list_del (&block->link); + merge_buddies (pool, block, max_bits); + } + } + + return pool->max_free_bits; +} + +/* find store for 1 << bits blocks */ +static void * +buddy_malloc (cairo_mempool_t *pool, int bits) +{ + size_t past, offset; + struct _cairo_memblock *block; + int b; + + if (bits > pool->max_free_bits && bits > merge_bits (pool, bits)) + return NULL; + + /* Find a list with blocks big enough on it */ + block = NULL; + for (b = bits; b <= pool->max_free_bits; b++) { + if (! cairo_list_is_empty (&pool->free[b])) { + block = cairo_list_first_entry (&pool->free[b], + struct _cairo_memblock, + link); + break; + } + } + assert (block != NULL); + + cairo_list_del (&block->link); + + while (cairo_list_is_empty (&pool->free[pool->max_free_bits])) { + if (--pool->max_free_bits == -1) + break; + } + + /* Mark end of allocated area */ + offset = block - pool->blocks; + past = offset + (1 << bits); + BITSET (pool, past - 1); + block->bits = bits; + + /* If we used a larger free block than we needed, free the rest */ + pool->free_bytes -= 1 << (b + pool->min_bits); + free_blocks (pool, past, offset + (1 << b), 0); + + return pool->base + ((block - pool->blocks) << pool->min_bits); +} + +cairo_status_t +_cairo_mempool_init (cairo_mempool_t *pool, + void *base, size_t bytes, + int min_bits, int num_sizes) +{ + unsigned long tmp; + int num_blocks; + int i; + + /* Align the start to an integral chunk */ + tmp = ((unsigned long) base) & ((1 << min_bits) - 1); + if (tmp) { + tmp = (1 << min_bits) - tmp; + base = (char *)base + tmp; + bytes -= tmp; + } + + assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0); + assert (num_sizes < ARRAY_LENGTH (pool->free)); + + pool->base = base; + pool->free_bytes = 0; + pool->max_bytes = bytes; + pool->max_free_bits = -1; + + num_blocks = bytes >> min_bits; + pool->blocks = calloc (num_blocks, sizeof (struct _cairo_memblock)); + if (pool->blocks == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pool->num_blocks = num_blocks; + pool->min_bits = min_bits; + pool->num_sizes = num_sizes; + + for (i = 0; i < ARRAY_LENGTH (pool->free); i++) + cairo_list_init (&pool->free[i]); + + pool->map = _cairo_malloc ((num_blocks + 7) >> 3); + if (pool->map == NULL) { + free (pool->blocks); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memset (pool->map, -1, (num_blocks + 7) >> 3); + clear_bits (pool, 0, num_blocks); + + /* Now add all blocks to the free list */ + free_blocks (pool, 0, num_blocks, 1); + + return CAIRO_STATUS_SUCCESS; +} + +void * +_cairo_mempool_alloc (cairo_mempool_t *pool, size_t bytes) +{ + size_t size; + int bits; + + size = 1 << pool->min_bits; + for (bits = 0; size < bytes; bits++) + size <<= 1; + if (bits >= pool->num_sizes) + return NULL; + + return buddy_malloc (pool, bits); +} + +void +_cairo_mempool_free (cairo_mempool_t *pool, void *storage) +{ + size_t block_offset; + struct _cairo_memblock *block; + + block_offset = ((char *)storage - pool->base) >> pool->min_bits; + block = pool->blocks + block_offset; + + BITCLEAR (pool, block_offset + ((1 << block->bits) - 1)); + pool->free_bytes += 1 << (block->bits + pool->min_bits); + + merge_buddies (pool, block, pool->num_sizes); +} + +void +_cairo_mempool_fini (cairo_mempool_t *pool) +{ + free (pool->map); + free (pool->blocks); +} diff --git a/gfx/cairo/cairo/src/cairo-mesh-pattern-rasterizer.c b/gfx/cairo/cairo/src/cairo-mesh-pattern-rasterizer.c new file mode 100644 index 000000000000..e7f0db666ae9 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mesh-pattern-rasterizer.c @@ -0,0 +1,941 @@ +/* -*- 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 Andrea Canciani + * + * 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 Andrea Canciani. + * + * Contributor(s): + * Andrea Canciani + */ + +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-pattern-private.h" + +/* + * Rasterizer for mesh patterns. + * + * This implementation is based on techniques derived from several + * papers (available from ACM): + * + * - Lien, Shantz and Pratt "Adaptive Forward Differencing for + * Rendering Curves and Surfaces" (discussion of the AFD technique, + * bound of 1/sqrt(2) on step length without proof) + * + * - Popescu and Rosen, "Forward rasterization" (description of + * forward rasterization, proof of the previous bound) + * + * - Klassen, "Integer Forward Differencing of Cubic Polynomials: + * Analysis and Algorithms" + * + * - Klassen, "Exact Integer Hybrid Subdivision and Forward + * Differencing of Cubics" (improving the bound on the minimum + * number of steps) + * + * - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces + * with Integer Adaptive Forward Differencing" (analysis of forward + * differencing applied to Bezier patches) + * + * Notes: + * - Poor performance expected in degenerate cases + * + * - Patches mostly outside the drawing area are drawn completely (and + * clipped), wasting time + * + * - Both previous problems are greatly reduced by splitting until a + * reasonably small size and clipping the new tiles: execution time + * is quadratic in the convex-hull diameter instead than linear to + * the painted area. Splitting the tiles doesn't change the painted + * area but (usually) reduces the bounding box area (bbox area can + * remain the same after splitting, but cannot grow) + * + * - The initial implementation used adaptive forward differencing, + * but simple forward differencing scored better in benchmarks + * + * Idea: + * + * We do a sampling over the cubic patch with step du and dv (in the + * two parameters) that guarantees that any point of our sampling will + * be at most at 1/sqrt(2) from its adjacent points. In formulae + * (assuming B is the patch): + * + * |B(u,v) - B(u+du,v)| < 1/sqrt(2) + * |B(u,v) - B(u,v+dv)| < 1/sqrt(2) + * + * This means that every pixel covered by the patch will contain at + * least one of the samples, thus forward rasterization can be + * performed. Sketch of proof (from Popescu and Rosen): + * + * Let's take the P pixel we're interested into. If we assume it to be + * square, its boundaries define 9 regions on the plane: + * + * 1|2|3 + * -+-+- + * 8|P|4 + * -+-+- + * 7|6|5 + * + * Let's check that the pixel P will contain at least one point + * assuming that it is covered by the patch. + * + * Since the pixel is covered by the patch, its center will belong to + * (at least) one of the quads: + * + * {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]} + * + * If P doesn't contain any of the corners of the quad: + * + * - if one of the corners is in 1,3,5 or 7, other two of them have to + * be in 2,4,6 or 8, thus if the last corner is not in P, the length + * of one of the edges will be > 1/sqrt(2) + * + * - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6 + * and/or 8. If they are all in different regions, they can't + * satisfy the distance constraint. If two of them are in the same + * region (let's say 2), no point is in 6 and again it is impossible + * to have the center of P in the quad respecting the distance + * constraint (both these assertions can be checked by continuity + * considering the length of the edges of a quad with the vertices + * on the edges of P) + * + * Each of the cases led to a contradiction, so P contains at least + * one of the corners of the quad. + */ + +/* + * Make sure that errors are less than 1 in fixed point math if you + * change these values. + * + * The error is amplified by about steps^3/4 times. + * The rasterizer always uses a number of steps that is a power of 2. + * + * 256 is the maximum allowed number of steps (to have error < 1) + * using 8.24 for the differences. + */ +#define STEPS_MAX_V 256.0 +#define STEPS_MAX_U 256.0 + +/* + * If the patch/curve is only partially visible, split it to a finer + * resolution to get higher chances to clip (part of) it. + * + * These values have not been computed, but simply obtained + * empirically (by benchmarking some patches). They should never be + * greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small + * as 1 (depending on how much you want to spend time in splitting the + * patch/curve when trying to save some rasterization time). + */ +#define STEPS_CLIP_V 64.0 +#define STEPS_CLIP_U 64.0 + + +/* Utils */ +static inline double +sqlen (cairo_point_double_t p0, cairo_point_double_t p1) +{ + cairo_point_double_t delta; + + delta.x = p0.x - p1.x; + delta.y = p0.y - p1.y; + + return delta.x * delta.x + delta.y * delta.y; +} + +static inline int16_t +_color_delta_to_shifted_short (int32_t from, int32_t to, int shift) +{ + int32_t delta = to - from; + + /* We need to round toward zero, because otherwise adding the + * delta 2^shift times can overflow */ + if (delta >= 0) + return delta >> shift; + else + return -((-delta) >> shift); +} + +/* + * Convert a number of steps to the equivalent shift. + * + * Input: the square of the minimum number of steps + * + * Output: the smallest integer x such that 2^x > steps + */ +static inline int +sqsteps2shift (double steps_sq) +{ + int r; + frexp (MAX (1.0, steps_sq), &r); + return (r + 1) >> 1; +} + +/* + * FD functions + * + * A Bezier curve is defined (with respect to a parameter t in + * [0,1]) from its nodes (x,y,z,w) like this: + * + * B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3 + * + * To efficiently evaluate a Bezier curve, the rasterizer uses forward + * differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it + * is possible to convert them to forward differences form and walk + * over the curve using fd_init (), fd_down () and fd_fwd (). + * + * f[0] is always the value of the Bezier curve for "current" t. + */ + +/* + * Initialize the coefficient for forward differences. + * + * Input: x,y,z,w are the 4 nodes of the Bezier curve + * + * Output: f[i] is the i-th difference of the curve + * + * f[0] is the value of the curve for t==0, i.e. f[0]==x. + * + * The initial step is 1; this means that each step increases t by 1 + * (so fd_init () immediately followed by fd_fwd (f) n times makes + * f[0] be the value of the curve for t==n). + */ +static inline void +fd_init (double x, double y, double z, double w, double f[4]) +{ + f[0] = x; + f[1] = w - x; + f[2] = 6. * (w - 2. * z + y); + f[3] = 6. * (w - 3. * z + 3. * y - x); +} + +/* + * Halve the step of the coefficients for forward differences. + * + * Input: f[i] is the i-th difference of the curve + * + * Output: f[i] is the i-th difference of the curve with half the + * original step + * + * f[0] is not affected, so the current t is not changed. + * + * The other coefficients are changed so that the step is half the + * original step. This means that doing fd_fwd (f) n times with the + * input f results in the same f[0] as doing fd_fwd (f) 2n times with + * the output f. + */ +static inline void +fd_down (double f[4]) +{ + f[3] *= 0.125; + f[2] = f[2] * 0.25 - f[3]; + f[1] = (f[1] - f[2]) * 0.5; +} + +/* + * Perform one step of forward differences along the curve. + * + * Input: f[i] is the i-th difference of the curve + * + * Output: f[i] is the i-th difference of the curve after one step + */ +static inline void +fd_fwd (double f[4]) +{ + f[0] += f[1]; + f[1] += f[2]; + f[2] += f[3]; +} + +/* + * Transform to integer forward differences. + * + * Input: d[n] is the n-th difference (in double precision) + * + * Output: i[n] is the n-th difference (in fixed point precision) + * + * i[0] is 9.23 fixed point, other differences are 4.28 fixed point. + */ +static inline void +fd_fixed (double d[4], int32_t i[4]) +{ + i[0] = _cairo_fixed_16_16_from_double (256 * 2 * d[0]); + i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]); + i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]); + i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]); +} + +/* + * Perform one step of integer forward differences along the curve. + * + * Input: f[n] is the n-th difference + * + * Output: f[n] is the n-th difference + * + * f[0] is 9.23 fixed point, other differences are 4.28 fixed point. + */ +static inline void +fd_fixed_fwd (int32_t f[4]) +{ + f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1); + f[1] += f[2]; + f[2] += f[3]; +} + +/* + * Compute the minimum number of steps that guarantee that walking + * over a curve will leave no holes. + * + * Input: p[0..3] the nodes of the Bezier curve + * + * Returns: the square of the number of steps + * + * Idea: + * + * We want to make sure that at every step we move by less than + * 1/sqrt(2). + * + * The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is + * the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3, + * so (since a Bezier curve is always bounded by its convex hull), we + * can say that: + * + * max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|) + * + * We can improve this by noticing that a quadratic Bezier (a,b,c) is + * bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so + * (substituting the previous values, using t=0.5 and simplifying): + * + * max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) + * + * So, to guarantee a maximum step length of 1/sqrt(2) we must do: + * + * 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps + */ +static inline double +bezier_steps_sq (cairo_point_double_t p[4]) +{ + double tmp = sqlen (p[0], p[1]); + tmp = MAX (tmp, sqlen (p[2], p[3])); + tmp = MAX (tmp, sqlen (p[0], p[2]) * .25); + tmp = MAX (tmp, sqlen (p[1], p[3]) * .25); + return 18.0 * tmp; +} + +/* + * Split a 1D Bezier cubic using de Casteljau's algorithm. + * + * Input: x,y,z,w the nodes of the Bezier curve + * + * Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of + * the first half and of the second half of the curve + * + * The output control nodes have to be distinct. + */ +static inline void +split_bezier_1D (double x, double y, double z, double w, + double *x0, double *y0, double *z0, double *w0, + double *x1, double *y1, double *z1, double *w1) +{ + double tmp; + + *x0 = x; + *w1 = w; + + tmp = 0.5 * (y + z); + *y0 = 0.5 * (x + y); + *z1 = 0.5 * (z + w); + + *z0 = 0.5 * (*y0 + tmp); + *y1 = 0.5 * (tmp + *z1); + + *w0 = *x1 = 0.5 * (*z0 + *y1); +} + +/* + * Split a Bezier curve using de Casteljau's algorithm. + * + * Input: p[0..3] the nodes of the Bezier curve + * + * Output: fst_half[0..3] and snd_half[0..3] are respectively the + * nodes of the first and of the second half of the curve + * + * fst_half and snd_half must be different, but they can be the same as + * nodes. + */ +static void +split_bezier (cairo_point_double_t p[4], + cairo_point_double_t fst_half[4], + cairo_point_double_t snd_half[4]) +{ + split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x, + &fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x, + &snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x); + + split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y, + &fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y, + &snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y); +} + + +typedef enum _intersection { + INSIDE = -1, /* the interval is entirely contained in the reference interval */ + OUTSIDE = 0, /* the interval has no intersection with the reference interval */ + PARTIAL = 1 /* the interval intersects the reference interval (but is not fully inside it) */ +} intersection_t; + +/* + * Check if an interval if inside another. + * + * Input: a,b are the extrema of the first interval + * c,d are the extrema of the second interval + * + * Returns: INSIDE iff [a,b) intersection [c,d) = [a,b) + * OUTSIDE iff [a,b) intersection [c,d) = {} + * PARTIAL otherwise + * + * The function assumes a < b and c < d + * + * Note: Bitwise-anding the results along each component gives the + * expected result for [a,b) x [A,B) intersection [c,d) x [C,D). + */ +static inline int +intersect_interval (double a, double b, double c, double d) +{ + if (c <= a && b <= d) + return INSIDE; + else if (a >= d || b <= c) + return OUTSIDE; + else + return PARTIAL; +} + +/* + * Set the color of a pixel. + * + * Input: data is the base pointer of the image + * width, height are the dimensions of the image + * stride is the stride in bytes between adjacent rows + * x, y are the coordinates of the pixel to be colored + * r,g,b,a are the color components of the color to be set + * + * Output: the (x,y) pixel in data has the (r,g,b,a) color + * + * The input color components are not premultiplied, but the data + * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, + * premultiplied). + * + * If the pixel to be set is outside the image, this function does + * nothing. + */ +static inline void +draw_pixel (unsigned char *data, int width, int height, int stride, + int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a) +{ + if (likely (0 <= x && 0 <= y && x < width && y < height)) { + uint32_t tr, tg, tb, ta; + + /* Premultiply and round */ + ta = a; + tr = r * ta + 0x8000; + tg = g * ta + 0x8000; + tb = b * ta + 0x8000; + + tr += tr >> 16; + tg += tg >> 16; + tb += tb >> 16; + + *((uint32_t*) (data + y*(ptrdiff_t)stride + 4*x)) = ((ta << 16) & 0xff000000) | + ((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24); + } +} + +/* + * Forward-rasterize a cubic curve using forward differences. + * + * Input: data is the base pointer of the image + * width, height are the dimensions of the image + * stride is the stride in bytes between adjacent rows + * ushift is log2(n) if n is the number of desired steps + * dxu[i], dyu[i] are the x,y forward differences of the curve + * r0,g0,b0,a0 are the color components of the start point + * r3,g3,b3,a3 are the color components of the end point + * + * Output: data will be changed to have the requested curve drawn in + * the specified colors + * + * The input color components are not premultiplied, but the data + * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, + * premultiplied). + * + * The function draws n+1 pixels, that is from the point at step 0 to + * the point at step n, both included. This is the discrete equivalent + * to drawing the curve for values of the interpolation parameter in + * [0,1] (including both extremes). + */ +static inline void +rasterize_bezier_curve (unsigned char *data, int width, int height, int stride, + int ushift, double dxu[4], double dyu[4], + uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0, + uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3) +{ + int32_t xu[4], yu[4]; + int x0, y0, u, usteps = 1 << ushift; + + uint16_t r = r0, g = g0, b = b0, a = a0; + int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift); + int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift); + int16_t db = _color_delta_to_shifted_short (b0, b3, ushift); + int16_t da = _color_delta_to_shifted_short (a0, a3, ushift); + + fd_fixed (dxu, xu); + fd_fixed (dyu, yu); + + /* + * Use (dxu[0],dyu[0]) as origin for the forward differences. + * + * This makes it possible to handle much larger coordinates (the + * ones that can be represented as cairo_fixed_t) + */ + x0 = _cairo_fixed_from_double (dxu[0]); + y0 = _cairo_fixed_from_double (dyu[0]); + xu[0] = 0; + yu[0] = 0; + + for (u = 0; u <= usteps; ++u) { + /* + * This rasterizer assumes that pixels are integer aligned + * squares, so a generic (x,y) point belongs to the pixel with + * top-left coordinates (floor(x), floor(y)) + */ + + int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1)); + int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1)); + + draw_pixel (data, width, height, stride, x, y, r, g, b, a); + + fd_fixed_fwd (xu); + fd_fixed_fwd (yu); + r += dr; + g += dg; + b += db; + a += da; + } +} + +/* + * Clip, split and rasterize a Bezier curve. + * + * Input: data is the base pointer of the image + * width, height are the dimensions of the image + * stride is the stride in bytes between adjacent rows + * p[i] is the i-th node of the Bezier curve + * c0[i] is the i-th color component at the start point + * c3[i] is the i-th color component at the end point + * + * Output: data will be changed to have the requested curve drawn in + * the specified colors + * + * The input color components are not premultiplied, but the data + * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, + * premultiplied). + * + * The color components are red, green, blue and alpha, in this order. + * + * The function guarantees that it will draw the curve with a step + * small enough to never have a distance above 1/sqrt(2) between two + * consecutive points (which is needed to ensure that no hole can + * appear when using this function to rasterize a patch). + */ +static void +draw_bezier_curve (unsigned char *data, int width, int height, int stride, + cairo_point_double_t p[4], double c0[4], double c3[4]) +{ + double top, bottom, left, right, steps_sq; + int i, v; + + top = bottom = p[0].y; + for (i = 1; i < 4; ++i) { + top = MIN (top, p[i].y); + bottom = MAX (bottom, p[i].y); + } + + /* Check visibility */ + v = intersect_interval (top, bottom, 0, height); + if (v == OUTSIDE) + return; + + left = right = p[0].x; + for (i = 1; i < 4; ++i) { + left = MIN (left, p[i].x); + right = MAX (right, p[i].x); + } + + v &= intersect_interval (left, right, 0, width); + if (v == OUTSIDE) + return; + + steps_sq = bezier_steps_sq (p); + if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) { + /* + * The number of steps is greater than the threshold. This + * means that either the error would become too big if we + * directly rasterized it or that we can probably save some + * time by splitting the curve and clipping part of it + */ + cairo_point_double_t first[4], second[4]; + double midc[4]; + split_bezier (p, first, second); + midc[0] = (c0[0] + c3[0]) * 0.5; + midc[1] = (c0[1] + c3[1]) * 0.5; + midc[2] = (c0[2] + c3[2]) * 0.5; + midc[3] = (c0[3] + c3[3]) * 0.5; + draw_bezier_curve (data, width, height, stride, first, c0, midc); + draw_bezier_curve (data, width, height, stride, second, midc, c3); + } else { + double xu[4], yu[4]; + int ushift = sqsteps2shift (steps_sq), k; + + fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu); + fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu); + + for (k = 0; k < ushift; ++k) { + fd_down (xu); + fd_down (yu); + } + + rasterize_bezier_curve (data, width, height, stride, ushift, + xu, yu, + _cairo_color_double_to_short (c0[0]), + _cairo_color_double_to_short (c0[1]), + _cairo_color_double_to_short (c0[2]), + _cairo_color_double_to_short (c0[3]), + _cairo_color_double_to_short (c3[0]), + _cairo_color_double_to_short (c3[1]), + _cairo_color_double_to_short (c3[2]), + _cairo_color_double_to_short (c3[3])); + + /* Draw the end point, to make sure that we didn't leave it + * out because of rounding */ + draw_pixel (data, width, height, stride, + _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)), + _cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)), + _cairo_color_double_to_short (c3[0]), + _cairo_color_double_to_short (c3[1]), + _cairo_color_double_to_short (c3[2]), + _cairo_color_double_to_short (c3[3])); + } +} + +/* + * Forward-rasterize a cubic Bezier patch using forward differences. + * + * Input: data is the base pointer of the image + * 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 + * 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 + * the specified colors + * + * The nodes of the patch are as follows: + * + * u\v 0 - > 1 + * 0 p00 p01 p02 p03 + * | p10 p11 p12 p13 + * v p20 p21 p22 p23 + * 1 p30 p31 p32 p33 + * + * i.e. u varies along the first component (rows), v varies along the + * second one (columns). + * + * The color components are red, green, blue and alpha, in this order. + * c[0..3] are the colors in p00, p30, p03, p33 respectively + * + * The input color components are not premultiplied, but the data + * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, + * premultiplied). + * + * If the patch folds over itself, the part with the highest v + * parameter is considered above. If both have the same v, the one + * with the highest u parameter is above. + * + * The function draws n+1 curves, that is from the curve at step 0 to + * the curve at step n, both included. This is the discrete equivalent + * to drawing the patch for values of the interpolation parameter in + * [0,1] (including both extremes). + */ +static inline void +rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift, + cairo_point_double_t p[4][4], double col[4][4]) +{ + double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4]; + int v, i, k; + + v = 1 << vshift; + + /* + * pv[i][0] is the function (represented using forward + * differences) mapping v to the x coordinate of the i-th node of + * the Bezier curve with parameter u. + * (Likewise p[i][0] gives the y coordinate). + * + * This means that (pv[0][0][0],pv[0][1][0]), + * (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and + * (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for + * the "current" v value (see the FD comments for more details). + */ + for (i = 0; i < 4; ++i) { + fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]); + fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]); + for (k = 0; k < vshift; ++k) { + fd_down (pv[i][0]); + fd_down (pv[i][1]); + } + } + + for (i = 0; i < 4; ++i) { + cstart[i] = col[0][i]; + cend[i] = col[1][i]; + dcstart[i] = (col[2][i] - col[0][i]) / v; + dcend[i] = (col[3][i] - col[1][i]) / v; + } + + v++; + while (v--) { + cairo_point_double_t nodes[4]; + for (i = 0; i < 4; ++i) { + nodes[i].x = pv[i][0][0]; + nodes[i].y = pv[i][1][0]; + } + + draw_bezier_curve (data, width, height, stride, nodes, cstart, cend); + + for (i = 0; i < 4; ++i) { + fd_fwd (pv[i][0]); + fd_fwd (pv[i][1]); + cstart[i] += dcstart[i]; + cend[i] += dcend[i]; + } + } +} + +/* + * Clip, split and rasterize a Bezier cubic patch. + * + * Input: data is the base pointer of the image + * width, height are the dimensions of the image + * stride is the stride in bytes between adjacent rows + * p[i][j], p[i][j] are the nodes of the 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 + * the specified colors + * + * The nodes of the patch are as follows: + * + * u\v 0 - > 1 + * 0 p00 p01 p02 p03 + * | p10 p11 p12 p13 + * v p20 p21 p22 p23 + * 1 p30 p31 p32 p33 + * + * i.e. u varies along the first component (rows), v varies along the + * second one (columns). + * + * The color components are red, green, blue and alpha, in this order. + * c[0..3] are the colors in p00, p30, p03, p33 respectively + * + * The input color components are not premultiplied, but the data + * stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc, + * premultiplied). + * + * If the patch folds over itself, the part with the highest v + * parameter is considered above. If both have the same v, the one + * with the highest u parameter is above. + * + * The function guarantees that it will draw the patch with a step + * small enough to never have a distance above 1/sqrt(2) between two + * adjacent points (which guarantees that no hole can appear). + * + * This function can be used to rasterize a tile of PDF type 7 + * shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html). + */ +static void +draw_bezier_patch (unsigned char *data, int width, int height, int stride, + cairo_point_double_t p[4][4], double c[4][4]) +{ + double top, bottom, left, right, steps_sq; + int i, j, v; + + top = bottom = p[0][0].y; + for (i = 0; i < 4; ++i) { + for (j= 0; j < 4; ++j) { + top = MIN (top, p[i][j].y); + bottom = MAX (bottom, p[i][j].y); + } + } + + v = intersect_interval (top, bottom, 0, height); + if (v == OUTSIDE) + return; + + left = right = p[0][0].x; + for (i = 0; i < 4; ++i) { + for (j= 0; j < 4; ++j) { + left = MIN (left, p[i][j].x); + right = MAX (right, p[i][j].x); + } + } + + v &= intersect_interval (left, right, 0, width); + if (v == OUTSIDE) + return; + + steps_sq = 0; + for (i = 0; i < 4; ++i) + steps_sq = MAX (steps_sq, bezier_steps_sq (p[i])); + + if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) { + /* The number of steps is greater than the threshold. This + * means that either the error would become too big if we + * directly rasterized it or that we can probably save some + * time by splitting the curve and clipping part of it. The + * patch is only split in the v direction to guarantee that + * rasterizing each part will overwrite parts with low v with + * overlapping parts with higher v. */ + + cairo_point_double_t first[4][4], second[4][4]; + double subc[4][4]; + + for (i = 0; i < 4; ++i) + split_bezier (p[i], first[i], second[i]); + + for (i = 0; i < 4; ++i) { + subc[0][i] = c[0][i]; + subc[1][i] = c[1][i]; + subc[2][i] = 0.5 * (c[0][i] + c[2][i]); + subc[3][i] = 0.5 * (c[1][i] + c[3][i]); + } + + draw_bezier_patch (data, width, height, stride, first, subc); + + for (i = 0; i < 4; ++i) { + subc[0][i] = subc[2][i]; + subc[1][i] = subc[3][i]; + subc[2][i] = c[2][i]; + subc[3][i] = c[3][i]; + } + draw_bezier_patch (data, width, height, stride, second, subc); + } else { + rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c); + } +} + +/* + * Draw a tensor product shading pattern. + * + * Input: mesh is the mesh pattern + * data is the base pointer of the image + * width, height are the dimensions of the image + * stride is the stride in bytes between adjacent rows + * + * Output: data will be changed to have the pattern drawn on it + * + * data is assumed to be clear and its content is assumed to be in + * CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied). + * + * This function can be used to rasterize a PDF type 7 shading (see + * http://www.adobe.com/devnet/pdf/pdf_reference.html). + */ +void +_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh, + void *data, + int width, + int height, + int stride, + double x_offset, + double y_offset) +{ + cairo_point_double_t nodes[4][4]; + double colors[4][4]; + cairo_matrix_t p2u; + unsigned int i, j, k, n; + cairo_status_t status; + const cairo_mesh_patch_t *patch; + const cairo_color_t *c; + + assert (mesh->base.status == CAIRO_STATUS_SUCCESS); + assert (mesh->current_patch == NULL); + + p2u = mesh->base.matrix; + status = cairo_matrix_invert (&p2u); + assert (status == CAIRO_STATUS_SUCCESS); + + n = _cairo_array_num_elements (&mesh->patches); + patch = _cairo_array_index_const (&mesh->patches, 0); + for (i = 0; i < n; i++) { + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + nodes[j][k] = patch->points[j][k]; + cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y); + nodes[j][k].x += x_offset; + nodes[j][k].y += y_offset; + } + } + + c = &patch->colors[0]; + colors[0][0] = c->red; + colors[0][1] = c->green; + colors[0][2] = c->blue; + colors[0][3] = c->alpha; + + c = &patch->colors[3]; + colors[1][0] = c->red; + colors[1][1] = c->green; + colors[1][2] = c->blue; + colors[1][3] = c->alpha; + + c = &patch->colors[1]; + colors[2][0] = c->red; + colors[2][1] = c->green; + colors[2][2] = c->blue; + colors[2][3] = c->alpha; + + c = &patch->colors[2]; + colors[3][0] = c->red; + colors[3][1] = c->green; + colors[3][2] = c->blue; + colors[3][3] = c->alpha; + + draw_bezier_patch (data, width, height, stride, nodes, colors); + patch++; + } +} diff --git a/gfx/cairo/cairo/src/cairo-meta-surface-private.h b/gfx/cairo/cairo/src/cairo-meta-surface-private.h deleted file mode 100644 index f0c95c19c505..000000000000 --- a/gfx/cairo/cairo/src/cairo-meta-surface-private.h +++ /dev/null @@ -1,187 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Kristian Høgsberg - * Adrian Johnson - */ - -#ifndef CAIRO_META_SURFACE_H -#define CAIRO_META_SURFACE_H - -#include "cairoint.h" -#include "cairo-path-fixed-private.h" - -typedef enum { - /* The 5 basic drawing operations. */ - CAIRO_COMMAND_PAINT, - CAIRO_COMMAND_MASK, - CAIRO_COMMAND_STROKE, - CAIRO_COMMAND_FILL, - CAIRO_COMMAND_SHOW_TEXT_GLYPHS, - - /* Other junk. For most of these, we should be able to assert that - * they never get called except as part of fallbacks for the 5 - * basic drawing operations (which we implement already so the - * fallbacks should never get triggered). So the plan is to - * eliminate as many of these as possible. */ - - CAIRO_COMMAND_INTERSECT_CLIP_PATH - -} cairo_command_type_t; - -typedef enum { - CAIRO_META_REGION_ALL, - CAIRO_META_REGION_NATIVE, - CAIRO_META_REGION_IMAGE_FALLBACK -} cairo_meta_region_type_t; - -typedef struct _cairo_command_header { - cairo_command_type_t type; - cairo_meta_region_type_t region; - cairo_rectangle_int_t extents; -} cairo_command_header_t; - -typedef struct _cairo_command_paint { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; -} cairo_command_paint_t; - -typedef struct _cairo_command_mask { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - cairo_pattern_union_t mask; -} cairo_command_mask_t; - -typedef struct _cairo_command_stroke { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - cairo_path_fixed_t path; - cairo_stroke_style_t style; - cairo_matrix_t ctm; - cairo_matrix_t ctm_inverse; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_stroke_t; - -typedef struct _cairo_command_fill { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_fill_t; - -typedef struct _cairo_command_show_text_glyphs { - cairo_command_header_t header; - cairo_operator_t op; - cairo_pattern_union_t source; - char *utf8; - int utf8_len; - cairo_glyph_t *glyphs; - unsigned int num_glyphs; - cairo_text_cluster_t *clusters; - int num_clusters; - cairo_text_cluster_flags_t cluster_flags; - cairo_scaled_font_t *scaled_font; -} cairo_command_show_text_glyphs_t; - -typedef struct _cairo_command_intersect_clip_path { - cairo_command_header_t header; - cairo_path_fixed_t *path_pointer; - cairo_path_fixed_t path; - cairo_fill_rule_t fill_rule; - double tolerance; - cairo_antialias_t antialias; -} cairo_command_intersect_clip_path_t; - -typedef union _cairo_command { - cairo_command_header_t header; - - /* The 5 basic drawing operations. */ - cairo_command_paint_t paint; - cairo_command_mask_t mask; - cairo_command_stroke_t stroke; - cairo_command_fill_t fill; - cairo_command_show_text_glyphs_t show_text_glyphs; - - /* The other junk. */ - cairo_command_intersect_clip_path_t intersect_clip_path; -} cairo_command_t; - -typedef struct _cairo_meta_surface { - cairo_surface_t base; - - cairo_content_t content; - - /* A meta-surface is logically unbounded, but when used as a - * source we need to render it to an image, so we need a size at - * which to create that image. */ - double width_pixels; - double height_pixels; - cairo_rectangle_int_t extents; - - cairo_array_t commands; - cairo_surface_t *commands_owner; - - cairo_bool_t is_clipped; - int replay_start_idx; -} cairo_meta_surface_t; - -slim_hidden_proto (cairo_meta_surface_create); -slim_hidden_proto (cairo_meta_surface_replay); - -cairo_private cairo_int_status_t -_cairo_meta_surface_get_path (cairo_surface_t *surface, - cairo_path_fixed_t *path); - - -cairo_private cairo_status_t -_cairo_meta_surface_replay_analyze_meta_pattern (cairo_surface_t *surface, - cairo_surface_t *target); - -cairo_private cairo_status_t -_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target); -cairo_private cairo_status_t -_cairo_meta_surface_replay_region (cairo_surface_t *surface, - cairo_surface_t *target, - cairo_meta_region_type_t region); - -cairo_private cairo_bool_t -_cairo_surface_is_meta (const cairo_surface_t *surface); - -#endif /* CAIRO_META_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/cairo-misc.c b/gfx/cairo/cairo/src/cairo-misc.c index e0ed70cb6172..81f9325ca29a 100644 --- a/gfx/cairo/cairo/src/cairo-misc.c +++ b/gfx/cairo/cairo/src/cairo-misc.c @@ -38,10 +38,20 @@ * Adrian Johnson */ +#define _GNU_SOURCE 1 /* strtod_l() */ + #include "cairoint.h" #include "cairo-error-private.h" -COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < CAIRO_INT_STATUS_UNSUPPORTED); +#include +#include +#include +#include +#ifdef HAVE_XLOCALE_H +#include +#endif + +COMPILE_TIME_ASSERT ((int)CAIRO_STATUS_LAST_STATUS < (int)CAIRO_INT_STATUS_UNSUPPORTED); COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); /** @@ -49,7 +59,7 @@ COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); * @Title: Error handling * @Short_Description: Decoding cairo's status * @See_Also: cairo_status(), cairo_surface_status(), cairo_pattern_status(), - * cairo_font_face_status(), cairo_scaled_font_status(), + * cairo_font_face_status(), cairo_scaled_font_status(), * cairo_region_status() * * Cairo uses a single status type to represent all kinds of errors. A status @@ -62,7 +72,7 @@ COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); * the mean time, it is safe to call all cairo functions normally even if the * underlying object is in an error status. This means that no error handling * code is required before or after each individual cairo function call. - */ + **/ /* Public stuff */ @@ -73,7 +83,9 @@ COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127); * Provides a human-readable description of a #cairo_status_t. * * Returns: a string representation of the status - */ + * + * Since: 1.0 + **/ const char * cairo_status_to_string (cairo_status_t status) { @@ -150,6 +162,20 @@ cairo_status_to_string (cairo_status_t status) return "the device type is not appropriate for the operation"; case CAIRO_STATUS_DEVICE_ERROR: return "an operation to the device caused an unspecified error"; + case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: + return "invalid operation during mesh pattern construction"; + case CAIRO_STATUS_DEVICE_FINISHED: + return "the target device has been finished"; + case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + return "CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID used but no CAIRO_MIME_TYPE_JBIG2_GLOBAL data provided"; + case CAIRO_STATUS_PNG_ERROR: + return "error occurred in libpng while reading from or writing to a PNG file"; + case CAIRO_STATUS_FREETYPE_ERROR: + return "error occurred in libfreetype"; + case CAIRO_STATUS_WIN32_GDI_ERROR: + return "error occurred in the Windows Graphics Device Interface"; + case CAIRO_STATUS_TAG_ERROR: + return "invalid tag name, attributes, or nesting"; default: case CAIRO_STATUS_LAST_STATUS: return ""; @@ -176,7 +202,7 @@ cairo_status_to_string (cairo_status_t status) * freed using cairo_glyph_free() * * Since: 1.8 - */ + **/ cairo_glyph_t * cairo_glyph_allocate (int num_glyphs) { @@ -199,12 +225,11 @@ slim_hidden_def (cairo_glyph_allocate); * for glyphs. * * Since: 1.8 - */ + **/ void cairo_glyph_free (cairo_glyph_t *glyphs) { - if (glyphs) - free (glyphs); + free (glyphs); } slim_hidden_def (cairo_glyph_free); @@ -227,7 +252,7 @@ slim_hidden_def (cairo_glyph_free); * freed using cairo_text_cluster_free() * * Since: 1.8 - */ + **/ cairo_text_cluster_t * cairo_text_cluster_allocate (int num_clusters) { @@ -250,12 +275,11 @@ slim_hidden_def (cairo_text_cluster_allocate); * for text clusters. * * Since: 1.8 - */ + **/ void cairo_text_cluster_free (cairo_text_cluster_t *clusters) { - if (clusters) - free (clusters); + free (clusters); } slim_hidden_def (cairo_text_cluster_free); @@ -279,7 +303,7 @@ slim_hidden_def (cairo_text_cluster_free); * %CAIRO_STATUS_INVALID_CLUSTERS on error. * The error is either invalid UTF-8 input, * or bad cluster mapping. - */ + **/ cairo_status_t _cairo_validate_text_clusters (const char *utf8, int utf8_len, @@ -483,7 +507,7 @@ _cairo_operator_bounded_by_either (cairo_operator_t op) } -#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L +#if DISABLE_SOME_FLOATING_POINT /* This function is identical to the C99 function lround(), except that it * performs arithmetic rounding (floor(d + .5) instead of away-from-zero rounding) and * has a valid input range of (INT_MIN, INT_MAX] instead of @@ -753,6 +777,172 @@ _cairo_half_from_float (float f) } } +#ifndef __BIONIC__ +# include + +const char * +_cairo_get_locale_decimal_point (void) +{ + struct lconv *locale_data = localeconv (); + return locale_data->decimal_point; +} + +#else +/* Android's Bionic libc doesn't provide decimal_point */ +const char * +_cairo_get_locale_decimal_point (void) +{ + return "."; +} +#endif + +#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) + +static locale_t C_locale; + +static locale_t +get_C_locale (void) +{ + locale_t C; + +retry: + C = (locale_t) _cairo_atomic_ptr_get ((void **) &C_locale); + + if (unlikely (!C)) { + C = newlocale (LC_ALL_MASK, "C", NULL); + + if (!_cairo_atomic_ptr_cmpxchg ((void **) &C_locale, NULL, C)) { + freelocale (C_locale); + goto retry; + } + } + + return C; +} + +double +_cairo_strtod (const char *nptr, char **endptr) +{ + return strtod_l (nptr, endptr, get_C_locale ()); +} + +#else + +/* strtod replacement that ignores locale and only accepts decimal points */ +double +_cairo_strtod (const char *nptr, char **endptr) +{ + const char *decimal_point; + int decimal_point_len; + const char *p; + char buf[100]; + char *bufptr; + char *bufend = buf + sizeof(buf) - 1; + double value; + char *end; + int delta; + cairo_bool_t have_dp; + + decimal_point = _cairo_get_locale_decimal_point (); + decimal_point_len = strlen (decimal_point); + assert (decimal_point_len != 0); + + p = nptr; + bufptr = buf; + delta = 0; + have_dp = FALSE; + while (*p && _cairo_isspace (*p)) { + p++; + delta++; + } + + while (*p && (bufptr + decimal_point_len < bufend)) { + if (_cairo_isdigit (*p)) { + *bufptr++ = *p; + } else if (*p == '.') { + if (have_dp) + break; + strncpy (bufptr, decimal_point, decimal_point_len); + bufptr += decimal_point_len; + delta -= decimal_point_len - 1; + have_dp = TRUE; + } else { + break; + } + p++; + } + *bufptr = 0; + + value = strtod (buf, &end); + if (endptr) { + if (end == buf) + *endptr = (char*)(nptr); + else + *endptr = (char*)(nptr + (end - buf) + delta); + } + + return value; +} +#endif + +/** + * _cairo_fopen: + * @filename: filename to open + * @mode: mode string with which to open the file + * @file_out: reference to file handle + * + * Exactly like the C library function, but possibly doing encoding + * conversion on the filename. On all platforms, the filename is + * passed directly to the system, but on Windows, the filename is + * interpreted as UTF-8, rather than in a codepage that would depend + * on system settings. + * + * Return value: CAIRO_STATUS_SUCCESS when the filename was converted + * successfully to the native encoding, or the error reported by + * _cairo_utf8_to_utf16 otherwise. To check if the file handle could + * be obtained, dereference file_out and compare its value against + * NULL + **/ +cairo_status_t +_cairo_fopen (const char *filename, const char *mode, FILE **file_out) +{ + FILE *result; +#ifdef _WIN32 /* also defined on x86_64 */ + uint16_t *filename_w; + uint16_t *mode_w; + cairo_status_t status; + + *file_out = NULL; + + if (filename == NULL || mode == NULL) { + errno = EINVAL; + return CAIRO_STATUS_SUCCESS; + } + + if ((status = _cairo_utf8_to_utf16 (filename, -1, &filename_w, NULL)) != CAIRO_STATUS_SUCCESS) { + errno = EINVAL; + return status; + } + + if ((status = _cairo_utf8_to_utf16 (mode, -1, &mode_w, NULL)) != CAIRO_STATUS_SUCCESS) { + free (filename_w); + errno = EINVAL; + return status; + } + + result = _wfopen(filename_w, mode_w); + + free (filename_w); + free (mode_w); + +#else /* Use fopen directly */ + result = fopen (filename, mode); +#endif + + *file_out = result; + + return CAIRO_STATUS_SUCCESS; +} #ifdef _WIN32 @@ -772,7 +962,7 @@ _cairo_half_from_float (float f) /* tmpfile() replacement for Windows. * * On Windows tmpfile() creates the file in the root directory. This - * may fail due to unsufficient privileges. However, this isn't a + * may fail due to insufficient privileges. However, this isn't a * problem on Windows CE so we don't use it there. */ FILE * @@ -830,13 +1020,13 @@ typedef struct _cairo_intern_string { static cairo_hash_table_t *_cairo_intern_string_ht; -static unsigned long -_intern_string_hash (const char *str, int len) +unsigned long +_cairo_string_hash (const char *str, int len) { const signed char *p = (const signed char *) str; unsigned int h = *p; - for (p += 1; --len; p++) + for (p += 1; len > 0; len--, p++) h = (h << 5) - h + *p; return h; @@ -866,7 +1056,7 @@ _cairo_intern_string (const char **str_inout, int len) if (len < 0) len = strlen (str); - tmpl.hash_entry.hash = _intern_string_hash (str, len); + tmpl.hash_entry.hash = _cairo_string_hash (str, len); tmpl.len = len; tmpl.string = (char *) str; @@ -882,7 +1072,7 @@ _cairo_intern_string (const char **str_inout, int len) istring = _cairo_hash_table_lookup (_cairo_intern_string_ht, &tmpl.hash_entry); if (istring == NULL) { - istring = malloc (sizeof (cairo_intern_string_t) + len + 1); + istring = _cairo_malloc (sizeof (cairo_intern_string_t) + len + 1); if (likely (istring != NULL)) { istring->hash_entry.hash = tmpl.hash_entry.hash; istring->len = tmpl.len; diff --git a/gfx/cairo/cairo/src/cairo-mono-scan-converter.c b/gfx/cairo/cairo/src/cairo-mono-scan-converter.c new file mode 100644 index 000000000000..891f435c9eb5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mono-scan-converter.c @@ -0,0 +1,612 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* + * Copyright (c) 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "cairoint.h" +#include "cairo-spans-private.h" +#include "cairo-error-private.h" + +#include +#include +#include + +struct quorem { + int32_t quo; + int32_t rem; +}; + +struct edge { + struct edge *next, *prev; + + int32_t height_left; + int32_t dir; + int32_t vertical; + + int32_t dy; + struct quorem x; + struct quorem dxdy; +}; + +/* A collection of sorted and vertically clipped edges of the polygon. + * Edges are moved from the polygon to an active list while scan + * converting. */ +struct polygon { + /* The vertical clip extents. */ + int32_t ymin, ymax; + + int num_edges; + struct edge *edges; + + /* Array of edges all starting in the same bucket. An edge is put + * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when + * it is added to the polygon. */ + struct edge **y_buckets; + + struct edge *y_buckets_embedded[64]; + struct edge edges_embedded[32]; +}; + +struct mono_scan_converter { + struct polygon polygon[1]; + + /* Leftmost edge on the current scan line. */ + struct edge head, tail; + int is_vertical; + + cairo_half_open_span_t *spans; + cairo_half_open_span_t spans_embedded[64]; + int num_spans; + + /* Clip box. */ + int32_t xmin, xmax; + int32_t ymin, ymax; +}; + +#define I(x) _cairo_fixed_integer_round_down(x) + +/* Compute the floored division a/b. Assumes / and % perform symmetric + * division. */ +inline static struct quorem +floored_divrem(int a, int b) +{ + struct quorem qr; + qr.quo = a/b; + qr.rem = a%b; + if ((a^b)<0 && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric + * division. */ +static struct quorem +floored_muldivrem(int x, int a, int b) +{ + struct quorem qr; + long long xa = (long long)x*a; + qr.quo = xa/b; + qr.rem = xa%b; + if ((xa>=0) != (b>=0) && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +static cairo_status_t +polygon_init (struct polygon *polygon, int ymin, int ymax) +{ + unsigned h = ymax - ymin + 1; + + polygon->y_buckets = polygon->y_buckets_embedded; + if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) { + polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *)); + if (unlikely (NULL == polygon->y_buckets)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memset (polygon->y_buckets, 0, h * sizeof (struct edge *)); + polygon->y_buckets[h-1] = (void *)-1; + + polygon->ymin = ymin; + polygon->ymax = ymax; + return CAIRO_STATUS_SUCCESS; +} + +static void +polygon_fini (struct polygon *polygon) +{ + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + if (polygon->edges != polygon->edges_embedded) + free (polygon->edges); +} + +static void +_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, + struct edge *e, + int y) +{ + struct edge **ptail = &polygon->y_buckets[y - polygon->ymin]; + if (*ptail) + (*ptail)->prev = e; + e->next = *ptail; + e->prev = NULL; + *ptail = e; +} + +inline static void +polygon_add_edge (struct polygon *polygon, + const cairo_edge_t *edge) +{ + struct edge *e; + cairo_fixed_t dx; + cairo_fixed_t dy; + int y, ytop, ybot; + int ymin = polygon->ymin; + int ymax = polygon->ymax; + + y = I(edge->top); + ytop = MAX(y, ymin); + + y = I(edge->bottom); + ybot = MIN(y, ymax); + + if (ybot <= ytop) + return; + + e = polygon->edges + polygon->num_edges++; + e->height_left = ybot - ytop; + e->dir = edge->dir; + + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + + if (dx == 0) { + e->vertical = TRUE; + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + e->dxdy.quo = 0; + e->dxdy.rem = 0; + e->dy = 0; + } else { + e->vertical = FALSE; + e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy); + e->dy = dy; + + e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y, + dx, dy); + e->x.quo += edge->line.p1.x; + } + e->x.rem -= dy; + + _polygon_insert_edge_into_its_y_bucket (polygon, e, ytop); +} + +static struct edge * +merge_sorted_edges (struct edge *head_a, struct edge *head_b) +{ + struct edge *head, **next, *prev; + int32_t x; + + prev = head_a->prev; + next = &head; + if (head_a->x.quo <= head_b->x.quo) { + head = head_a; + } else { + head = head_b; + head_b->prev = prev; + goto start_with_b; + } + + do { + x = head_b->x.quo; + while (head_a != NULL && head_a->x.quo <= x) { + prev = head_a; + next = &head_a->next; + head_a = head_a->next; + } + + head_b->prev = prev; + *next = head_b; + if (head_a == NULL) + return head; + +start_with_b: + x = head_a->x.quo; + while (head_b != NULL && head_b->x.quo <= x) { + prev = head_b; + next = &head_b->next; + head_b = head_b->next; + } + + head_a->prev = prev; + *next = head_a; + if (head_b == NULL) + return head; + } while (1); +} + +static struct edge * +sort_edges (struct edge *list, + unsigned int level, + struct edge **head_out) +{ + struct edge *head_other, *remaining; + unsigned int i; + + head_other = list->next; + + if (head_other == NULL) { + *head_out = list; + return NULL; + } + + remaining = head_other->next; + if (list->x.quo <= head_other->x.quo) { + *head_out = list; + head_other->next = NULL; + } else { + *head_out = head_other; + head_other->prev = list->prev; + head_other->next = list; + list->prev = head_other; + list->next = NULL; + } + + for (i = 0; i < level && remaining; i++) { + remaining = sort_edges (remaining, i, &head_other); + *head_out = merge_sorted_edges (*head_out, head_other); + } + + return remaining; +} + +static struct edge * +merge_unsorted_edges (struct edge *head, struct edge *unsorted) +{ + sort_edges (unsorted, UINT_MAX, &unsorted); + return merge_sorted_edges (head, unsorted); +} + +inline static void +active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges) +{ + struct edge *e; + + for (e = edges; c->is_vertical && e; e = e->next) + c->is_vertical = e->vertical; + + c->head.next = merge_unsorted_edges (c->head.next, edges); +} + +inline static void +add_span (struct mono_scan_converter *c, int x1, int x2) +{ + int n; + + if (x1 < c->xmin) + x1 = c->xmin; + if (x2 > c->xmax) + x2 = c->xmax; + if (x2 <= x1) + return; + + n = c->num_spans++; + c->spans[n].x = x1; + c->spans[n].coverage = 255; + + n = c->num_spans++; + c->spans[n].x = x2; + c->spans[n].coverage = 0; +} + +inline static void +row (struct mono_scan_converter *c, unsigned int mask) +{ + struct edge *edge = c->head.next; + int xstart = INT_MIN, prev_x = INT_MIN; + int winding = 0; + + c->num_spans = 0; + while (&c->tail != edge) { + struct edge *next = edge->next; + int xend = I(edge->x.quo); + + if (--edge->height_left) { + if (!edge->vertical) { + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } + } + + if (edge->x.quo < prev_x) { + struct edge *pos = edge->prev; + pos->next = next; + next->prev = pos; + do { + pos = pos->prev; + } while (edge->x.quo < pos->x.quo); + pos->next->prev = edge; + edge->next = pos->next; + edge->prev = pos; + pos->next = edge; + } else + prev_x = edge->x.quo; + } else { + edge->prev->next = next; + next->prev = edge->prev; + } + + winding += edge->dir; + if ((winding & mask) == 0) { + if (I(next->x.quo) > xend + 1) { + add_span (c, xstart, xend); + xstart = INT_MIN; + } + } else if (xstart == INT_MIN) + xstart = xend; + + edge = next; + } +} + +inline static void dec (struct edge *e, int h) +{ + e->height_left -= h; + if (e->height_left == 0) { + e->prev->next = e->next; + e->next->prev = e->prev; + } +} + +static cairo_status_t +_mono_scan_converter_init(struct mono_scan_converter *c, + int xmin, int ymin, + int xmax, int ymax) +{ + cairo_status_t status; + int max_num_spans; + + status = polygon_init (c->polygon, ymin, ymax); + if (unlikely (status)) + return status; + + max_num_spans = xmax - xmin + 1; + if (max_num_spans > ARRAY_LENGTH(c->spans_embedded)) { + c->spans = _cairo_malloc_ab (max_num_spans, + sizeof (cairo_half_open_span_t)); + if (unlikely (c->spans == NULL)) { + polygon_fini (c->polygon); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } else + c->spans = c->spans_embedded; + + c->xmin = xmin; + c->xmax = xmax; + c->ymin = ymin; + c->ymax = ymax; + + c->head.vertical = 1; + c->head.height_left = INT_MAX; + c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN)); + c->head.prev = NULL; + c->head.next = &c->tail; + c->tail.prev = &c->head; + c->tail.next = NULL; + c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX)); + c->tail.height_left = INT_MAX; + c->tail.vertical = 1; + + c->is_vertical = 1; + return CAIRO_STATUS_SUCCESS; +} + +static void +_mono_scan_converter_fini(struct mono_scan_converter *self) +{ + if (self->spans != self->spans_embedded) + free (self->spans); + + polygon_fini(self->polygon); +} + +static cairo_status_t +mono_scan_converter_allocate_edges(struct mono_scan_converter *c, + int num_edges) + +{ + c->polygon->num_edges = 0; + c->polygon->edges = c->polygon->edges_embedded; + if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) { + c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge)); + if (unlikely (c->polygon->edges == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +mono_scan_converter_add_edge (struct mono_scan_converter *c, + const cairo_edge_t *edge) +{ + polygon_add_edge (c->polygon, edge); +} + +static void +step_edges (struct mono_scan_converter *c, int count) +{ + struct edge *edge; + + for (edge = c->head.next; edge != &c->tail; edge = edge->next) { + edge->height_left -= count; + if (! edge->height_left) { + edge->prev->next = edge->next; + edge->next->prev = edge->prev; + } + } +} + +static cairo_status_t +mono_scan_converter_render(struct mono_scan_converter *c, + unsigned int winding_mask, + cairo_span_renderer_t *renderer) +{ + struct polygon *polygon = c->polygon; + int i, j, h = c->ymax - c->ymin; + cairo_status_t status; + + for (i = 0; i < h; i = j) { + j = i + 1; + + if (polygon->y_buckets[i]) + active_list_merge_edges (c, polygon->y_buckets[i]); + + if (c->is_vertical) { + int min_height; + struct edge *e; + + e = c->head.next; + min_height = e->height_left; + while (e != &c->tail) { + if (e->height_left < min_height) + min_height = e->height_left; + e = e->next; + } + + while (--min_height >= 1 && polygon->y_buckets[j] == NULL) + j++; + if (j != i + 1) + step_edges (c, j - (i + 1)); + } + + row (c, winding_mask); + if (c->num_spans) { + status = renderer->render_rows (renderer, c->ymin+i, j-i, + c->spans, c->num_spans); + if (unlikely (status)) + return status; + } + + /* XXX recompute after dropping edges? */ + if (c->head.next == &c->tail) + c->is_vertical = 1; + } + + return CAIRO_STATUS_SUCCESS; +} + +struct _cairo_mono_scan_converter { + cairo_scan_converter_t base; + + struct mono_scan_converter converter[1]; + cairo_fill_rule_t fill_rule; +}; + +typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t; + +static void +_cairo_mono_scan_converter_destroy (void *converter) +{ + cairo_mono_scan_converter_t *self = converter; + _mono_scan_converter_fini (self->converter); + free(self); +} + +cairo_status_t +_cairo_mono_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon) +{ + cairo_mono_scan_converter_t *self = converter; + cairo_status_t status; + int i; + +#if 0 + FILE *file = fopen ("polygon.txt", "w"); + _cairo_debug_print_polygon (file, polygon); + fclose (file); +#endif + + status = mono_scan_converter_allocate_edges (self->converter, + polygon->num_edges); + if (unlikely (status)) + return status; + + for (i = 0; i < polygon->num_edges; i++) + mono_scan_converter_add_edge (self->converter, &polygon->edges[i]); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_mono_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_mono_scan_converter_t *self = converter; + + return mono_scan_converter_render (self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, + renderer); +} + +cairo_scan_converter_t * +_cairo_mono_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule) +{ + cairo_mono_scan_converter_t *self; + cairo_status_t status; + + self = _cairo_malloc (sizeof(struct _cairo_mono_scan_converter)); + if (unlikely (self == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail_nomem; + } + + self->base.destroy = _cairo_mono_scan_converter_destroy; + self->base.generate = _cairo_mono_scan_converter_generate; + + status = _mono_scan_converter_init (self->converter, + xmin, ymin, xmax, ymax); + if (unlikely (status)) + goto bail; + + self->fill_rule = fill_rule; + + return &self->base; + + bail: + self->base.destroy(&self->base); + bail_nomem: + return _cairo_scan_converter_create_in_error (status); +} diff --git a/gfx/cairo/cairo/src/cairo-mutex-impl-private.h b/gfx/cairo/cairo/src/cairo-mutex-impl-private.h index 81c998515ab2..25223f3eac0b 100644 --- a/gfx/cairo/cairo/src/cairo-mutex-impl-private.h +++ b/gfx/cairo/cairo/src/cairo-mutex-impl-private.h @@ -236,7 +236,6 @@ # define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL) #endif # define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex)) -# define CAIRO_MUTEX_IMPL_TRY_LOCK(mutex) (! pthread_mutex_trylock (&(mutex))) # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) #if HAVE_LOCKDEP # define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) diff --git a/gfx/cairo/cairo/src/cairo-mutex-list-private.h b/gfx/cairo/cairo/src/cairo-mutex-list-private.h index 7d5ba0299756..ca74030062d8 100644 --- a/gfx/cairo/cairo/src/cairo-mutex-list-private.h +++ b/gfx/cairo/cairo/src/cairo-mutex-list-private.h @@ -40,12 +40,12 @@ CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex) -CAIRO_MUTEX_DECLARE (_cairo_error_mutex) CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex) CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex) CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex) +CAIRO_MUTEX_DECLARE (_cairo_glyph_cache_mutex) #if CAIRO_HAS_FT_FONT CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) @@ -53,6 +53,7 @@ CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex) #if CAIRO_HAS_WIN32_FONT CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex) +CAIRO_MUTEX_DECLARE (_cairo_win32_font_dc_mutex) #endif #if CAIRO_HAS_XLIB_SURFACE diff --git a/gfx/cairo/cairo/src/cairo-mutex-type-private.h b/gfx/cairo/cairo/src/cairo-mutex-type-private.h index a706f42b0076..e8c493985c9c 100644 --- a/gfx/cairo/cairo/src/cairo-mutex-type-private.h +++ b/gfx/cairo/cairo/src/cairo-mutex-type-private.h @@ -167,7 +167,6 @@ typedef cairo_recursive_mutex_impl_t cairo_recursive_mutex_t; #define CAIRO_MUTEX_INITIALIZE CAIRO_MUTEX_IMPL_INITIALIZE #define CAIRO_MUTEX_FINALIZE CAIRO_MUTEX_IMPL_FINALIZE #define CAIRO_MUTEX_LOCK CAIRO_MUTEX_IMPL_LOCK -#define CAIRO_MUTEX_TRY_LOCK CAIRO_MUTEX_IMPL_TRY_LOCK #define CAIRO_MUTEX_UNLOCK CAIRO_MUTEX_IMPL_UNLOCK #define CAIRO_MUTEX_INIT CAIRO_MUTEX_IMPL_INIT #define CAIRO_MUTEX_FINI CAIRO_MUTEX_IMPL_FINI diff --git a/gfx/cairo/cairo/src/cairo-no-compositor.c b/gfx/cairo/cairo/src/cairo-no-compositor.c new file mode 100644 index 000000000000..1602a12f63a1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-no-compositor.c @@ -0,0 +1,107 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" + +static cairo_int_status_t +_cairo_no_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_no_compositor_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) +{ + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_no_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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) +{ + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_no_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_no_compositor_glyphs (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +const cairo_compositor_t __cairo_no_compositor = { + NULL, + _cairo_no_compositor_paint, + _cairo_no_compositor_mask, + _cairo_no_compositor_stroke, + _cairo_no_compositor_fill, + _cairo_no_compositor_glyphs, +}; diff --git a/gfx/cairo/cairo/src/cairo-no-features.h b/gfx/cairo/cairo/src/cairo-no-features.h deleted file mode 100644 index 9b3d86be2637..000000000000 --- a/gfx/cairo/cairo/src/cairo-no-features.h +++ /dev/null @@ -1,12 +0,0 @@ -/* Generated by configure. Do not edit */ -#ifndef CAIRO_NO_FEATURES_H -#define CAIRO_NO_FEATURES_H - -#include - -/* This is a dummy header, to trick gtk-doc only */ - -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_WIN32_SURFACE 1 - -#endif diff --git a/gfx/cairo/cairo/src/cairo-observer.c b/gfx/cairo/cairo/src/cairo-observer.c index 7c7b69c91f0a..36d6b93bd634 100644 --- a/gfx/cairo/cairo/src/cairo-observer.c +++ b/gfx/cairo/cairo/src/cairo-observer.c @@ -36,6 +36,8 @@ #include "cairoint.h" +#include "cairo-list-inline.h" + void _cairo_observers_notify (cairo_list_t *observers, void *arg) { diff --git a/gfx/cairo/cairo/src/cairo-os2-private.h b/gfx/cairo/cairo/src/cairo-os2-private.h index 3e123bc7899e..829dd3c8d41f 100644 --- a/gfx/cairo/cairo/src/cairo-os2-private.h +++ b/gfx/cairo/cairo/src/cairo-os2-private.h @@ -1,4 +1,4 @@ -/* vim: set sw=2 sts=2 et cin: */ +/* vim: set sw=4 sts=4 et cin: */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2005-2006 netlabs.org diff --git a/gfx/cairo/cairo/src/cairo-os2-surface.c b/gfx/cairo/cairo/src/cairo-os2-surface.c index c2f7669cd8d1..84f08c8077fb 100644 --- a/gfx/cairo/cairo/src/cairo-os2-surface.c +++ b/gfx/cairo/cairo/src/cairo-os2-surface.c @@ -1,4 +1,4 @@ -/* vim: set sw=2 sts=2 et cin: */ +/* vim: set sw=4 sts=4 et cin: */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2005-2006 netlabs.org @@ -39,7 +39,10 @@ #include "cairoint.h" #include "cairo-os2-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-surface-fallback-private.h" +#include "cairo-image-surface-private.h" #if CAIRO_HAS_FC_FONT #include @@ -195,7 +198,7 @@ void *_buffer_alloc (size_t a, size_t b, const unsigned int size) return NULL; #else /* Clear the malloc'd buffer the way DosAllocMem() does. */ - buffer = malloc (nbytes); + buffer = _cairo_malloc (nbytes); if (buffer) { memset (buffer, 0, nbytes); } @@ -550,25 +553,17 @@ _cairo_os2_surface_acquire_source_image (void *abstract_surfac cairo_image_surface_t **image_out, void **image_extra) { - cairo_os2_surface_t *local_os2_surface; + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); /* Increase lend counter */ - local_os2_surface->pixel_array_lend_count++; + surface->pixel_array_lend_count++; - *image_out = local_os2_surface->image_surface; + *image_out = surface->image_surface; *image_extra = NULL; - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + DosReleaseMutexSem (surface->hmtx_use_private_fields); return CAIRO_STATUS_SUCCESS; } @@ -578,156 +573,110 @@ _cairo_os2_surface_release_source_image (void *abstract_surface cairo_image_surface_t *image, void *image_extra) { - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; /* Decrease Lend counter! */ - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - if (local_os2_surface->pixel_array_lend_count > 0) - local_os2_surface->pixel_array_lend_count--; - DosPostEventSem (local_os2_surface->hev_pixel_array_came_back); + if (surface->pixel_array_lend_count > 0) + surface->pixel_array_lend_count--; + DosPostEventSem (surface->hev_pixel_array_came_back); - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); - return; + DosReleaseMutexSem (surface->hmtx_use_private_fields); } -static cairo_status_t -_cairo_os2_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, - void **image_extra) +static cairo_image_surface_t * +_cairo_os2_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) { - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } - - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); /* Increase lend counter */ - local_os2_surface->pixel_array_lend_count++; - - *image_out = local_os2_surface->image_surface; - *image_extra = NULL; - - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = local_os2_surface->bitmap_info.cx; - image_rect->height = local_os2_surface->bitmap_info.cy; - + surface->pixel_array_lend_count++; DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + /* XXX: BROKEN! */ + *image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface, + extents); + return CAIRO_STATUS_SUCCESS; } -static void -_cairo_os2_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) +static cairo_int_status_t +_cairo_os2_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) { - cairo_os2_surface_t *local_os2_surface; - RECTL rclToBlit; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return; - } + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; /* So, we got back the image, and if all goes well, then * something has been changed inside the interest_rect. * So, we blit it to the screen! */ + if (surface->blit_as_changes) { + RECTL rclToBlit; - if (local_os2_surface->blit_as_changes) { /* Get mutex, we'll work with the pixel array! */ - if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { + if (DosRequestMutexSem (surface->hmtx_use_private_fields, + SEM_INDEFINITE_WAIT) != NO_ERROR) + { /* Could not get mutex! */ return; } - if (local_os2_surface->hwnd_client_window) { + rclToBlit.xLeft = image->base.device_transform_inverse.x0; + rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */ + rclToBlit.yTop = image->base.device_transform_inverse.y0; + rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */ + + if (surface->hwnd_client_window) { /* We know the HWND, so let's invalidate the window region, * so the application will redraw itself, using the * cairo_os2_surface_refresh_window () API from its own PM thread. * * This is the safe method, which should be preferred every time. */ - rclToBlit.xLeft = interest_rect->x; - rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */ - rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (interest_rect->y); - rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (interest_rect->y+interest_rect->height); /* Noninclusive */ - - WinInvalidateRect (local_os2_surface->hwnd_client_window, + rclToBlit.yTop = surface->bitmap_info.cy - rclToBlit.yTop; + rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop; + WinInvalidateRect (surface->hwnd_client_window, &rclToBlit, FALSE); } else { /* We don't know the HWND, so try to blit the pixels from here! * Please note that it can be problematic if this is not the PM thread! * - * It can cause internal PM stuffs to be scewed up, for some reason. + * It can cause internal PM stuffs to be screwed up, for some reason. * Please always tell the HWND to the surface using the * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window () * from your WM_PAINT, if it's possible! */ - rclToBlit.xLeft = interest_rect->x; - rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */ - rclToBlit.yBottom = interest_rect->y; - rclToBlit.yTop = interest_rect->y+interest_rect->height; /* Noninclusive */ - /* Now blit there the stuffs! */ - _cairo_os2_surface_blit_pixels (local_os2_surface, - local_os2_surface->hps_client_window, + _cairo_os2_surface_blit_pixels (surface, + surface->hps_client_window, &rclToBlit); } - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + DosReleaseMutexSem (surface->hmtx_use_private_fields); } /* Also decrease Lend counter! */ - DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); - if (local_os2_surface->pixel_array_lend_count > 0) - local_os2_surface->pixel_array_lend_count--; - DosPostEventSem (local_os2_surface->hev_pixel_array_came_back); + if (surface->pixel_array_lend_count > 0) + surface->pixel_array_lend_count--; + DosPostEventSem (surface->hev_pixel_array_came_back); - DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + DosReleaseMutexSem (surface->hmtx_use_private_fields); } static cairo_bool_t _cairo_os2_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { - cairo_os2_surface_t *local_os2_surface; - - local_os2_surface = (cairo_os2_surface_t *) abstract_surface; - if ((!local_os2_surface) || - (local_os2_surface->base.backend != &cairo_os2_surface_backend)) - { - /* Invalid parameter (wrong surface)! */ - return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - } + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; rectangle->x = 0; rectangle->y = 0; - rectangle->width = local_os2_surface->bitmap_info.cx; - rectangle->height = local_os2_surface->bitmap_info.cy; + rectangle->width = surface->bitmap_info.cx; + rectangle->height = surface->bitmap_info.cy; return TRUE; } @@ -769,7 +718,7 @@ cairo_os2_surface_create (HPS hps_client_window, } /* Allocate an OS/2 surface structure. */ - local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); + local_os2_surface = (cairo_os2_surface_t *) _cairo_malloc (sizeof (cairo_os2_surface_t)); if (!local_os2_surface) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto error_exit; @@ -831,14 +780,15 @@ cairo_os2_surface_create (HPS hps_client_window, _cairo_surface_init (&local_os2_surface->base, &cairo_os2_surface_backend, NULL, /* device */ - _cairo_content_from_format (CAIRO_FORMAT_ARGB32)); + _cairo_content_from_format (CAIRO_FORMAT_ARGB32), + FALSE); /* is_vector */ /* Successful exit */ return (cairo_surface_t *)local_os2_surface; error_exit: - /* This point will only be reached if an error occured */ + /* This point will only be reached if an error occurred */ if (local_os2_surface) { if (local_os2_surface->pixels) @@ -1438,37 +1388,29 @@ _cairo_os2_surface_mark_dirty_rectangle (void *surface, static const cairo_surface_backend_t cairo_os2_surface_backend = { CAIRO_SURFACE_TYPE_OS2, - NULL, /* create_similar */ _cairo_os2_surface_finish, + _cairo_default_context_create, + + NULL, /* create_similar */ + NULL, /* create_similar_image */ + _cairo_os2_surface_map_to_image, + _cairo_os2_surface_unmap_image, + + _cairo_surface_default_source, _cairo_os2_surface_acquire_source_image, _cairo_os2_surface_release_source_image, - _cairo_os2_surface_acquire_dest_image, - _cairo_os2_surface_release_dest_image, - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ + NULL, /* snapshot */ + _cairo_os2_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ _cairo_os2_surface_mark_dirty_rectangle, - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - NULL, /* has_show_text_glyphs */ - NULL /* show_text_glyphs */ + + _cairo_surface_fallback_paint, + _cairo_surface_fallback_mask, + _cairo_surface_fallback_fill, + _cairo_surface_fallback_stroke, + NULL, /* fill/stroke */ + _cairo_surface_fallback_glyphs, }; diff --git a/gfx/cairo/cairo/src/cairo-os2.h b/gfx/cairo/cairo/src/cairo-os2.h index d1babb1fcbfe..d23f2dec42ad 100644 --- a/gfx/cairo/cairo/src/cairo-os2.h +++ b/gfx/cairo/cairo/src/cairo-os2.h @@ -1,4 +1,4 @@ -/* vim: set sw=2 sts=2 et cin: */ +/* vim: set sw=4 sts=4 et cin: */ /* cairo - a vector graphics library with display and print output * * Copyright (c) 2005-2006 netlabs.org diff --git a/gfx/cairo/cairo/src/cairo-output-stream-private.h b/gfx/cairo/cairo/src/cairo-output-stream-private.h index edaabbe78e51..2542646b8608 100644 --- a/gfx/cairo/cairo/src/cairo-output-stream-private.h +++ b/gfx/cairo/cairo/src/cairo-output-stream-private.h @@ -135,6 +135,11 @@ _cairo_output_stream_printf (cairo_output_stream_t *stream, const char *fmt, ...) CAIRO_PRINTF_FORMAT (2, 3); +/* Print matrix element values with rounding of insignificant digits. */ +cairo_private void +_cairo_output_stream_print_matrix (cairo_output_stream_t *stream, + const cairo_matrix_t *matrix); + cairo_private long _cairo_output_stream_get_position (cairo_output_stream_t *stream); diff --git a/gfx/cairo/cairo/src/cairo-output-stream.c b/gfx/cairo/cairo/src/cairo-output-stream.c index 1aabe821afa4..935fa44c332d 100644 --- a/gfx/cairo/cairo/src/cairo-output-stream.c +++ b/gfx/cairo/cairo/src/cairo-output-stream.c @@ -33,15 +33,16 @@ * Kristian Høgsberg */ -#define _BSD_SOURCE /* for snprintf() */ +#define _DEFAULT_SOURCE /* for snprintf() */ #include "cairoint.h" #include "cairo-output-stream-private.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-compiler-private.h" #include -#include #include /* Numbers printed with %f are printed with this number of significant @@ -146,7 +147,7 @@ _cairo_output_stream_create (cairo_write_func_t write_func, { cairo_output_stream_with_closure_t *stream; - stream = malloc (sizeof (cairo_output_stream_with_closure_t)); + stream = _cairo_malloc (sizeof (cairo_output_stream_with_closure_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; @@ -172,7 +173,7 @@ _cairo_output_stream_create_in_error (cairo_status_t status) if (status == CAIRO_STATUS_WRITE_ERROR) return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; - stream = malloc (sizeof (cairo_output_stream_t)); + stream = _cairo_malloc (sizeof (cairo_output_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; @@ -292,7 +293,7 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, /* Format a double in a locale independent way and trim trailing * zeros. Based on code from Alex Larson . - * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html + * https://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html * * The code in the patch is copyright Red Hat, Inc under the LGPL, but * has been relicensed under the LGPL/MPL dual license for inclusion @@ -301,7 +302,6 @@ _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, static void _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision) { - struct lconv *locale_data; const char *decimal_point; int decimal_point_len; char *p; @@ -312,14 +312,8 @@ _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precisi if (d == 0.0) d = 0.0; -#ifdef HAVE_LOCALECONV - locale_data = localeconv (); - decimal_point = locale_data->decimal_point; + decimal_point = _cairo_get_locale_decimal_point (); decimal_point_len = strlen (decimal_point); -#else - decimal_point = "."; - decimal_point_len = 1; -#endif assert (decimal_point_len != 0); @@ -496,9 +490,13 @@ _cairo_output_stream_vprintf (cairo_output_stream_t *stream, single_fmt, va_arg (ap, long int)); } break; - case 's': - snprintf (buffer, sizeof buffer, - single_fmt, va_arg (ap, const char *)); + case 's': { + /* Write out strings as they may be larger than the buffer. */ + const char *s = va_arg (ap, const char *); + int len = strlen(s); + _cairo_output_stream_write (stream, s, len); + buffer[0] = 0; + } break; case 'f': _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE); @@ -533,6 +531,45 @@ _cairo_output_stream_printf (cairo_output_stream_t *stream, va_end (ap); } +/* Matrix elements that are smaller than the value of the largest element * MATRIX_ROUNDING_TOLERANCE + * are rounded down to zero. */ +#define MATRIX_ROUNDING_TOLERANCE 1e-12 + +void +_cairo_output_stream_print_matrix (cairo_output_stream_t *stream, + const cairo_matrix_t *matrix) +{ + cairo_matrix_t m; + double s, e; + + m = *matrix; + s = fabs (m.xx); + if (fabs (m.xy) > s) + s = fabs (m.xy); + if (fabs (m.yx) > s) + s = fabs (m.yx); + if (fabs (m.yy) > s) + s = fabs (m.yy); + + e = s * MATRIX_ROUNDING_TOLERANCE; + if (fabs(m.xx) < e) + m.xx = 0; + if (fabs(m.xy) < e) + m.xy = 0; + if (fabs(m.yx) < e) + m.yx = 0; + if (fabs(m.yy) < e) + m.yy = 0; + if (fabs(m.x0) < e) + m.x0 = 0; + if (fabs(m.y0) < e) + m.y0 = 0; + + _cairo_output_stream_printf (stream, + "%f %f %f %f %f %f", + m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); +} + long _cairo_output_stream_get_position (cairo_output_stream_t *stream) { @@ -602,7 +639,7 @@ _cairo_output_stream_create_for_file (FILE *file) return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; } - stream = malloc (sizeof *stream); + stream = _cairo_malloc (sizeof *stream); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; @@ -620,11 +657,16 @@ _cairo_output_stream_create_for_filename (const char *filename) { stdio_stream_t *stream; FILE *file; + cairo_status_t status; if (filename == NULL) return _cairo_null_stream_create (); - file = fopen (filename, "wb"); + status = _cairo_fopen (filename, "wb", &file); + + if (status != CAIRO_STATUS_SUCCESS) + return _cairo_output_stream_create_in_error (status); + if (file == NULL) { switch (errno) { case ENOMEM: @@ -636,7 +678,7 @@ _cairo_output_stream_create_for_filename (const char *filename) } } - stream = malloc (sizeof *stream); + stream = _cairo_malloc (sizeof *stream); if (unlikely (stream == NULL)) { fclose (file); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); @@ -680,7 +722,7 @@ _cairo_memory_stream_create (void) { memory_stream_t *stream; - stream = malloc (sizeof *stream); + stream = _cairo_malloc (sizeof *stream); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; @@ -707,7 +749,7 @@ _cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, stream = (memory_stream_t *) abstract_stream; *length_out = _cairo_array_num_elements (&stream->array); - *data_out = malloc (*length_out); + *data_out = _cairo_malloc (*length_out); if (unlikely (*data_out == NULL)) { status = _cairo_output_stream_destroy (abstract_stream); assert (status == CAIRO_STATUS_SUCCESS); @@ -757,7 +799,7 @@ _cairo_null_stream_create (void) { cairo_output_stream_t *stream; - stream = malloc (sizeof *stream); + stream = _cairo_malloc (sizeof *stream); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; diff --git a/gfx/cairo/cairo/src/cairo-paginated-private.h b/gfx/cairo/cairo/src/cairo-paginated-private.h index 42badbfdfe1e..89f1c99a253b 100644 --- a/gfx/cairo/cairo/src/cairo-paginated-private.h +++ b/gfx/cairo/cairo/src/cairo-paginated-private.h @@ -56,7 +56,7 @@ struct _cairo_paginated_surface_backend { * CAIRO_PAGINATED_MODE_RENDER. See more details in the * documentation for _cairo_paginated_surface_create below. */ - void + cairo_warn cairo_int_status_t (*set_paginated_mode) (void *surface, cairo_paginated_mode_t mode); @@ -77,7 +77,23 @@ struct _cairo_paginated_surface_backend { cairo_bool_t fallbacks_required); cairo_bool_t - (*supports_fine_grained_fallbacks) (void *surface); + (*supports_fine_grained_fallbacks) (void *surface); + + /* Optional. Indicates whether the page requires a thumbnail image to be + * supplied. If a thumbnail is required, set width, height to size required + * and return TRUE. + */ + cairo_bool_t + (*requires_thumbnail_image) (void *surface, + int *width, + int *height); + + /* If thumbbail image requested, this function will be called before + * _show_page(). + */ + cairo_warn cairo_int_status_t + (*set_thumbnail_image) (void *surface, + cairo_image_surface_t *image); }; /* A #cairo_paginated_surface_t provides a very convenient wrapper that @@ -154,6 +170,9 @@ _cairo_paginated_surface_create (cairo_surface_t *target, cairo_private cairo_surface_t * _cairo_paginated_surface_get_target (cairo_surface_t *surface); +cairo_private cairo_surface_t * +_cairo_paginated_surface_get_recording (cairo_surface_t *surface); + cairo_private cairo_bool_t _cairo_surface_is_paginated (cairo_surface_t *surface); diff --git a/gfx/cairo/cairo/src/cairo-paginated-surface.c b/gfx/cairo/cairo/src/cairo-paginated-surface.c index af4790e7ea99..6b35c1f92d19 100644 --- a/gfx/cairo/cairo/src/cairo-paginated-surface.c +++ b/gfx/cairo/cairo/src/cairo-paginated-surface.c @@ -49,6 +49,8 @@ #include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-subsurface-inline.h" static const cairo_surface_backend_t cairo_paginated_surface_backend; @@ -96,7 +98,7 @@ _cairo_paginated_surface_create (cairo_surface_t *target, cairo_paginated_surface_t *surface; cairo_status_t status; - surface = malloc (sizeof (cairo_paginated_surface_t)); + surface = _cairo_malloc (sizeof (cairo_paginated_surface_t)); if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto FAIL; @@ -105,7 +107,8 @@ _cairo_paginated_surface_create (cairo_surface_t *target, _cairo_surface_init (&surface->base, &cairo_paginated_surface_backend, NULL, /* device */ - content); + content, + target->is_vector); /* Override surface->base.type with target's type so we don't leak * evidence of the paginated wrapper out to the user. */ @@ -147,10 +150,48 @@ _cairo_paginated_surface_get_target (cairo_surface_t *surface) assert (_cairo_surface_is_paginated (surface)); paginated_surface = (cairo_paginated_surface_t *) surface; - return paginated_surface->target; } +cairo_surface_t * +_cairo_paginated_surface_get_recording (cairo_surface_t *surface) +{ + cairo_paginated_surface_t *paginated_surface; + + assert (_cairo_surface_is_paginated (surface)); + + paginated_surface = (cairo_paginated_surface_t *) surface; + return paginated_surface->recording_surface; +} + +cairo_status_t +_cairo_paginated_surface_set_size (cairo_surface_t *surface, + int width, + int height) +{ + cairo_paginated_surface_t *paginated_surface; + cairo_status_t status; + cairo_rectangle_t recording_extents; + + assert (_cairo_surface_is_paginated (surface)); + + paginated_surface = (cairo_paginated_surface_t *) surface; + + recording_extents.x = 0; + recording_extents.y = 0; + recording_extents.width = width; + recording_extents.height = height; + + cairo_surface_destroy (paginated_surface->recording_surface); + paginated_surface->recording_surface = cairo_recording_surface_create (paginated_surface->content, + &recording_extents); + status = paginated_surface->recording_surface->status; + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_paginated_surface_finish (void *abstract_surface) { @@ -202,6 +243,14 @@ _cairo_paginated_surface_create_image_surface (void *abstract_surface, return image; } +static cairo_surface_t * +_cairo_paginated_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_paginated_surface_t *surface = abstract_surface; + return _cairo_surface_get_source (surface->target, extents); +} + static cairo_status_t _cairo_paginated_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, @@ -241,6 +290,63 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } +static cairo_int_status_t +_paint_thumbnail_image (cairo_paginated_surface_t *surface, + int width, + int height) +{ + cairo_surface_pattern_t pattern; + cairo_rectangle_int_t extents; + double x_scale; + double y_scale; + cairo_surface_t *image = NULL; + cairo_surface_t *opaque = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + _cairo_surface_get_extents (surface->target, &extents); + x_scale = (double)width / extents.width; + y_scale = (double)height / extents.height; + + image = _cairo_paginated_surface_create_image_surface (surface, width, height); + cairo_surface_set_device_scale (image, x_scale, y_scale); + cairo_surface_set_device_offset (image, -extents.x*x_scale, -extents.y*y_scale); + status = _cairo_recording_surface_replay (surface->recording_surface, image); + if (unlikely (status)) + goto cleanup; + + /* flatten transparency */ + + opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + if (unlikely (opaque->status)) { + status = opaque->status; + goto cleanup; + } + + status = _cairo_surface_paint (opaque, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + NULL); + if (unlikely (status)) + goto cleanup; + + _cairo_pattern_init_for_surface (&pattern, image); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto cleanup; + + status = surface->backend->set_thumbnail_image (surface->target, (cairo_image_surface_t *)opaque); + + cleanup: + if (image) + cairo_surface_destroy (image); + if (opaque) + cairo_surface_destroy (opaque); + + return status; +} + static cairo_int_status_t _paint_fallback_image (cairo_paginated_surface_t *surface, cairo_rectangle_int_t *rect) @@ -251,7 +357,7 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, cairo_status_t status; cairo_surface_t *image; cairo_surface_pattern_t pattern; - cairo_clip_t clip; + cairo_clip_t *clip; x = rect->x; y = rect->y; @@ -260,7 +366,7 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, image = _cairo_paginated_surface_create_image_surface (surface, ceil (width * x_scale), ceil (height * y_scale)); - _cairo_surface_set_device_scale (image, x_scale, y_scale); + cairo_surface_set_device_scale (image, x_scale, y_scale); /* set_device_offset just sets the x0/y0 components of the matrix; * so we have to do the scaling manually. */ cairo_surface_set_device_offset (image, -x*x_scale, -y*y_scale); @@ -276,15 +382,11 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, * filtering (if possible) to avoid introducing potential artifacts. */ pattern.base.filter = CAIRO_FILTER_NEAREST; - _cairo_clip_init (&clip); - status = _cairo_clip_rectangle (&clip, rect); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_surface_paint (surface->target, - CAIRO_OPERATOR_SOURCE, - &pattern.base, &clip); - } - - _cairo_clip_fini (&clip); + clip = _cairo_clip_intersect_rectangle (NULL, rect); + status = _cairo_surface_paint (surface->target, + CAIRO_OPERATOR_SOURCE, + &pattern.base, clip); + _cairo_clip_destroy (clip); _cairo_pattern_fini (&pattern.base); CLEANUP_IMAGE: @@ -297,7 +399,7 @@ static cairo_int_status_t _paint_page (cairo_paginated_surface_t *surface) { cairo_surface_t *analysis; - cairo_status_t status; + cairo_int_status_t status; cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; if (unlikely (surface->target->status)) @@ -307,15 +409,17 @@ _paint_page (cairo_paginated_surface_t *surface) if (unlikely (analysis->status)) return _cairo_surface_set_error (surface->target, analysis->status); - surface->backend->set_paginated_mode (surface->target, + status = surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE); - status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, - analysis); - if (status || analysis->status) { - if (status == CAIRO_STATUS_SUCCESS) - status = analysis->status; + if (unlikely (status)) goto FAIL; - } + + status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, + NULL, analysis, FALSE); + if (status) + goto FAIL; + + assert (analysis->status == CAIRO_STATUS_SUCCESS); if (surface->backend->set_bounding_box) { cairo_box_t bbox; @@ -357,8 +461,10 @@ _paint_page (cairo_paginated_surface_t *surface) } if (has_supported) { - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_RENDER); + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_RENDER); + if (unlikely (status)) + goto FAIL; status = _cairo_recording_surface_replay_region (surface->recording_surface, NULL, @@ -373,8 +479,10 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_rectangle_int_t extents; cairo_bool_t is_bounded; - surface->backend->set_paginated_mode (surface->target, - CAIRO_PAGINATED_MODE_FALLBACK); + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_FALLBACK); + if (unlikely (status)) + goto FAIL; is_bounded = _cairo_surface_get_extents (surface->target, &extents); if (! is_bounded) { @@ -391,8 +499,10 @@ _paint_page (cairo_paginated_surface_t *surface) cairo_region_t *region; int num_rects, i; - surface->backend->set_paginated_mode (surface->target, + status = surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); + if (unlikely (status)) + goto FAIL; region = _cairo_analysis_surface_get_unsupported (analysis); @@ -407,6 +517,13 @@ _paint_page (cairo_paginated_surface_t *surface) } } + if (surface->backend->requires_thumbnail_image) { + int width, height; + + if (surface->backend->requires_thumbnail_image (surface->target, &width, &height)) + _paint_thumbnail_image (surface, width, height); + } + FAIL: cairo_surface_destroy (analysis); @@ -514,7 +631,7 @@ static cairo_int_status_t _cairo_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -526,7 +643,7 @@ _cairo_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -537,13 +654,13 @@ static cairo_int_status_t _cairo_paginated_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -558,11 +675,11 @@ static cairo_int_status_t _cairo_paginated_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -592,7 +709,7 @@ _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_paginated_surface_t *surface = abstract_surface; @@ -605,47 +722,90 @@ _cairo_paginated_surface_show_text_glyphs (void *abstract_surface, clip); } +static const char ** +_cairo_paginated_surface_get_supported_mime_types (void *abstract_surface) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + if (surface->target->backend->get_supported_mime_types) + return surface->target->backend->get_supported_mime_types (surface->target); + + return NULL; +} + +static cairo_int_status_t +_cairo_paginated_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_tag (surface->recording_surface, + begin, tag_name, attributes, + source, style, + ctm, ctm_inverse, + clip); +} + static cairo_surface_t * _cairo_paginated_surface_snapshot (void *abstract_other) { cairo_paginated_surface_t *other = abstract_other; - return _cairo_surface_snapshot (other->recording_surface); + return other->recording_surface->backend->snapshot (other->recording_surface); +} + +static cairo_t * +_cairo_paginated_context_create (void *target) +{ + cairo_paginated_surface_t *surface = target; + + if (_cairo_surface_is_subsurface (&surface->base)) + surface = (cairo_paginated_surface_t *) + _cairo_surface_subsurface_get_target (&surface->base); + + return surface->recording_surface->backend->create_context (target); } static const cairo_surface_backend_t cairo_paginated_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, - _cairo_paginated_surface_create_similar, _cairo_paginated_surface_finish, + + _cairo_paginated_context_create, + + _cairo_paginated_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_paginated_surface_source, _cairo_paginated_surface_acquire_source_image, _cairo_paginated_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + _cairo_paginated_surface_snapshot, + _cairo_paginated_surface_copy_page, _cairo_paginated_surface_show_page, + _cairo_paginated_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_paginated_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ + _cairo_paginated_surface_paint, _cairo_paginated_surface_mask, _cairo_paginated_surface_stroke, _cairo_paginated_surface_fill, - NULL, /* show_glyphs */ - _cairo_paginated_surface_snapshot, - NULL, /* is_similar */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* show_glyphs */ _cairo_paginated_surface_has_show_text_glyphs, - _cairo_paginated_surface_show_text_glyphs + _cairo_paginated_surface_show_text_glyphs, + _cairo_paginated_surface_get_supported_mime_types, + _cairo_paginated_surface_tag, }; diff --git a/gfx/cairo/cairo/src/cairo-path-bounds.c b/gfx/cairo/cairo/src/cairo-path-bounds.c index d719cfe91349..ac85f27bee38 100644 --- a/gfx/cairo/cairo/src/cairo-path-bounds.c +++ b/gfx/cairo/cairo/src/cairo-path-bounds.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 @@ -35,48 +36,16 @@ */ #include "cairoint.h" +#include "cairo-box-inline.h" +#include "cairo-error-private.h" #include "cairo-path-fixed-private.h" -typedef struct cairo_path_bounder { +typedef struct _cairo_path_bounder { cairo_point_t current_point; - cairo_bool_t has_initial_point; - cairo_bool_t has_point; - + cairo_bool_t has_extents; cairo_box_t extents; } cairo_path_bounder_t; -static void -_cairo_path_bounder_init (cairo_path_bounder_t *bounder) -{ - bounder->has_initial_point = FALSE; - bounder->has_point = FALSE; -} - -static void -_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, - const cairo_point_t *point) -{ - if (bounder->has_point) { - if (point->x < bounder->extents.p1.x) - bounder->extents.p1.x = point->x; - - if (point->y < bounder->extents.p1.y) - bounder->extents.p1.y = point->y; - - if (point->x > bounder->extents.p2.x) - bounder->extents.p2.x = point->x; - - if (point->y > bounder->extents.p2.y) - bounder->extents.p2.y = point->y; - } else { - bounder->extents.p1.x = point->x; - bounder->extents.p1.y = point->y; - bounder->extents.p2.x = point->x; - bounder->extents.p2.y = point->y; - bounder->has_point = TRUE; - } -} - static cairo_status_t _cairo_path_bounder_move_to (void *closure, const cairo_point_t *point) @@ -84,7 +53,13 @@ _cairo_path_bounder_move_to (void *closure, cairo_path_bounder_t *bounder = closure; bounder->current_point = *point; - bounder->has_initial_point = TRUE; + + if (likely (bounder->has_extents)) { + _cairo_box_add_point (&bounder->extents, point); + } else { + bounder->has_extents = TRUE; + _cairo_box_set (&bounder->extents, point, point); + } return CAIRO_STATUS_SUCCESS; } @@ -95,13 +70,8 @@ _cairo_path_bounder_line_to (void *closure, { cairo_path_bounder_t *bounder = closure; - if (bounder->has_initial_point) { - _cairo_path_bounder_add_point (bounder, &bounder->current_point); - bounder->has_initial_point = FALSE; - } - - _cairo_path_bounder_add_point (bounder, point); bounder->current_point = *point; + _cairo_box_add_point (&bounder->extents, point); return CAIRO_STATUS_SUCCESS; } @@ -114,88 +84,54 @@ _cairo_path_bounder_curve_to (void *closure, { cairo_path_bounder_t *bounder = closure; - /* If the bbox of the control points is entirely inside, then we - * do not need to further evaluate the spline. - */ - if (! bounder->has_point || - b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x || - b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y || - c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x || - c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y || - d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x || - d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y) - { - return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder, - &bounder->current_point, b, c, d); - } - else - { - /* All control points are within the current extents. */ - bounder->current_point = *d; - return CAIRO_STATUS_SUCCESS; - } + _cairo_box_add_curve_to (&bounder->extents, + &bounder->current_point, + b, c, d); + bounder->current_point = *d; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_bounder_close_path (void *closure) { - cairo_path_bounder_t *bounder = closure; - - if (bounder->has_initial_point) { - _cairo_path_bounder_add_point (bounder, &bounder->current_point); - bounder->has_initial_point = FALSE; - } - return CAIRO_STATUS_SUCCESS; } -/* This computes the extents of all the points in the path, not those of - * the damage area (i.e it does not consider winding and it only inspects - * the control points of the curves, not the flattened path). - */ -void -_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, - cairo_rectangle_int_t *extents) -{ - if (path->extents.p1.x < path->extents.p2.x) { - _cairo_box_round_to_rectangle (&path->extents, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } -} - -/* A slightly better approximation than above - we actually decompose the - * Bezier, but we continue to ignore winding. - */ -void -_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, - cairo_rectangle_int_t *extents) +cairo_bool_t +_cairo_path_bounder_extents (const cairo_path_fixed_t *path, + cairo_box_t *extents) { cairo_path_bounder_t bounder; cairo_status_t status; - if (! path->has_curve_to) { - bounder.extents = path->extents; - bounder.has_point = path->extents.p1.x < path->extents.p2.x; - } else { - _cairo_path_bounder_init (&bounder); + bounder.has_extents = FALSE; + status = _cairo_path_fixed_interpret (path, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (!status); - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - } + if (bounder.has_extents) + *extents = bounder.extents; - if (bounder.has_point) { - _cairo_box_round_to_rectangle (&bounder.extents, extents); - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } + return bounder.has_extents; +} + +void +_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents) +{ + _cairo_path_fixed_approximate_fill_extents (path, extents); +} + +void +_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents) +{ + _cairo_path_fixed_fill_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents); } void @@ -204,25 +140,9 @@ _cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, double tolerance, cairo_rectangle_int_t *extents) { - cairo_path_bounder_t bounder; - cairo_status_t status; - - if (! path->has_curve_to) { - bounder.extents = path->extents; - bounder.has_point = path->extents.p1.x < path->extents.p2.x; - } else { - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_close_path, - &bounder, tolerance); - assert (status == CAIRO_STATUS_SUCCESS); - } - - if (bounder.has_point) { - _cairo_box_round_to_rectangle (&bounder.extents, extents); + if (path->extents.p1.x < path->extents.p2.x && + path->extents.p1.y < path->extents.p2.y) { + _cairo_box_round_to_rectangle (&path->extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; @@ -234,67 +154,33 @@ void _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, + cairo_bool_t is_vector, cairo_rectangle_int_t *extents) { - cairo_path_bounder_t bounder; - cairo_status_t status; + if (path->has_extents) { + cairo_box_t box_extents; + double dx, dy; - if (! path->has_curve_to) { - bounder.extents = path->extents; + _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); + if (is_vector) + { + /* When calculating extents for vector surfaces, ensure lines thinner + * than the fixed point resolution are not optimized away. */ + double min = _cairo_fixed_to_double (CAIRO_FIXED_EPSILON*2); + if (dx < min) + dx = min; - /* include trailing move-to for degenerate segments */ - if (path->has_last_move_point) { - const cairo_point_t *point = &path->last_move_point; - - if (point->x < bounder.extents.p1.x) - bounder.extents.p1.x = point->x; - if (point->y < bounder.extents.p1.y) - bounder.extents.p1.y = point->y; - - if (point->x > bounder.extents.p2.x) - bounder.extents.p2.x = point->x; - if (point->y > bounder.extents.p2.y) - bounder.extents.p2.y = point->y; + if (dy < min) + dy = min; } - bounder.has_point = bounder.extents.p1.x <= bounder.extents.p2.x; - bounder.has_initial_point = FALSE; - } else { - _cairo_path_bounder_init (&bounder); + box_extents = path->extents; + box_extents.p1.x -= _cairo_fixed_from_double (dx); + box_extents.p1.y -= _cairo_fixed_from_double (dy); + box_extents.p2.x += _cairo_fixed_from_double (dx); + box_extents.p2.y += _cairo_fixed_from_double (dy); - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - } - - if (bounder.has_point) { - double dx, dy; - - _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); - - bounder.extents.p1.x -= _cairo_fixed_from_double (dx); - bounder.extents.p2.x += _cairo_fixed_from_double (dx); - bounder.extents.p1.y -= _cairo_fixed_from_double (dy); - bounder.extents.p2.y += _cairo_fixed_from_double (dy); - - _cairo_box_round_to_rectangle (&bounder.extents, extents); - } else if (bounder.has_initial_point) { - double dx, dy; - - /* accommodate capping of degenerate paths */ - - _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); - - bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx); - bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx); - bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy); - bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy); - - _cairo_box_round_to_rectangle (&bounder.extents, extents); + _cairo_box_round_to_rectangle (&box_extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; @@ -309,23 +195,28 @@ _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, double tolerance, cairo_rectangle_int_t *extents) { - cairo_traps_t traps; - cairo_box_t bbox; + cairo_polygon_t polygon; cairo_status_t status; + cairo_stroke_style_t style; - _cairo_traps_init (&traps); + /* When calculating extents for vector surfaces, ensure lines thinner + * than one point are not optimized away. */ + double min_line_width = _cairo_matrix_transformed_circle_major_axis (ctm_inverse, 1.0); + if (stroke_style->line_width < min_line_width) + { + style = *stroke_style; + style.line_width = min_line_width; + stroke_style = &style; + } - status = _cairo_path_fixed_stroke_to_traps (path, - stroke_style, - ctm, - ctm_inverse, - tolerance, - &traps); - - _cairo_traps_extents (&traps, &bbox); - _cairo_traps_fini (&traps); - - _cairo_box_round_to_rectangle (&bbox, extents); + _cairo_polygon_init (&polygon, NULL, 0); + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); + _cairo_box_round_to_rectangle (&polygon.extents, extents); + _cairo_polygon_fini (&polygon); return status; } @@ -334,24 +225,6 @@ cairo_bool_t _cairo_path_fixed_extents (const cairo_path_fixed_t *path, cairo_box_t *box) { - cairo_path_bounder_t bounder; - cairo_status_t status; - - if (! path->has_curve_to) { - *box = path->extents; - return path->extents.p1.x <= path->extents.p2.x; - } - - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - - *box = bounder.extents; - return bounder.has_point; + *box = path->extents; + return path->has_extents; } diff --git a/gfx/cairo/cairo/src/cairo-path-fill.c b/gfx/cairo/cairo/src/cairo-path-fill.c index 24aaa3969c56..4000c9c586ee 100644 --- a/gfx/cairo/cairo/src/cairo-path-fill.c +++ b/gfx/cairo/cairo/src/cairo-path-fill.c @@ -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 © 2002 University of Southern California @@ -39,24 +40,42 @@ #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-region-private.h" +#include "cairo-traps-private.h" typedef struct cairo_filler { - double tolerance; cairo_polygon_t *polygon; + double tolerance; + + cairo_box_t limit; + cairo_bool_t has_limits; + + cairo_point_t current_point; + cairo_point_t last_move_to; } cairo_filler_t; -static void -_cairo_filler_init (cairo_filler_t *filler, - double tolerance, - cairo_polygon_t *polygon) +static cairo_status_t +_cairo_filler_line_to (void *closure, + const cairo_point_t *point) { - filler->tolerance = tolerance; - filler->polygon = polygon; + cairo_filler_t *filler = closure; + cairo_status_t status; + + status = _cairo_polygon_add_external_edge (filler->polygon, + &filler->current_point, + point); + + filler->current_point = *point; + + return status; } -static void -_cairo_filler_fini (cairo_filler_t *filler) +static cairo_status_t +_cairo_filler_close (void *closure) { + cairo_filler_t *filler = closure; + + /* close the subpath */ + return _cairo_filler_line_to (closure, &filler->last_move_to); } static cairo_status_t @@ -64,46 +83,45 @@ _cairo_filler_move_to (void *closure, const cairo_point_t *point) { cairo_filler_t *filler = closure; - cairo_polygon_t *polygon = filler->polygon; + cairo_status_t status; - return _cairo_polygon_close (polygon) || - _cairo_polygon_move_to (polygon, point); + /* close current subpath */ + status = _cairo_filler_close (closure); + if (unlikely (status)) + return status; + + /* make sure that the closure represents a degenerate path */ + filler->current_point = *point; + filler->last_move_to = *point; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_filler_line_to (void *closure, - const cairo_point_t *point) -{ - cairo_filler_t *filler = closure; - return _cairo_polygon_line_to (filler->polygon, point); -} - -static cairo_status_t -_cairo_filler_curve_to (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) +_cairo_filler_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) { cairo_filler_t *filler = closure; cairo_spline_t spline; + if (filler->has_limits) { + if (! _cairo_spline_intersects (&filler->current_point, p1, p2, p3, + &filler->limit)) + return _cairo_filler_line_to (filler, p3); + } + if (! _cairo_spline_init (&spline, - _cairo_filler_line_to, filler, - &filler->polygon->current_point, b, c, d)) + (cairo_spline_add_point_func_t)_cairo_filler_line_to, filler, + &filler->current_point, p1, p2, p3)) { - return _cairo_filler_line_to (closure, d); + return _cairo_filler_line_to (closure, p3); } return _cairo_spline_decompose (&spline, filler->tolerance); } -static cairo_status_t -_cairo_filler_close_path (void *closure) -{ - cairo_filler_t *filler = closure; - return _cairo_polygon_close (filler->polygon); -} - cairo_status_t _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, double tolerance, @@ -112,24 +130,119 @@ _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, cairo_filler_t filler; cairo_status_t status; - _cairo_filler_init (&filler, tolerance, polygon); + filler.polygon = polygon; + filler.tolerance = tolerance; + + filler.has_limits = FALSE; + if (polygon->num_limits) { + filler.has_limits = TRUE; + filler.limit = polygon->limit; + } + + /* make sure that the closure represents a degenerate path */ + filler.current_point.x = 0; + filler.current_point.y = 0; + filler.last_move_to = filler.current_point; status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_filler_move_to, _cairo_filler_line_to, _cairo_filler_curve_to, - _cairo_filler_close_path, + _cairo_filler_close, &filler); if (unlikely (status)) return status; - status = _cairo_polygon_close (polygon); - _cairo_filler_fini (&filler); + return _cairo_filler_close (&filler); +} + +typedef struct cairo_filler_rectilinear_aligned { + cairo_polygon_t *polygon; + + cairo_point_t current_point; + cairo_point_t last_move_to; +} cairo_filler_ra_t; + +static cairo_status_t +_cairo_filler_ra_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_filler_ra_t *filler = closure; + cairo_status_t status; + cairo_point_t p; + + p.x = _cairo_fixed_round_down (point->x); + p.y = _cairo_fixed_round_down (point->y); + + status = _cairo_polygon_add_external_edge (filler->polygon, + &filler->current_point, + &p); + + filler->current_point = p; return status; } +static cairo_status_t +_cairo_filler_ra_close (void *closure) +{ + cairo_filler_ra_t *filler = closure; + return _cairo_filler_ra_line_to (closure, &filler->last_move_to); +} + +static cairo_status_t +_cairo_filler_ra_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_filler_ra_t *filler = closure; + cairo_status_t status; + cairo_point_t p; + + /* close current subpath */ + status = _cairo_filler_ra_close (closure); + if (unlikely (status)) + return status; + + p.x = _cairo_fixed_round_down (point->x); + p.y = _cairo_fixed_round_down (point->y); + + /* make sure that the closure represents a degenerate path */ + filler->current_point = p; + filler->last_move_to = p; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path, + cairo_antialias_t antialias, + cairo_polygon_t *polygon) +{ + cairo_filler_ra_t filler; + cairo_status_t status; + + if (antialias != CAIRO_ANTIALIAS_NONE) + return _cairo_path_fixed_fill_to_polygon (path, 0., polygon); + + filler.polygon = polygon; + + /* make sure that the closure represents a degenerate path */ + filler.current_point.x = 0; + filler.current_point.y = 0; + filler.last_move_to = filler.current_point; + + status = _cairo_path_fixed_interpret_flat (path, + _cairo_filler_ra_move_to, + _cairo_filler_ra_line_to, + _cairo_filler_ra_close, + &filler, + 0.); + if (unlikely (status)) + return status; + + return _cairo_filler_ra_close (&filler); +} + cairo_status_t _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, @@ -139,275 +252,36 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, cairo_polygon_t polygon; cairo_status_t status; - if (path->is_empty_fill) + if (_cairo_path_fixed_fill_is_empty (path)) return CAIRO_STATUS_SUCCESS; - _cairo_polygon_init (&polygon); - if (traps->num_limits) - _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); - - status = _cairo_path_fixed_fill_to_polygon (path, - tolerance, - &polygon); + _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); if (unlikely (status || polygon.num_edges == 0)) goto CLEANUP; - if (path->is_rectilinear) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps, - &polygon, - fill_rule); - } else { - status = _cairo_bentley_ottmann_tessellate_polygon (traps, - &polygon, - fill_rule); - } + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, fill_rule); CLEANUP: _cairo_polygon_fini (&polygon); return status; } -static cairo_region_t * -_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_rectangle_int_t *extents) -{ - cairo_box_t box; - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_status_t status; - cairo_region_t *region; - - /* first try to bypass fill-to-polygon */ - _cairo_traps_init (&traps); - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (_cairo_status_is_error (status)) - goto CLEANUP_TRAPS; - - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_traps_extract_region (&traps, ®ion); - goto CLEANUP_TRAPS; - } - - /* path is not rectangular, try extracting clipped rectilinear edges */ - _cairo_polygon_init (&polygon); - if (extents != NULL) { - _cairo_box_from_rectangle (&box, extents); - _cairo_polygon_limit (&polygon, &box, 1); - } - - /* tolerance will be ignored as the path is rectilinear */ - status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); - if (unlikely (status)) - goto CLEANUP_POLYGON; - - if (polygon.num_edges == 0) { - region = cairo_region_create (); - } else { - status = - _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) - status = _cairo_traps_extract_region (&traps, ®ion); - } - - CLEANUP_POLYGON: - _cairo_polygon_fini (&polygon); - - CLEANUP_TRAPS: - _cairo_traps_fini (&traps); - - if (unlikely (status)) - region = _cairo_region_create_in_error (status); - - return region; -} - -/* This special-case filler supports only a path that describes a - * device-axis aligned rectangle. It exists to avoid the overhead of - * the general tessellator when drawing very common rectangles. - * - * If the path described anything but a device-axis aligned rectangle, - * this function will abort. - */ -cairo_region_t * -_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - const cairo_rectangle_int_t *extents) -{ - cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_box_t box; - cairo_region_t *region = NULL; - - assert (path->maybe_fill_region); - assert (! path->is_empty_fill); - - if (_cairo_path_fixed_is_box (path, &box)) { - rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x); - rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y); - rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) - - rectangle_stack[0].x; - rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) - - rectangle_stack[0].y; - if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents)) - region = cairo_region_create (); - else - region = cairo_region_create_rectangle (&rectangle_stack[0]); - } else if (fill_rule == CAIRO_FILL_RULE_WINDING) { - cairo_rectangle_int_t *rects = rectangle_stack; - cairo_path_fixed_iter_t iter; - int last_cw = -1; - int size = ARRAY_LENGTH (rectangle_stack); - int count = 0; - - /* Support a series of rectangles as can be expected to describe a - * GdkRegion clip region during exposes. - */ - _cairo_path_fixed_iter_init (&iter, path); - while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { - int cw = 0; - - if (box.p1.x > box.p2.x) { - cairo_fixed_t t; - - t = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = t; - - cw = ! cw; - } - - if (box.p1.y > box.p2.y) { - cairo_fixed_t t; - - t = box.p1.y; - box.p1.y = box.p2.y; - box.p2.y = t; - - cw = ! cw; - } - - if (last_cw < 0) - last_cw = cw; - else if (last_cw != cw) - goto TESSELLATE; - - if (count == size) { - cairo_rectangle_int_t *new_rects; - - size *= 4; - if (rects == rectangle_stack) { - new_rects = _cairo_malloc_ab (size, - sizeof (cairo_rectangle_int_t)); - if (unlikely (new_rects == NULL)) { - /* XXX _cairo_region_nil */ - break; - } - memcpy (new_rects, rects, sizeof (rectangle_stack)); - } else { - new_rects = _cairo_realloc_ab (rects, size, - sizeof (cairo_rectangle_int_t)); - if (unlikely (new_rects == NULL)) { - /* XXX _cairo_region_nil */ - break; - } - } - rects = new_rects; - } - - rects[count].x = _cairo_fixed_integer_part (box.p1.x); - rects[count].y = _cairo_fixed_integer_part (box.p1.y); - rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x; - rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y; - if (_cairo_rectangle_intersect (&rects[count], extents)) - count++; - } - - if (_cairo_path_fixed_iter_at_end (&iter)) - region = cairo_region_create_rectangles (rects, count); - -TESSELLATE: - if (rects != rectangle_stack) - free (rects); - } - - if (region == NULL) { - /* Hmm, complex polygon */ - region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path, - fill_rule, - extents); - - - } - - return region; -} - -cairo_int_status_t -_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps) -{ - cairo_box_t box; - cairo_status_t status; - - traps->is_rectilinear = TRUE; - traps->is_rectangular = TRUE; - - if (_cairo_path_fixed_is_box (path, &box)) { - return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); - } else { - cairo_path_fixed_iter_t iter; - - _cairo_path_fixed_iter_init (&iter, path); - while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { - if (box.p1.y > box.p2.y) { - cairo_fixed_t t; - - t = box.p1.y; - box.p1.y = box.p2.y; - box.p2.y = t; - - t = box.p1.x; - box.p1.x = box.p2.x; - box.p2.x = t; - } - - status = _cairo_traps_tessellate_rectangle (traps, - &box.p1, &box.p2); - if (unlikely (status)) { - _cairo_traps_clear (traps); - return status; - } - } - - if (_cairo_path_fixed_iter_at_end (&iter)) - return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule); - - _cairo_traps_clear (traps); - return CAIRO_INT_STATUS_UNSUPPORTED; - } -} - static cairo_status_t _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_polygon_t polygon; cairo_status_t status; - _cairo_polygon_init (&polygon); - if (boxes->num_limits) { - _cairo_polygon_limit (&polygon, boxes->limits, boxes->num_limits); - boxes->num_limits = 0; - } + _cairo_polygon_init (&polygon, boxes->limits, boxes->num_limits); + boxes->num_limits = 0; /* tolerance will be ignored as the path is rectilinear */ - status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon); + status = _cairo_path_fixed_fill_rectilinear_to_polygon (path, antialias, &polygon); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon, @@ -423,6 +297,7 @@ _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t cairo_status_t _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_path_fixed_iter_t iter; @@ -430,7 +305,7 @@ _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, cairo_box_t box; if (_cairo_path_fixed_is_box (path, &box)) - return _cairo_boxes_add (boxes, &box); + return _cairo_boxes_add (boxes, antialias, &box); _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { @@ -449,7 +324,7 @@ _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, box.p2.x = t; } - status = _cairo_boxes_add (boxes, &box); + status = _cairo_boxes_add (boxes, antialias, &box); if (unlikely (status)) return status; } @@ -461,5 +336,6 @@ _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, _cairo_boxes_clear (boxes); return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path, fill_rule, + antialias, boxes); } diff --git a/gfx/cairo/cairo/src/cairo-path-fixed-private.h b/gfx/cairo/cairo/src/cairo-path-fixed-private.h index 42e64eda35c4..cf7cd0836fb1 100644 --- a/gfx/cairo/cairo/src/cairo-path-fixed-private.h +++ b/gfx/cairo/cairo/src/cairo-path-fixed-private.h @@ -59,6 +59,20 @@ typedef char cairo_path_op_t; #define CAIRO_PATH_BUF_SIZE ((512 - sizeof (cairo_path_buf_t)) \ / (2 * sizeof (cairo_point_t) + sizeof (cairo_path_op_t))) +#define cairo_path_head(path__) (&(path__)->buf.base) +#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__)) + +#define cairo_path_buf_next(pos__) \ + cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link) +#define cairo_path_buf_prev(pos__) \ + cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link) + +#define cairo_path_foreach_buf_start(pos__, path__) \ + pos__ = cairo_path_head (path__); do +#define cairo_path_foreach_buf_end(pos__, path__) \ + while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__)) + + typedef struct _cairo_path_buf { cairo_list_t link; unsigned int num_ops; @@ -77,15 +91,24 @@ typedef struct _cairo_path_buf_fixed { cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE]; } cairo_path_buf_fixed_t; +/* + NOTES: + has_curve_to => !stroke_is_rectilinear + fill_is_rectilinear => stroke_is_rectilinear + fill_is_empty => fill_is_rectilinear + fill_maybe_region => fill_is_rectilinear +*/ struct _cairo_path_fixed { cairo_point_t last_move_point; cairo_point_t current_point; unsigned int has_current_point : 1; - unsigned int has_last_move_point : 1; + unsigned int needs_move_to : 1; + unsigned int has_extents : 1; unsigned int has_curve_to : 1; - unsigned int is_rectilinear : 1; - unsigned int maybe_fill_region : 1; - unsigned int is_empty_fill : 1; + unsigned int stroke_is_rectilinear : 1; + unsigned int fill_is_rectilinear : 1; + unsigned int fill_maybe_region : 1; + unsigned int fill_is_empty : 1; cairo_box_t extents; @@ -100,7 +123,6 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path, cairo_private cairo_status_t _cairo_path_fixed_append (cairo_path_fixed_t *path, const cairo_path_fixed_t *other, - cairo_direction_t dir, cairo_fixed_t tx, cairo_fixed_t ty); @@ -135,16 +157,16 @@ _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); static inline cairo_bool_t _cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path) { - return path->is_empty_fill; + return path->fill_is_empty; } static inline cairo_bool_t -_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path) +_cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path) { - if (! path->is_rectilinear) + if (! path->fill_is_rectilinear) return 0; - if (! path->has_current_point) + if (! path->has_current_point || path->needs_move_to) return 1; /* check whether the implicit close preserves the rectilinear property */ @@ -153,13 +175,32 @@ _cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path) } static inline cairo_bool_t -_cairo_path_fixed_maybe_fill_region (const cairo_path_fixed_t *path) +_cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path) { -#if WATCH_PATH - fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n", - path->maybe_fill_region ? "true" : "false"); -#endif - return path->maybe_fill_region; + return path->stroke_is_rectilinear; } +static inline cairo_bool_t +_cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path) +{ + if (! path->fill_maybe_region) + return 0; + + if (! path->has_current_point || path->needs_move_to) + return 1; + + /* check whether the implicit close preserves the rectilinear property + * (the integer point property is automatically preserved) + */ + return path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; +} + +cairo_private cairo_bool_t +_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path, + cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path); + #endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-path-fixed.c b/gfx/cairo/cairo/src/cairo-path-fixed.c index eea8630bd445..d741823460b1 100644 --- a/gfx/cairo/cairo/src/cairo-path-fixed.c +++ b/gfx/cairo/cairo/src/cairo-path-fixed.c @@ -38,7 +38,9 @@ #include "cairoint.h" +#include "cairo-box-inline.h" #include "cairo-error-private.h" +#include "cairo-list-inline.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" @@ -67,19 +69,6 @@ _cairo_path_buf_add_points (cairo_path_buf_t *buf, const cairo_point_t *points, int num_points); -#define cairo_path_head(path__) (&(path__)->buf.base) -#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__)) - -#define cairo_path_buf_next(pos__) \ - cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link) -#define cairo_path_buf_prev(pos__) \ - cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link) - -#define cairo_path_foreach_buf_start(pos__, path__) \ - pos__ = cairo_path_head (path__); do -#define cairo_path_foreach_buf_end(pos__, path__) \ - while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__)) - void _cairo_path_fixed_init (cairo_path_fixed_t *path) { @@ -97,15 +86,18 @@ _cairo_path_fixed_init (cairo_path_fixed_t *path) path->current_point.x = 0; path->current_point.y = 0; path->last_move_point = path->current_point; - path->has_last_move_point = FALSE; - path->has_current_point = FALSE; - path->has_curve_to = FALSE; - path->is_rectilinear = TRUE; - path->maybe_fill_region = TRUE; - path->is_empty_fill = TRUE; - path->extents.p1.x = path->extents.p1.y = INT_MAX; - path->extents.p2.x = path->extents.p2.y = INT_MIN; + path->has_current_point = FALSE; + path->needs_move_to = TRUE; + path->has_extents = FALSE; + path->has_curve_to = FALSE; + path->stroke_is_rectilinear = TRUE; + path->fill_is_rectilinear = TRUE; + path->fill_maybe_region = TRUE; + path->fill_is_empty = TRUE; + + path->extents.p1.x = path->extents.p1.y = 0; + path->extents.p2.x = path->extents.p2.y = 0; } cairo_status_t @@ -126,12 +118,15 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, path->current_point = other->current_point; path->last_move_point = other->last_move_point; - path->has_last_move_point = other->has_last_move_point; + path->has_current_point = other->has_current_point; + path->needs_move_to = other->needs_move_to; + path->has_extents = other->has_extents; path->has_curve_to = other->has_curve_to; - path->is_rectilinear = other->is_rectilinear; - path->maybe_fill_region = other->maybe_fill_region; - path->is_empty_fill = other->is_empty_fill; + path->stroke_is_rectilinear = other->stroke_is_rectilinear; + path->fill_is_rectilinear = other->fill_is_rectilinear; + path->fill_maybe_region = other->fill_maybe_region; + path->fill_is_empty = other->fill_is_empty; path->extents = other->extents; @@ -182,23 +177,23 @@ _cairo_path_fixed_hash (const cairo_path_fixed_t *path) { unsigned long hash = _CAIRO_HASH_INIT_VALUE; const cairo_path_buf_t *buf; - int num_points, num_ops; + unsigned int count; - hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents)); - - num_ops = num_points = 0; + count = 0; cairo_path_foreach_buf_start (buf, path) { hash = _cairo_hash_bytes (hash, buf->op, buf->num_ops * sizeof (buf->op[0])); + count += buf->num_ops; + } cairo_path_foreach_buf_end (buf, path); + hash = _cairo_hash_bytes (hash, &count, sizeof (count)); + + count = 0; + cairo_path_foreach_buf_start (buf, path) { hash = _cairo_hash_bytes (hash, buf->points, buf->num_points * sizeof (buf->points[0])); - - num_ops += buf->num_ops; - num_points += buf->num_points; + count += buf->num_points; } cairo_path_foreach_buf_end (buf, path); - - hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops)); - hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points)); + hash = _cairo_hash_bytes (hash, &count, sizeof (count)); return hash; } @@ -233,10 +228,7 @@ _cairo_path_fixed_equal (const cairo_path_fixed_t *a, return TRUE; /* use the flags to quickly differentiate based on contents */ - if (a->is_empty_fill != b->is_empty_fill || - a->has_curve_to != b->has_curve_to || - a->maybe_fill_region != b->maybe_fill_region || - a->is_rectilinear != b->is_rectilinear) + if (a->has_curve_to != b->has_curve_to) { return FALSE; } @@ -333,7 +325,7 @@ _cairo_path_fixed_create (void) { cairo_path_fixed_t *path; - path = malloc (sizeof (cairo_path_fixed_t)); + path = _cairo_malloc (sizeof (cairo_path_fixed_t)); if (!path) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; @@ -355,7 +347,7 @@ _cairo_path_fixed_fini (cairo_path_fixed_t *path) _cairo_path_buf_destroy (this); } - VG (VALGRIND_MAKE_MEM_NOACCESS (path, sizeof (cairo_path_fixed_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); } void @@ -366,30 +358,42 @@ _cairo_path_fixed_destroy (cairo_path_fixed_t *path) } static cairo_path_op_t -_cairo_path_last_op (cairo_path_fixed_t *path) +_cairo_path_fixed_last_op (cairo_path_fixed_t *path) { cairo_path_buf_t *buf; buf = cairo_path_tail (path); - if (buf->num_ops == 0) - return -1; + assert (buf->num_ops != 0); return buf->op[buf->num_ops - 1]; } -static inline void -_cairo_path_fixed_extents_add (cairo_path_fixed_t *path, - const cairo_point_t *point) +static inline const cairo_point_t * +_cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path) { - if (point->x < path->extents.p1.x) - path->extents.p1.x = point->x; - if (point->y < path->extents.p1.y) - path->extents.p1.y = point->y; + cairo_path_buf_t *buf; - if (point->x > path->extents.p2.x) - path->extents.p2.x = point->x; - if (point->y > path->extents.p2.y) - path->extents.p2.y = point->y; + buf = cairo_path_tail (path); + if (likely (buf->num_points >= 2)) { + return &buf->points[buf->num_points - 2]; + } else { + cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); + + assert (prev_buf->num_points >= 2 - buf->num_points); + return &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; + } +} + +static void +_cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path) +{ + cairo_path_buf_t *buf; + + assert (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO); + + buf = cairo_path_tail (path); + buf->num_points--; + buf->num_ops--; } cairo_status_t @@ -397,48 +401,55 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t *path, cairo_fixed_t x, cairo_fixed_t y) { - cairo_status_t status; - cairo_point_t point; + _cairo_path_fixed_new_sub_path (path); - point.x = x; - point.y = y; - - /* If the previous op was also a MOVE_TO, then just change its - * point rather than adding a new op. */ - if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) { - cairo_path_buf_t *buf; - - buf = cairo_path_tail (path); - buf->points[buf->num_points - 1] = point; - } else { - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); - if (unlikely (status)) - return status; - - if (path->has_current_point && path->is_rectilinear) { - /* a move-to is first an implicit close */ - path->is_rectilinear = path->current_point.x == path->last_move_point.x || - path->current_point.y == path->last_move_point.y; - path->maybe_fill_region &= path->is_rectilinear; - } - if (path->maybe_fill_region) { - path->maybe_fill_region = - _cairo_fixed_is_integer (path->last_move_point.x) && - _cairo_fixed_is_integer (path->last_move_point.y); - } - } - - path->current_point = point; - path->last_move_point = point; - path->has_last_move_point = TRUE; path->has_current_point = TRUE; + path->current_point.x = x; + path->current_point.y = y; + path->last_move_point = path->current_point; return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_path_fixed_move_to_apply (cairo_path_fixed_t *path) +{ + if (likely (! path->needs_move_to)) + return CAIRO_STATUS_SUCCESS; + + path->needs_move_to = FALSE; + + if (path->has_extents) { + _cairo_box_add_point (&path->extents, &path->current_point); + } else { + _cairo_box_set (&path->extents, &path->current_point, &path->current_point); + path->has_extents = TRUE; + } + + if (path->fill_maybe_region) { + path->fill_maybe_region = _cairo_fixed_is_integer (path->current_point.x) && + _cairo_fixed_is_integer (path->current_point.y); + } + + path->last_move_point = path->current_point; + + return _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &path->current_point, 1); +} + void _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path) { + if (! path->needs_move_to) { + /* If the current subpath doesn't need_move_to, it contains at least one command */ + if (path->fill_is_rectilinear) { + /* Implicitly close for fill */ + path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; + path->fill_maybe_region &= path->fill_is_rectilinear; + } + path->needs_move_to = TRUE; + } + path->has_current_point = FALSE; } @@ -475,12 +486,16 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path, if (! path->has_current_point) return _cairo_path_fixed_move_to (path, point.x, point.y); + status = _cairo_path_fixed_move_to_apply (path); + if (unlikely (status)) + return status; + /* If the previous op was but the initial MOVE_TO and this segment * is degenerate, then we can simply skip this point. Note that * a move-to followed by a degenerate line-to is a valid path for * stroking, but at all other times is simply a degenerate segment. */ - if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { + if (_cairo_path_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { if (x == path->current_point.x && y == path->current_point.y) return CAIRO_STATUS_SUCCESS; } @@ -488,22 +503,13 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path, /* If the previous op was also a LINE_TO with the same gradient, * then just change its end-point rather than adding a new op. */ - if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { - cairo_path_buf_t *buf; + if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) { const cairo_point_t *p; - buf = cairo_path_tail (path); - if (likely (buf->num_points >= 2)) { - p = &buf->points[buf->num_points-2]; - } else { - cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); - p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; - } - + p = _cairo_path_fixed_penultimate_point (path); if (p->x == path->current_point.x && p->y == path->current_point.y) { /* previous line element was degenerate, replace */ - buf->points[buf->num_points - 1] = point; - goto FLAGS; + _cairo_path_fixed_drop_line_to (path); } else { cairo_slope_t prev, self; @@ -513,38 +519,36 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path, /* cannot trim anti-parallel segments whilst stroking */ ! _cairo_slope_backwards (&prev, &self)) { - buf->points[buf->num_points - 1] = point; - goto FLAGS; + _cairo_path_fixed_drop_line_to (path); + /* In this case the flags might be more restrictive than + * what we actually need. + * When changing the flags definition we should check if + * changing the line_to point can affect them. + */ } } } - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); - if (unlikely (status)) - return status; - - FLAGS: - if (path->is_rectilinear) { - path->is_rectilinear = path->current_point.x == x || - path->current_point.y == y; - path->maybe_fill_region &= path->is_rectilinear; - } - if (path->maybe_fill_region) { - path->maybe_fill_region = _cairo_fixed_is_integer (x) && - _cairo_fixed_is_integer (y); - } - if (path->is_empty_fill) { - path->is_empty_fill = path->current_point.x == x && - path->current_point.y == y; + if (path->stroke_is_rectilinear) { + path->stroke_is_rectilinear = path->current_point.x == x || + path->current_point.y == y; + path->fill_is_rectilinear &= path->stroke_is_rectilinear; + path->fill_maybe_region &= path->fill_is_rectilinear; + if (path->fill_maybe_region) { + path->fill_maybe_region = _cairo_fixed_is_integer (x) && + _cairo_fixed_is_integer (y); + } + if (path->fill_is_empty) { + path->fill_is_empty = path->current_point.x == x && + path->current_point.y == y; + } } path->current_point = point; - if (path->has_last_move_point) { - _cairo_path_fixed_extents_add (path, &path->last_move_point); - path->has_last_move_point = FALSE; - } - _cairo_path_fixed_extents_add (path, &point); - return CAIRO_STATUS_SUCCESS; + + _cairo_box_add_point (&path->extents, &point); + + return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); } cairo_status_t @@ -569,37 +573,54 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, cairo_status_t status; cairo_point_t point[3]; + /* If this curves does not move, replace it with a line-to. + * This frequently happens with rounded-rectangles and r==0. + */ + if (path->current_point.x == x2 && path->current_point.y == y2) { + if (x1 == x2 && x0 == x2 && y1 == y2 && y0 == y2) + return _cairo_path_fixed_line_to (path, x2, y2); + + /* We may want to check for the absence of a cusp, in which case + * we can also replace the curve-to with a line-to. + */ + } + /* make sure subpaths are started properly */ if (! path->has_current_point) { status = _cairo_path_fixed_move_to (path, x0, y0); - if (unlikely (status)) - return status; + assert (status == CAIRO_STATUS_SUCCESS); + } + + status = _cairo_path_fixed_move_to_apply (path); + if (unlikely (status)) + return status; + + /* If the previous op was a degenerate LINE_TO, drop it. */ + if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) { + const cairo_point_t *p; + + p = _cairo_path_fixed_penultimate_point (path); + if (p->x == path->current_point.x && p->y == path->current_point.y) { + /* previous line element was degenerate, replace */ + _cairo_path_fixed_drop_line_to (path); + } } point[0].x = x0; point[0].y = y0; point[1].x = x1; point[1].y = y1; point[2].x = x2; point[2].y = y2; - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); - if (unlikely (status)) - return status; + + _cairo_box_add_curve_to (&path->extents, &path->current_point, + &point[0], &point[1], &point[2]); path->current_point = point[2]; - path->has_current_point = TRUE; - path->is_empty_fill = FALSE; path->has_curve_to = TRUE; - path->is_rectilinear = FALSE; - path->maybe_fill_region = FALSE; + path->stroke_is_rectilinear = FALSE; + path->fill_is_rectilinear = FALSE; + path->fill_maybe_region = FALSE; + path->fill_is_empty = FALSE; - /* coarse bounds */ - if (path->has_last_move_point) { - _cairo_path_fixed_extents_add (path, &path->last_move_point); - path->has_last_move_point = FALSE; - } - _cairo_path_fixed_extents_add (path, &point[0]); - _cairo_path_fixed_extents_add (path, &point[1]); - _cairo_path_fixed_extents_add (path, &point[2]); - - return CAIRO_STATUS_SUCCESS; + return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); } cairo_status_t @@ -630,35 +651,28 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path) if (! path->has_current_point) return CAIRO_STATUS_SUCCESS; - /* If the previous op was also a LINE_TO back to the start, discard it */ - if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) { - if (path->current_point.x == path->last_move_point.x && - path->current_point.y == path->last_move_point.y) - { - cairo_path_buf_t *buf; - cairo_point_t *p; - - buf = cairo_path_tail (path); - if (likely (buf->num_points >= 2)) { - p = &buf->points[buf->num_points-2]; - } else { - cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf); - p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)]; - } - - path->current_point = *p; - buf->num_ops--; - buf->num_points--; - } - } - - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + /* + * Add a line_to, to compute flags and solve any degeneracy. + * It will be removed later (if it was actually added). + */ + status = _cairo_path_fixed_line_to (path, + path->last_move_point.x, + path->last_move_point.y); if (unlikely (status)) return status; - return _cairo_path_fixed_move_to (path, - path->last_move_point.x, - path->last_move_point.y); + /* + * If the command used to close the path is a line_to, drop it. + * We must check that last command is actually a line_to, + * because the path could have been closed with a curve_to (and + * the previous line_to not added as it would be degenerate). + */ + if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) + _cairo_path_fixed_drop_line_to (path); + + path->needs_move_to = TRUE; /* After close_path, add an implicit move_to */ + + return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); } cairo_bool_t @@ -714,9 +728,20 @@ _cairo_path_fixed_add (cairo_path_fixed_t *path, } len += snprintf (buf + len, sizeof (buf), "]"); +#define STRINGIFYFLAG(x) (path->x ? #x " " : "") fprintf (stderr, - "_cairo_path_fixed_add (%s, %s)\n", - op_str[(int) op], buf); + "_cairo_path_fixed_add (%s, %s) [%s%s%s%s%s%s%s%s]\n", + op_str[(int) op], buf, + STRINGIFYFLAG(has_current_point), + STRINGIFYFLAG(needs_move_to), + STRINGIFYFLAG(has_extents), + STRINGIFYFLAG(has_curve_to), + STRINGIFYFLAG(stroke_is_rectilinear), + STRINGIFYFLAG(fill_is_rectilinear), + STRINGIFYFLAG(fill_is_empty), + STRINGIFYFLAG(fill_maybe_region) + ); +#undef STRINGIFYFLAG } _cairo_path_buf_add_op (buf, op); @@ -772,6 +797,9 @@ _cairo_path_buf_add_points (cairo_path_buf_t *buf, const cairo_point_t *points, int num_points) { + if (num_points == 0) + return; + memcpy (buf->points + buf->num_points, points, sizeof (points[0]) * num_points); @@ -780,54 +808,32 @@ _cairo_path_buf_add_points (cairo_path_buf_t *buf, cairo_status_t _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, - cairo_direction_t dir, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_curve_to_func_t *curve_to, cairo_path_fixed_close_path_func_t *close_path, void *closure) { - const uint8_t num_args[] = { - 1, /* cairo_path_move_to */ - 1, /* cairo_path_op_line_to */ - 3, /* cairo_path_op_curve_to */ - 0, /* cairo_path_op_close_path */ - }; + const cairo_path_buf_t *buf; cairo_status_t status; - const cairo_path_buf_t *buf, *first; - cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD); - int step = forward ? 1 : -1; - buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path); - do { - cairo_point_t *points; - int start, stop, i; + cairo_path_foreach_buf_start (buf, path) { + const cairo_point_t *points = buf->points; + unsigned int i; - if (forward) { - start = 0; - stop = buf->num_ops; - points = buf->points; - } else { - start = buf->num_ops - 1; - stop = -1; - points = buf->points + buf->num_points; - } - - for (i = start; i != stop; i += step) { - cairo_path_op_t op = buf->op[i]; - - if (! forward) - points -= num_args[(int) op]; - - switch (op) { + for (i = 0; i < buf->num_ops; i++) { + switch (buf->op[i]) { case CAIRO_PATH_OP_MOVE_TO: status = (*move_to) (closure, &points[0]); + points += 1; break; case CAIRO_PATH_OP_LINE_TO: status = (*line_to) (closure, &points[0]); + points += 1; break; case CAIRO_PATH_OP_CURVE_TO: status = (*curve_to) (closure, &points[0], &points[1], &points[2]); + points += 3; break; default: ASSERT_NOT_REACHED; @@ -835,13 +841,14 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, status = (*close_path) (closure); break; } + if (unlikely (status)) return status; - - if (forward) - points += num_args[(int) op]; } - } while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first); + } cairo_path_foreach_buf_end (buf, path); + + if (path->needs_move_to && path->has_current_point) + return (*move_to) (closure, &path->current_point); return CAIRO_STATUS_SUCCESS; } @@ -901,7 +908,6 @@ _append_close_path (void *abstract_closure) cairo_status_t _cairo_path_fixed_append (cairo_path_fixed_t *path, const cairo_path_fixed_t *other, - cairo_direction_t dir, cairo_fixed_t tx, cairo_fixed_t ty) { @@ -911,7 +917,7 @@ _cairo_path_fixed_append (cairo_path_fixed_t *path, closure.offset.x = tx; closure.offset.y = ty; - return _cairo_path_fixed_interpret (other, dir, + return _cairo_path_fixed_interpret (other, _append_move_to, _append_line_to, _append_curve_to, @@ -929,13 +935,18 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, cairo_path_buf_t *buf; unsigned int i; - if (path->maybe_fill_region) { - path->maybe_fill_region = _cairo_fixed_is_integer (offx) && - _cairo_fixed_is_integer (offy) && - _cairo_fixed_is_integer (scalex) && - _cairo_fixed_is_integer (scaley); + if (scalex == CAIRO_FIXED_ONE && scaley == CAIRO_FIXED_ONE) { + _cairo_path_fixed_translate (path, offx, offy); + return; } + path->last_move_point.x = _cairo_fixed_mul (scalex, path->last_move_point.x) + offx; + path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy; + path->current_point.x = _cairo_fixed_mul (scalex, path->current_point.x) + offx; + path->current_point.y = _cairo_fixed_mul (scaley, path->current_point.y) + offy; + + path->fill_maybe_region = TRUE; + cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { if (scalex != CAIRO_FIXED_ONE) @@ -945,14 +956,31 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, if (scaley != CAIRO_FIXED_ONE) buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley); buf->points[i].y += offy; + + if (path->fill_maybe_region) { + path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) && + _cairo_fixed_is_integer (buf->points[i].y); + } } } cairo_path_foreach_buf_end (buf, path); + path->fill_maybe_region &= path->fill_is_rectilinear; + path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx; path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx; + if (scalex < 0) { + cairo_fixed_t t = path->extents.p1.x; + path->extents.p1.x = path->extents.p2.x; + path->extents.p2.x = t; + } path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy; path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy; + if (scaley < 0) { + cairo_fixed_t t = path->extents.p1.y; + path->extents.p1.y = path->extents.p2.y; + path->extents.p2.y = t; + } } void @@ -966,30 +994,47 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path, if (offx == 0 && offy == 0) return; - if (path->maybe_fill_region && - ! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy))) - { - path->maybe_fill_region = FALSE; - } - path->last_move_point.x += offx; path->last_move_point.y += offy; path->current_point.x += offx; path->current_point.y += offy; + path->fill_maybe_region = TRUE; + cairo_path_foreach_buf_start (buf, path) { - for (i = 0; i < buf->num_points; i++) { - buf->points[i].x += offx; - buf->points[i].y += offy; + for (i = 0; i < buf->num_points; i++) { + buf->points[i].x += offx; + buf->points[i].y += offy; + + if (path->fill_maybe_region) { + path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) && + _cairo_fixed_is_integer (buf->points[i].y); + } } } cairo_path_foreach_buf_end (buf, path); + path->fill_maybe_region &= path->fill_is_rectilinear; + path->extents.p1.x += offx; path->extents.p1.y += offy; path->extents.p2.x += offx; path->extents.p2.y += offy; } + +static inline void +_cairo_path_fixed_transform_point (cairo_point_t *p, + const cairo_matrix_t *matrix) +{ + double dx, dy; + + dx = _cairo_fixed_to_double (p->x); + dy = _cairo_fixed_to_double (p->y); + cairo_matrix_transform_point (matrix, &dx, &dy); + p->x = _cairo_fixed_from_double (dx); + p->y = _cairo_fixed_from_double (dy); +} + /** * _cairo_path_fixed_transform: * @path: a #cairo_path_fixed_t to be transformed @@ -1003,80 +1048,58 @@ void _cairo_path_fixed_transform (cairo_path_fixed_t *path, const cairo_matrix_t *matrix) { + cairo_box_t extents; + cairo_point_t point; cairo_path_buf_t *buf; unsigned int i; - double dx, dy; - - /* XXX current_point, last_move_to */ if (matrix->yx == 0.0 && matrix->xy == 0.0) { /* Fast path for the common case of scale+transform */ - if (matrix->xx == 1. && matrix->yy == 1.) { - _cairo_path_fixed_translate (path, - _cairo_fixed_from_double (matrix->x0), - _cairo_fixed_from_double (matrix->y0)); - } else { - _cairo_path_fixed_offset_and_scale (path, - _cairo_fixed_from_double (matrix->x0), - _cairo_fixed_from_double (matrix->y0), - _cairo_fixed_from_double (matrix->xx), - _cairo_fixed_from_double (matrix->yy)); - } + _cairo_path_fixed_offset_and_scale (path, + _cairo_fixed_from_double (matrix->x0), + _cairo_fixed_from_double (matrix->y0), + _cairo_fixed_from_double (matrix->xx), + _cairo_fixed_from_double (matrix->yy)); return; } - path->extents.p1.x = path->extents.p1.y = INT_MAX; - path->extents.p2.x = path->extents.p2.y = INT_MIN; - path->maybe_fill_region = FALSE; + _cairo_path_fixed_transform_point (&path->last_move_point, matrix); + _cairo_path_fixed_transform_point (&path->current_point, matrix); + + buf = cairo_path_head (path); + if (buf->num_points == 0) + return; + + extents = path->extents; + point = buf->points[0]; + _cairo_path_fixed_transform_point (&point, matrix); + _cairo_box_set (&path->extents, &point, &point); + cairo_path_foreach_buf_start (buf, path) { - for (i = 0; i < buf->num_points; i++) { - dx = _cairo_fixed_to_double (buf->points[i].x); - dy = _cairo_fixed_to_double (buf->points[i].y); - - cairo_matrix_transform_point (matrix, &dx, &dy); - - buf->points[i].x = _cairo_fixed_from_double (dx); - buf->points[i].y = _cairo_fixed_from_double (dy); - - /* XXX need to eliminate surplus move-to's? */ - _cairo_path_fixed_extents_add (path, &buf->points[i]); - } + for (i = 0; i < buf->num_points; i++) { + _cairo_path_fixed_transform_point (&buf->points[i], matrix); + _cairo_box_add_point (&path->extents, &buf->points[i]); + } } cairo_path_foreach_buf_end (buf, path); -} -cairo_bool_t -_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, - const cairo_path_fixed_t *other) -{ - const cairo_path_buf_t *path_buf, *other_buf; + if (path->has_curve_to) { + cairo_bool_t is_tight; - if (path->current_point.x != other->current_point.x || - path->current_point.y != other->current_point.y || - path->has_current_point != other->has_current_point || - path->has_curve_to != other->has_curve_to || - path->is_rectilinear != other->is_rectilinear || - path->maybe_fill_region != other->maybe_fill_region || - path->last_move_point.x != other->last_move_point.x || - path->last_move_point.y != other->last_move_point.y) - { - return FALSE; + _cairo_matrix_transform_bounding_box_fixed (matrix, &extents, &is_tight); + if (!is_tight) { + cairo_bool_t has_extents; + + has_extents = _cairo_path_bounder_extents (path, &extents); + assert (has_extents); + } + path->extents = extents; } - other_buf = cairo_path_head (other); - cairo_path_foreach_buf_start (path_buf, path) { - if (path_buf->num_ops != other_buf->num_ops || - path_buf->num_points != other_buf->num_points || - memcmp (path_buf->op, other_buf->op, - sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 || - memcmp (path_buf->points, other_buf->points, - sizeof (cairo_point_t) * path_buf->num_points) != 0) - { - return FALSE; - } - other_buf = cairo_path_buf_next (other_buf); - } cairo_path_foreach_buf_end (path_buf, path); - - return TRUE; + /* flags might become more strict than needed */ + path->stroke_is_rectilinear = FALSE; + path->fill_is_rectilinear = FALSE; + path->fill_is_empty = FALSE; + path->fill_maybe_region = FALSE; } /* Closure for path flattening */ @@ -1123,7 +1146,7 @@ _cpf_curve_to (void *closure, cairo_point_t *p0 = &cpf->current_point; if (! _cairo_spline_init (&spline, - cpf->line_to, + (cairo_spline_add_point_func_t)cpf->line_to, cpf->closure, p0, p1, p2, p3)) { @@ -1145,7 +1168,6 @@ _cpf_close_path (void *closure) cairo_status_t _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, - cairo_direction_t dir, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_close_path_func_t *close_path, @@ -1155,7 +1177,7 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, cpf_t flattener; if (! path->has_curve_to) { - return _cairo_path_fixed_interpret (path, dir, + return _cairo_path_fixed_interpret (path, move_to, line_to, NULL, @@ -1168,7 +1190,7 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, flattener.line_to = line_to; flattener.close_path = close_path; flattener.closure = closure; - return _cairo_path_fixed_interpret (path, dir, + return _cairo_path_fixed_interpret (path, _cpf_move_to, _cpf_line_to, _cpf_curve_to, @@ -1198,18 +1220,11 @@ _canonical_box (cairo_box_t *box, } } -/* - * Check whether the given path contains a single rectangle. - */ -cairo_bool_t -_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, - cairo_box_t *box) +static inline cairo_bool_t +_path_is_quad (const cairo_path_fixed_t *path) { const cairo_path_buf_t *buf = cairo_path_head (path); - if (! path->is_rectilinear) - return FALSE; - /* Do we have the right number of ops? */ if (buf->num_ops < 4 || buf->num_ops > 6) return FALSE; @@ -1244,6 +1259,154 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path, } } + return TRUE; +} + +static inline cairo_bool_t +_points_form_rect (const cairo_point_t *points) +{ + if (points[0].y == points[1].y && + points[1].x == points[2].x && + points[2].y == points[3].y && + points[3].x == points[0].x) + return TRUE; + if (points[0].x == points[1].x && + points[1].y == points[2].y && + points[2].x == points[3].x && + points[3].y == points[0].y) + return TRUE; + return FALSE; +} + +/* + * Check whether the given path contains a single rectangle. + */ +cairo_bool_t +_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + const cairo_path_buf_t *buf; + + if (! path->fill_is_rectilinear) + return FALSE; + + if (! _path_is_quad (path)) + return FALSE; + + buf = cairo_path_head (path); + if (_points_form_rect (buf->points)) { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + return FALSE; +} + +/* Determine whether two lines A->B and C->D intersect based on the + * algorithm described here: http://paulbourke.net/geometry/pointlineplane/ */ +static inline cairo_bool_t +_lines_intersect_or_are_coincident (cairo_point_t a, + cairo_point_t b, + cairo_point_t c, + cairo_point_t d) +{ + cairo_int64_t numerator_a, numerator_b, denominator; + cairo_bool_t denominator_negative; + + denominator = _cairo_int64_sub (_cairo_int32x32_64_mul (d.y - c.y, b.x - a.x), + _cairo_int32x32_64_mul (d.x - c.x, b.y - a.y)); + numerator_a = _cairo_int64_sub (_cairo_int32x32_64_mul (d.x - c.x, a.y - c.y), + _cairo_int32x32_64_mul (d.y - c.y, a.x - c.x)); + numerator_b = _cairo_int64_sub (_cairo_int32x32_64_mul (b.x - a.x, a.y - c.y), + _cairo_int32x32_64_mul (b.y - a.y, a.x - c.x)); + + if (_cairo_int64_is_zero (denominator)) { + /* If the denominator and numerators are both zero, + * the lines are coincident. */ + if (_cairo_int64_is_zero (numerator_a) && _cairo_int64_is_zero (numerator_b)) + return TRUE; + + /* Otherwise, a zero denominator indicates the lines are + * parallel and never intersect. */ + return FALSE; + } + + /* The lines intersect if both quotients are between 0 and 1 (exclusive). */ + + /* We first test whether either quotient is a negative number. */ + denominator_negative = _cairo_int64_negative (denominator); + if (_cairo_int64_negative (numerator_a) ^ denominator_negative) + return FALSE; + if (_cairo_int64_negative (numerator_b) ^ denominator_negative) + return FALSE; + + /* A zero quotient indicates an "intersection" at an endpoint, which + * we aren't considering a true intersection. */ + if (_cairo_int64_is_zero (numerator_a) || _cairo_int64_is_zero (numerator_b)) + return FALSE; + + /* If the absolute value of the numerator is larger than or equal to the + * denominator the result of the division would be greater than or equal + * to one. */ + if (! denominator_negative) { + if (! _cairo_int64_lt (numerator_a, denominator) || + ! _cairo_int64_lt (numerator_b, denominator)) + return FALSE; + } else { + if (! _cairo_int64_lt (denominator, numerator_a) || + ! _cairo_int64_lt (denominator, numerator_b)) + return FALSE; + } + + return TRUE; +} + +cairo_bool_t +_cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path) +{ + const cairo_point_t *points; + + if (! _path_is_quad (path)) + return FALSE; + + points = cairo_path_head (path)->points; + if (_points_form_rect (points)) + return TRUE; + + if (_lines_intersect_or_are_coincident (points[0], points[1], + points[3], points[2])) + return FALSE; + + if (_lines_intersect_or_are_coincident (points[0], points[3], + points[1], points[2])) + return FALSE; + + return TRUE; +} + +cairo_bool_t +_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + const cairo_path_buf_t *buf = cairo_path_head (path); + + if (! path->fill_is_rectilinear) + return FALSE; + + /* Do we have the right number of ops? */ + if (buf->num_ops != 5) + return FALSE; + + /* Check whether the ops are those that would be used for a rectangle */ + if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO || + buf->op[1] != CAIRO_PATH_OP_LINE_TO || + buf->op[2] != CAIRO_PATH_OP_LINE_TO || + buf->op[3] != CAIRO_PATH_OP_LINE_TO || + buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) + { + return FALSE; + } + /* Ok, we may have a box, if the points line up */ if (buf->points[0].y == buf->points[1].y && buf->points[1].x == buf->points[2].x && @@ -1286,8 +1449,11 @@ _cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, if (! _cairo_path_fixed_is_box (path, box)) return FALSE; + /* This check is valid because the current implementation of + * _cairo_path_fixed_is_box () only accepts rectangles like: + * move,line,line,line[,line|close[,close|move]]. */ buf = cairo_path_head (path); - if (buf->points[0].y == buf->points[1].y) + if (buf->num_ops > 4) return TRUE; return FALSE; @@ -1331,11 +1497,8 @@ _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, iter = *_iter; - if (iter.n_op == iter.buf->num_ops && - ! _cairo_path_fixed_iter_next_op (&iter)) - { + if (iter.n_op == iter.buf->num_ops && ! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; - } /* Check whether the ops are those that would be used for a rectangle */ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO) @@ -1350,8 +1513,20 @@ _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, if (! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; - if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + /* a horizontal/vertical closed line is also a degenerate rectangle */ + switch (iter.buf->op[iter.n_op]) { + case CAIRO_PATH_OP_CLOSE_PATH: + _cairo_path_fixed_iter_next_op (&iter); /* fall through */ + case CAIRO_PATH_OP_MOVE_TO: /* implicit close */ + box->p1 = box->p2 = points[0]; + *_iter = iter; + return TRUE; + default: return FALSE; + case CAIRO_PATH_OP_LINE_TO: + break; + } + points[2] = iter.buf->points[iter.n_point++]; if (! _cairo_path_fixed_iter_next_op (&iter)) return FALSE; @@ -1359,25 +1534,24 @@ _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) return FALSE; points[3] = iter.buf->points[iter.n_point++]; - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; /* Now, there are choices. The rectangle might end with a LINE_TO * (to the original point), but this isn't required. If it * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */ - if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) - { + if (! _cairo_path_fixed_iter_next_op (&iter)) { + /* implicit close due to fill */ + } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) { points[4] = iter.buf->points[iter.n_point++]; if (points[4].x != points[0].x || points[4].y != points[0].y) return FALSE; - } - else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH || - iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO)) - { + _cairo_path_fixed_iter_next_op (&iter); + } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH) { + _cairo_path_fixed_iter_next_op (&iter); + } else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO) { + /* implicit close-path due to new-sub-path */ + } else { return FALSE; } - if (! _cairo_path_fixed_iter_next_op (&iter)) - return FALSE; /* Ok, we may have a box, if the points line up */ if (points[0].y == points[1].y && @@ -1411,14 +1585,5 @@ _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter) if (iter->buf == NULL) return TRUE; - if (iter->n_op == iter->buf->num_ops) - return TRUE; - - if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO && - iter->buf->num_ops == iter->n_op + 1) - { - return TRUE; - } - - return FALSE; + return iter->n_op == iter->buf->num_ops; } diff --git a/gfx/cairo/cairo/src/cairo-path-in-fill.c b/gfx/cairo/cairo/src/cairo-path-in-fill.c index b344f529da33..1787fb1a3bab 100644 --- a/gfx/cairo/cairo/src/cairo-path-in-fill.c +++ b/gfx/cairo/cairo/src/cairo-path-in-fill.c @@ -217,7 +217,7 @@ _cairo_in_fill_curve_to (void *closure, /* XXX Investigate direct inspection of the inflections? */ if (! _cairo_spline_init (&spline, - _cairo_in_fill_line_to, + (cairo_spline_add_point_func_t)_cairo_in_fill_line_to, in_fill, &in_fill->current_point, b, c, d)) { @@ -254,13 +254,12 @@ _cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, cairo_status_t status; cairo_bool_t is_inside; - if (path->is_empty_fill) + if (_cairo_path_fixed_fill_is_empty (path)) return FALSE; _cairo_in_fill_init (&in_fill, tolerance, x, y); status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_in_fill_move_to, _cairo_in_fill_line_to, _cairo_in_fill_curve_to, diff --git a/gfx/cairo/cairo/src/cairo-path-private.h b/gfx/cairo/cairo/src/cairo-path-private.h index 61b4060fa869..7b54317e20ef 100644 --- a/gfx/cairo/cairo/src/cairo-path-private.h +++ b/gfx/cairo/cairo/src/cairo-path-private.h @@ -41,11 +41,11 @@ cairo_private cairo_path_t * _cairo_path_create (cairo_path_fixed_t *path, - cairo_gstate_t *gstate); + cairo_t *cr); cairo_private cairo_path_t * _cairo_path_create_flat (cairo_path_fixed_t *path, - cairo_gstate_t *gstate); + cairo_t *cr); cairo_private cairo_path_t * _cairo_path_create_in_error (cairo_status_t status); diff --git a/gfx/cairo/cairo/src/cairo-path-stroke-boxes.c b/gfx/cairo/cairo/src/cairo-path-stroke-boxes.c new file mode 100644 index 000000000000..fba170c638aa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-stroke-boxes.c @@ -0,0 +1,711 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Chris Wilson + */ + +#define _DEFAULT_SOURCE /* for hypot() */ +#include "cairoint.h" + +#include "cairo-box-inline.h" +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" +#include "cairo-stroke-dash-private.h" + +typedef struct _segment_t { + cairo_point_t p1, p2; + unsigned flags; +#define HORIZONTAL 0x1 +#define FORWARDS 0x2 +#define JOIN 0x4 +} segment_t; + +typedef struct _cairo_rectilinear_stroker { + const cairo_stroke_style_t *stroke_style; + const cairo_matrix_t *ctm; + cairo_antialias_t antialias; + + cairo_fixed_t half_line_x, half_line_y; + cairo_boxes_t *boxes; + cairo_point_t current_point; + cairo_point_t first_point; + cairo_bool_t open_sub_path; + + cairo_stroker_dash_t dash; + + cairo_bool_t has_bounds; + cairo_box_t bounds; + + int num_segments; + int segments_size; + segment_t *segments; + segment_t segments_embedded[8]; /* common case is a single rectangle */ +} cairo_rectilinear_stroker_t; + +static void +_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, + const cairo_box_t *boxes, + int num_boxes) +{ + stroker->has_bounds = TRUE; + _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); + + stroker->bounds.p1.x -= stroker->half_line_x; + stroker->bounds.p2.x += stroker->half_line_x; + + stroker->bounds.p1.y -= stroker->half_line_y; + stroker->bounds.p2.y += stroker->half_line_y; +} + +static cairo_bool_t +_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_antialias_t antialias, + cairo_boxes_t *boxes) +{ + /* This special-case rectilinear stroker only supports + * miter-joined lines (not curves) and a translation-only matrix + * (though it could probably be extended to support a matrix with + * uniform, integer scaling). + * + * It also only supports horizontal and vertical line_to + * elements. But we don't catch that here, but instead return + * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any + * non-rectilinear line_to is encountered. + */ + if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) + return FALSE; + + /* If the miter limit turns right angles into bevels, then we + * can't use this optimization. Remember, the ratio is + * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, + * which we round for safety. */ + if (stroke_style->miter_limit < M_SQRT2) + return FALSE; + + if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || + stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) + { + return FALSE; + } + + if (! _cairo_matrix_is_scale (ctm)) + return FALSE; + + stroker->stroke_style = stroke_style; + stroker->ctm = ctm; + stroker->antialias = antialias; + + stroker->half_line_x = + _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0); + stroker->half_line_y = + _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0); + + stroker->open_sub_path = FALSE; + stroker->segments = stroker->segments_embedded; + stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); + stroker->num_segments = 0; + + _cairo_stroker_dash_init (&stroker->dash, stroke_style); + + stroker->has_bounds = FALSE; + + stroker->boxes = boxes; + + return TRUE; +} + +static void +_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) +{ + if (stroker->segments != stroker->segments_embedded) + free (stroker->segments); +} + +static cairo_status_t +_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, + const cairo_point_t *p1, + const cairo_point_t *p2, + unsigned flags) +{ + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (stroker->num_segments == stroker->segments_size) { + int new_size = stroker->segments_size * 2; + segment_t *new_segments; + + if (stroker->segments == stroker->segments_embedded) { + new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); + if (unlikely (new_segments == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_segments, stroker->segments, + stroker->num_segments * sizeof (segment_t)); + } else { + new_segments = _cairo_realloc_ab (stroker->segments, + new_size, sizeof (segment_t)); + if (unlikely (new_segments == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + stroker->segments_size = new_size; + stroker->segments = new_segments; + } + + stroker->segments[stroker->num_segments].p1 = *p1; + stroker->segments[stroker->num_segments].p2 = *p2; + stroker->segments[stroker->num_segments].flags = flags; + stroker->num_segments++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) +{ + cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; + cairo_fixed_t half_line_x = stroker->half_line_x; + cairo_fixed_t half_line_y = stroker->half_line_y; + cairo_status_t status; + int i, j; + + /* For each segment we generate a single rectangle. + * This rectangle is based on a perpendicular extension (by half the + * line width) of the segment endpoints * after some adjustments of the + * endpoints to account for caps and joins. + */ + for (i = 0; i < stroker->num_segments; i++) { + cairo_bool_t lengthen_initial, lengthen_final; + cairo_point_t *a, *b; + cairo_box_t box; + + a = &stroker->segments[i].p1; + b = &stroker->segments[i].p2; + + /* We adjust the initial point of the segment to extend the + * rectangle to include the previous cap or join, (this + * adjustment applies to all segments except for the first + * segment of open, butt-capped paths). However, we must be + * careful not to emit a miter join across a degenerate segment + * which has been elided. + * + * Overlapping segments will be eliminated by the tessellation. + * Ideally, we would not emit these self-intersections at all, + * but that is tricky with segments shorter than half_line_width. + */ + j = i == 0 ? stroker->num_segments - 1 : i-1; + lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; + j = i == stroker->num_segments - 1 ? 0 : i+1; + lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; + if (stroker->open_sub_path) { + if (i == 0) + lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT; + + if (i == stroker->num_segments - 1) + lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT; + } + + /* Perform the adjustments of the endpoints. */ + if (lengthen_initial | lengthen_final) { + if (a->y == b->y) { + if (a->x < b->x) { + if (lengthen_initial) + a->x -= half_line_x; + if (lengthen_final) + b->x += half_line_x; + } else { + if (lengthen_initial) + a->x += half_line_x; + if (lengthen_final) + b->x -= half_line_x; + } + } else { + if (a->y < b->y) { + if (lengthen_initial) + a->y -= half_line_y; + if (lengthen_final) + b->y += half_line_y; + } else { + if (lengthen_initial) + a->y += half_line_y; + if (lengthen_final) + b->y -= half_line_y; + } + } + } + + /* Form the rectangle by expanding by half the line width in + * either perpendicular direction. */ + if (a->y == b->y) { + a->y -= half_line_y; + b->y += half_line_y; + } else { + a->x -= half_line_x; + b->x += half_line_x; + } + + if (a->x < b->x) { + box.p1.x = a->x; + box.p2.x = b->x; + } else { + box.p1.x = b->x; + box.p2.x = a->x; + } + if (a->y < b->y) { + box.p1.y = a->y; + box.p2.y = b->y; + } else { + box.p1.y = b->y; + box.p2.y = a->y; + } + + status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); + if (unlikely (status)) + return status; + } + + stroker->num_segments = 0; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) +{ + cairo_status_t status; + cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; + cairo_fixed_t half_line_x = stroker->half_line_x; + cairo_fixed_t half_line_y = stroker->half_line_y; + int i; + + for (i = 0; i < stroker->num_segments; i++) { + cairo_point_t *a, *b; + cairo_bool_t is_horizontal; + cairo_box_t box; + + a = &stroker->segments[i].p1; + b = &stroker->segments[i].p2; + + is_horizontal = stroker->segments[i].flags & HORIZONTAL; + + /* Handle the joins for a potentially degenerate segment. */ + if (line_cap == CAIRO_LINE_CAP_BUTT && + stroker->segments[i].flags & JOIN && + (i != stroker->num_segments - 1 || + (! stroker->open_sub_path && stroker->dash.dash_starts_on))) + { + cairo_slope_t out_slope; + int j = (i + 1) % stroker->num_segments; + cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS); + + _cairo_slope_init (&out_slope, + &stroker->segments[j].p1, + &stroker->segments[j].p2); + box.p2 = box.p1 = stroker->segments[i].p2; + + if (is_horizontal) { + if (forwards) + box.p2.x += half_line_x; + else + box.p1.x -= half_line_x; + + if (out_slope.dy > 0) + box.p1.y -= half_line_y; + else + box.p2.y += half_line_y; + } else { + if (forwards) + box.p2.y += half_line_y; + else + box.p1.y -= half_line_y; + + if (out_slope.dx > 0) + box.p1.x -= half_line_x; + else + box.p2.x += half_line_x; + } + + status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); + if (unlikely (status)) + return status; + } + + /* Perform the adjustments of the endpoints. */ + if (is_horizontal) { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { + if (a->x <= b->x) { + a->x -= half_line_x; + b->x += half_line_x; + } else { + a->x += half_line_x; + b->x -= half_line_x; + } + } + + a->y += half_line_y; + b->y -= half_line_y; + } else { + if (line_cap == CAIRO_LINE_CAP_SQUARE) { + if (a->y <= b->y) { + a->y -= half_line_y; + b->y += half_line_y; + } else { + a->y += half_line_y; + b->y -= half_line_y; + } + } + + a->x += half_line_x; + b->x -= half_line_x; + } + + if (a->x == b->x && a->y == b->y) + continue; + + if (a->x < b->x) { + box.p1.x = a->x; + box.p2.x = b->x; + } else { + box.p1.x = b->x; + box.p2.x = a->x; + } + if (a->y < b->y) { + box.p1.y = a->y; + box.p2.y = b->y; + } else { + box.p1.y = b->y; + box.p2.y = a->y; + } + + status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); + if (unlikely (status)) + return status; + } + + stroker->num_segments = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_rectilinear_stroker_t *stroker = closure; + cairo_status_t status; + + if (stroker->dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (stroker); + if (unlikely (status)) + return status; + + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); + + stroker->current_point = *point; + stroker->first_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_line_to (void *closure, + const cairo_point_t *b) +{ + cairo_rectilinear_stroker_t *stroker = closure; + cairo_point_t *a = &stroker->current_point; + cairo_status_t status; + + /* We only support horizontal or vertical elements. */ + assert (a->x == b->x || a->y == b->y); + + /* We don't draw anything for degenerate paths. */ + if (a->x == b->x && a->y == b->y) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, + (a->y == b->y) | JOIN); + + stroker->current_point = *b; + stroker->open_sub_path = TRUE; + + return status; +} + +static cairo_status_t +_cairo_rectilinear_stroker_line_to_dashed (void *closure, + const cairo_point_t *point) +{ + cairo_rectilinear_stroker_t *stroker = closure; + const cairo_point_t *a = &stroker->current_point; + const cairo_point_t *b = point; + cairo_bool_t fully_in_bounds; + double sf, sign, remain; + cairo_fixed_t mag; + cairo_status_t status; + cairo_line_t segment; + cairo_bool_t dash_on = FALSE; + unsigned is_horizontal; + + /* We don't draw anything for degenerate paths. */ + if (a->x == b->x && a->y == b->y) + return CAIRO_STATUS_SUCCESS; + + /* We only support horizontal or vertical elements. */ + assert (a->x == b->x || a->y == b->y); + + fully_in_bounds = TRUE; + if (stroker->has_bounds && + (! _cairo_box_contains_point (&stroker->bounds, a) || + ! _cairo_box_contains_point (&stroker->bounds, b))) + { + fully_in_bounds = FALSE; + } + + is_horizontal = a->y == b->y; + if (is_horizontal) { + mag = b->x - a->x; + sf = fabs (stroker->ctm->xx); + } else { + mag = b->y - a->y; + sf = fabs (stroker->ctm->yy); + } + if (mag < 0) { + remain = _cairo_fixed_to_double (-mag); + sign = 1.; + } else { + remain = _cairo_fixed_to_double (mag); + is_horizontal |= FORWARDS; + sign = -1.; + } + + segment.p2 = segment.p1 = *a; + while (remain > 0.) { + double step_length; + + step_length = MIN (sf * stroker->dash.dash_remain, remain); + remain -= step_length; + + mag = _cairo_fixed_from_double (sign*remain); + if (is_horizontal & 0x1) + segment.p2.x = b->x + mag; + else + segment.p2.y = b->y + mag; + + if (stroker->dash.dash_on && + (fully_in_bounds || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + status = _cairo_rectilinear_stroker_add_segment (stroker, + &segment.p1, + &segment.p2, + is_horizontal | (remain <= 0.) << 2); + if (unlikely (status)) + return status; + + dash_on = TRUE; + } + else + { + dash_on = FALSE; + } + + _cairo_stroker_dash_step (&stroker->dash, step_length / sf); + segment.p1 = segment.p2; + } + + if (stroker->dash.dash_on && ! dash_on && + (fully_in_bounds || + _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) + { + + /* This segment ends on a transition to dash_on, compute a new face + * and add cap for the beginning of the next dash_on step. + */ + + status = _cairo_rectilinear_stroker_add_segment (stroker, + &segment.p1, + &segment.p1, + is_horizontal | JOIN); + if (unlikely (status)) + return status; + } + + stroker->current_point = *point; + stroker->open_sub_path = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_rectilinear_stroker_close_path (void *closure) +{ + cairo_rectilinear_stroker_t *stroker = closure; + cairo_status_t status; + + /* We don't draw anything for degenerate paths. */ + if (! stroker->open_sub_path) + return CAIRO_STATUS_SUCCESS; + + if (stroker->dash.dashed) { + status = _cairo_rectilinear_stroker_line_to_dashed (stroker, + &stroker->first_point); + } else { + status = _cairo_rectilinear_stroker_line_to (stroker, + &stroker->first_point); + } + if (unlikely (status)) + return status; + + stroker->open_sub_path = FALSE; + + if (stroker->dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (stroker); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + cairo_antialias_t antialias, + cairo_boxes_t *boxes) +{ + cairo_rectilinear_stroker_t rectilinear_stroker; + cairo_int_status_t status; + cairo_box_t box; + + assert (_cairo_path_fixed_stroke_is_rectilinear (path)); + + if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, + stroke_style, ctm, antialias, + boxes)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! rectilinear_stroker.dash.dashed && + _cairo_path_fixed_is_stroke_box (path, &box) && + /* if the segments overlap we need to feed them into the tessellator */ + box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x && + box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y) + { + cairo_box_t b; + + /* top */ + b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; + b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; + b.p1.y = box.p1.y - rectilinear_stroker.half_line_y; + b.p2.y = box.p1.y + rectilinear_stroker.half_line_y; + status = _cairo_boxes_add (boxes, antialias, &b); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + /* left (excluding top/bottom) */ + b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; + b.p2.x = box.p1.x + rectilinear_stroker.half_line_x; + b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; + b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; + status = _cairo_boxes_add (boxes, antialias, &b); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + /* right (excluding top/bottom) */ + b.p1.x = box.p2.x - rectilinear_stroker.half_line_x; + b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; + b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; + b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; + status = _cairo_boxes_add (boxes, antialias, &b); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + /* bottom */ + b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; + b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; + b.p1.y = box.p2.y - rectilinear_stroker.half_line_y; + b.p2.y = box.p2.y + rectilinear_stroker.half_line_y; + status = _cairo_boxes_add (boxes, antialias, &b); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + goto done; + } + + if (boxes->num_limits) { + _cairo_rectilinear_stroker_limit (&rectilinear_stroker, + boxes->limits, + boxes->num_limits); + } + + status = _cairo_path_fixed_interpret (path, + _cairo_rectilinear_stroker_move_to, + rectilinear_stroker.dash.dashed ? + _cairo_rectilinear_stroker_line_to_dashed : + _cairo_rectilinear_stroker_line_to, + NULL, + _cairo_rectilinear_stroker_close_path, + &rectilinear_stroker); + if (unlikely (status)) + goto BAIL; + + if (rectilinear_stroker.dash.dashed) + status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); + else + status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); + if (unlikely (status)) + goto BAIL; + + /* As we incrementally tessellate, we do not eliminate self-intersections */ + status = _cairo_bentley_ottmann_tessellate_boxes (boxes, + CAIRO_FILL_RULE_WINDING, + boxes); + if (unlikely (status)) + goto BAIL; + +done: + _cairo_rectilinear_stroker_fini (&rectilinear_stroker); + return CAIRO_STATUS_SUCCESS; + +BAIL: + _cairo_rectilinear_stroker_fini (&rectilinear_stroker); + _cairo_boxes_clear (boxes); + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-path-stroke-polygon.c b/gfx/cairo/cairo/src/cairo-path-stroke-polygon.c new file mode 100644 index 000000000000..3f7c49802801 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-stroke-polygon.c @@ -0,0 +1,1364 @@ +/* -*- 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 © 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#define _DEFAULT_SOURCE /* for hypot() */ +#include "cairoint.h" + +#include "cairo-box-inline.h" +#include "cairo-boxes-private.h" +#include "cairo-contour-inline.h" +#include "cairo-contour-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" + +#define DEBUG 0 + +struct stroker { + cairo_stroke_style_t style; + +#if DEBUG + cairo_contour_t path; +#endif + + struct stroke_contour { + /* Note that these are not strictly contours as they may intersect */ + cairo_contour_t contour; + } cw, ccw; + cairo_uint64_t contour_tolerance; + cairo_polygon_t *polygon; + + const cairo_matrix_t *ctm; + const cairo_matrix_t *ctm_inverse; + double tolerance; + double spline_cusp_tolerance; + double half_line_width; + cairo_bool_t ctm_det_positive; + + cairo_pen_t pen; + + cairo_point_t first_point; + + cairo_bool_t has_initial_sub_path; + + cairo_bool_t has_current_face; + cairo_stroke_face_t current_face; + + cairo_bool_t has_first_face; + cairo_stroke_face_t first_face; + + cairo_bool_t has_bounds; + cairo_box_t bounds; +}; + +static inline double +normalize_slope (double *dx, double *dy); + +static void +compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + struct stroker *stroker, + cairo_stroke_face_t *face); + +static cairo_uint64_t +point_distance_sq (const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int32_t dx = p1->x - p2->x; + int32_t dy = p1->y - p2->y; + return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy); +} + +static cairo_bool_t +within_tolerance (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_uint64_t tolerance) +{ + return FALSE; + return _cairo_int64_lt (point_distance_sq (p1, p2), tolerance); +} + +static void +contour_add_point (struct stroker *stroker, + struct stroke_contour *c, + const cairo_point_t *point) +{ + if (! within_tolerance (point, _cairo_contour_last_point (&c->contour), + stroker->contour_tolerance)) + _cairo_contour_add_point (&c->contour, point); + //*_cairo_contour_last_point (&c->contour) = *point; +} + +static void +translate_point (cairo_point_t *point, const cairo_point_t *offset) +{ + point->x += offset->x; + point->y += offset->y; +} + +static int +slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) +{ + double c = (dx1 * dy2 - dx2 * dy1); + + if (c > 0) return 1; + if (c < 0) return -1; + return 0; +} + +static inline int +range_step (int i, int step, int max) +{ + i += step; + if (i < 0) + i = max - 1; + if (i >= max) + i = 0; + return i; +} + +/* + * Construct a fan around the midpoint using the vertices from pen between + * inpt and outpt. + */ +static void +add_fan (struct stroker *stroker, + const cairo_slope_t *in_vector, + const cairo_slope_t *out_vector, + const cairo_point_t *midpt, + cairo_bool_t clockwise, + struct stroke_contour *c) +{ + cairo_pen_t *pen = &stroker->pen; + int start, stop; + + if (stroker->has_bounds && + ! _cairo_box_contains_point (&stroker->bounds, midpt)) + return; + + assert (stroker->pen.num_vertices); + + if (clockwise) { + _cairo_pen_find_active_cw_vertices (pen, + in_vector, out_vector, + &start, &stop); + while (start != stop) { + cairo_point_t p = *midpt; + translate_point (&p, &pen->vertices[start].point); + contour_add_point (stroker, c, &p); + + if (++start == pen->num_vertices) + start = 0; + } + } else { + _cairo_pen_find_active_ccw_vertices (pen, + in_vector, out_vector, + &start, &stop); + while (start != stop) { + cairo_point_t p = *midpt; + translate_point (&p, &pen->vertices[start].point); + contour_add_point (stroker, c, &p); + + if (start-- == 0) + start += pen->num_vertices; + } + } +} + +static int +join_is_clockwise (const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; +} + +static void +inner_join (struct stroker *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out, + int clockwise) +{ +#if 0 + cairo_point_t last; + const cairo_point_t *p, *outpt; + struct stroke_contour *inner; + cairo_int64_t d_p, d_last; + cairo_int64_t half_line_width; + cairo_bool_t negate; + + /* XXX line segments shorter than line-width */ + + if (clockwise) { + inner = &stroker->ccw; + outpt = &out->ccw; + negate = 1; + } else { + inner = &stroker->cw; + outpt = &out->cw; + negate = 0; + } + + half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5; + + /* On the inside, the previous end-point is always + * closer to the new face by definition. + */ + last = *_cairo_contour_last_point (&inner->contour); + d_last = distance_from_face (out, &last, negate); + _cairo_contour_remove_last_point (&inner->contour); + +prev: + if (inner->contour.chain.num_points == 0) { + contour_add_point (stroker, inner, outpt); + return; + } + p = _cairo_contour_last_point (&inner->contour); + d_p = distance_from_face (out, p, negate); + if (_cairo_int64_lt (d_p, half_line_width) && + !_cairo_int64_negative (distance_along_face (out, p))) + { + last = *p; + d_last = d_p; + _cairo_contour_remove_last_point (&inner->contour); + goto prev; + } + + compute_inner_joint (&last, d_last, p, d_p, half_line_width); + contour_add_point (stroker, inner, &last); +#else + const cairo_point_t *outpt; + struct stroke_contour *inner; + + if (clockwise) { + inner = &stroker->ccw; + outpt = &out->ccw; + } else { + inner = &stroker->cw; + outpt = &out->cw; + } + contour_add_point (stroker, inner, &in->point); + contour_add_point (stroker, inner, outpt); +#endif +} + +static void +inner_close (struct stroker *stroker, + const cairo_stroke_face_t *in, + cairo_stroke_face_t *out) +{ +#if 0 + cairo_point_t last; + const cairo_point_t *p, *outpt, *inpt; + struct stroke_contour *inner; + struct _cairo_contour_chain *chain; + + /* XXX line segments shorter than line-width */ + + if (join_is_clockwise (in, out)) { + inner = &stroker->ccw; + outpt = &in->ccw; + inpt = &out->ccw; + } else { + inner = &stroker->cw; + outpt = &in->cw; + inpt = &out->cw; + } + + if (inner->contour.chain.num_points == 0) { + contour_add_point (stroker, inner, &in->point); + contour_add_point (stroker, inner, inpt); + *_cairo_contour_first_point (&inner->contour) = + *_cairo_contour_last_point (&inner->contour); + return; + } + + line_width = stroker->style.line_width/2; + line_width *= CAIRO_FIXED_ONE; + + d_last = sign * distance_from_face (out, outpt); + last = *outpt; + + for (chain = &inner->contour.chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + p = &chain->points[i]; + if ((d_p = sign * distance_from_face (in, p)) >= line_width && + distance_from_edge (stroker, inpt, &last, p) < line_width) + { + goto out; + } + + if (p->x != last.x || p->y != last.y) { + last = *p; + d_last = d_p; + } + } + } +out: + + if (d_p != d_last) { + double dot = (line_width - d_last) / (d_p - d_last); + last.x += dot * (p->x - last.x); + last.y += dot * (p->y - last.y); + } + *_cairo_contour_last_point (&inner->contour) = last; + + for (chain = &inner->contour.chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + cairo_point_t *pp = &chain->points[i]; + if (pp == p) + return; + *pp = last; + } + } +#else + const cairo_point_t *inpt; + struct stroke_contour *inner; + + if (join_is_clockwise (in, out)) { + inner = &stroker->ccw; + inpt = &out->ccw; + } else { + inner = &stroker->cw; + inpt = &out->cw; + } + + contour_add_point (stroker, inner, &in->point); + contour_add_point (stroker, inner, inpt); + *_cairo_contour_first_point (&inner->contour) = + *_cairo_contour_last_point (&inner->contour); +#endif +} + +static void +outer_close (struct stroker *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + const cairo_point_t *inpt, *outpt; + struct stroke_contour *outer; + int clockwise; + + if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) + { + return; + } + + clockwise = join_is_clockwise (in, out); + if (clockwise) { + inpt = &in->cw; + outpt = &out->cw; + outer = &stroker->cw; + } else { + inpt = &in->ccw; + outpt = &out->ccw; + outer = &stroker->ccw; + } + + if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) { + *_cairo_contour_first_point (&outer->contour) = + *_cairo_contour_last_point (&outer->contour); + return; + } + + 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) + { + add_fan (stroker, + &in->dev_vector, &out->dev_vector, &in->point, + clockwise, outer); + break; + } + /* else fall through */ + case CAIRO_LINE_JOIN_MITER: + default: { + /* dot product of incoming slope vector with outgoing slope vector */ + double in_dot_out = in->dev_slope.x * out->dev_slope.x + + in->dev_slope.y * out->dev_slope.y; + double ml = stroker->style.miter_limit; + + /* Check the miter limit -- lines meeting at an acute angle + * can generate long miters, the limit converts them to bevel + * + * Consider the miter join formed when two line segments + * meet at an angle psi: + * + * /.\ + * /. .\ + * /./ \.\ + * /./psi\.\ + * + * We can zoom in on the right half of that to see: + * + * |\ + * | \ psi/2 + * | \ + * | \ + * | \ + * | \ + * miter \ + * length \ + * | \ + * | .\ + * | . \ + * |. line \ + * \ width \ + * \ \ + * + * + * The right triangle in that figure, (the line-width side is + * shown faintly with three '.' characters), gives us the + * following expression relating miter length, angle and line + * width: + * + * 1 /sin (psi/2) = miter_length / line_width + * + * The right-hand side of this relationship is the same ratio + * in which the miter limit (ml) is expressed. We want to know + * when the miter length is within the miter limit. That is + * when the following condition holds: + * + * 1/sin(psi/2) <= ml + * 1 <= ml sin(psi/2) + * 1 <= ml² sin²(psi/2) + * 2 <= ml² 2 sin²(psi/2) + * 2·sin²(psi/2) = 1-cos(psi) + * 2 <= ml² (1-cos(psi)) + * + * in · out = |in| |out| cos (psi) + * + * in and out are both unit vectors, so: + * + * in · out = cos (psi) + * + * 2 <= ml² (1 - in · out) + * + */ + if (2 <= ml * ml * (1 + in_dot_out)) { + double x1, y1, x2, y2; + double mx, my; + double dx1, dx2, dy1, dy2; + double ix, iy; + double fdx1, fdy1, fdx2, fdy2; + double mdx, mdy; + + /* + * we've got the points already transformed to device + * space, but need to do some computation with them and + * also need to transform the slope from user space to + * device space + */ + /* outer point of incoming line face */ + x1 = _cairo_fixed_to_double (inpt->x); + y1 = _cairo_fixed_to_double (inpt->y); + dx1 = in->dev_slope.x; + dy1 = in->dev_slope.y; + + /* outer point of outgoing line face */ + x2 = _cairo_fixed_to_double (outpt->x); + y2 = _cairo_fixed_to_double (outpt->y); + dx2 = out->dev_slope.x; + dy2 = out->dev_slope.y; + + /* + * Compute the location of the outer corner of the miter. + * That's pretty easy -- just the intersection of the two + * outer edges. We've got slopes and points on each + * of those edges. Compute my directly, then compute + * mx by using the edge with the larger dy; that avoids + * dividing by values close to zero. + */ + my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / + (dx1 * dy2 - dx2 * dy1)); + if (fabs (dy1) >= fabs (dy2)) + mx = (my - y1) * dx1 / dy1 + x1; + else + mx = (my - y2) * dx2 / dy2 + x2; + + /* + * When the two outer edges are nearly parallel, slight + * perturbations in the position of the outer points of the lines + * caused by representing them in fixed point form can cause the + * intersection point of the miter to move a large amount. If + * that moves the miter intersection from between the two faces, + * then draw a bevel instead. + */ + + ix = _cairo_fixed_to_double (in->point.x); + iy = _cairo_fixed_to_double (in->point.y); + + /* slope of one face */ + fdx1 = x1 - ix; fdy1 = y1 - iy; + + /* slope of the other face */ + fdx2 = x2 - ix; fdy2 = y2 - iy; + + /* slope from the intersection to the miter point */ + mdx = mx - ix; mdy = my - iy; + + /* + * Make sure the miter point line lies between the two + * faces by comparing the slopes + */ + if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + cairo_point_t p; + + p.x = _cairo_fixed_from_double (mx); + p.y = _cairo_fixed_from_double (my); + + *_cairo_contour_last_point (&outer->contour) = p; + *_cairo_contour_first_point (&outer->contour) = p; + return; + } + } + break; + } + + case CAIRO_LINE_JOIN_BEVEL: + break; + } + contour_add_point (stroker, outer, outpt); +} + +static void +outer_join (struct stroker *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out, + int clockwise) +{ + const cairo_point_t *inpt, *outpt; + struct stroke_contour *outer; + + if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) + { + return; + } + if (clockwise) { + inpt = &in->cw; + outpt = &out->cw; + outer = &stroker->cw; + } else { + inpt = &in->ccw; + outpt = &out->ccw; + outer = &stroker->ccw; + } + + 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); + break; + + case CAIRO_LINE_JOIN_MITER: + default: { + /* dot product of incoming slope vector with outgoing slope vector */ + double in_dot_out = in->dev_slope.x * out->dev_slope.x + + in->dev_slope.y * out->dev_slope.y; + double ml = stroker->style.miter_limit; + + /* Check the miter limit -- lines meeting at an acute angle + * can generate long miters, the limit converts them to bevel + * + * Consider the miter join formed when two line segments + * meet at an angle psi: + * + * /.\ + * /. .\ + * /./ \.\ + * /./psi\.\ + * + * We can zoom in on the right half of that to see: + * + * |\ + * | \ psi/2 + * | \ + * | \ + * | \ + * | \ + * miter \ + * length \ + * | \ + * | .\ + * | . \ + * |. line \ + * \ width \ + * \ \ + * + * + * The right triangle in that figure, (the line-width side is + * shown faintly with three '.' characters), gives us the + * following expression relating miter length, angle and line + * width: + * + * 1 /sin (psi/2) = miter_length / line_width + * + * The right-hand side of this relationship is the same ratio + * in which the miter limit (ml) is expressed. We want to know + * when the miter length is within the miter limit. That is + * when the following condition holds: + * + * 1/sin(psi/2) <= ml + * 1 <= ml sin(psi/2) + * 1 <= ml² sin²(psi/2) + * 2 <= ml² 2 sin²(psi/2) + * 2·sin²(psi/2) = 1-cos(psi) + * 2 <= ml² (1-cos(psi)) + * + * in · out = |in| |out| cos (psi) + * + * in and out are both unit vectors, so: + * + * in · out = cos (psi) + * + * 2 <= ml² (1 - in · out) + * + */ + if (2 <= ml * ml * (1 + in_dot_out)) { + double x1, y1, x2, y2; + double mx, my; + double dx1, dx2, dy1, dy2; + double ix, iy; + double fdx1, fdy1, fdx2, fdy2; + double mdx, mdy; + + /* + * we've got the points already transformed to device + * space, but need to do some computation with them and + * also need to transform the slope from user space to + * device space + */ + /* outer point of incoming line face */ + x1 = _cairo_fixed_to_double (inpt->x); + y1 = _cairo_fixed_to_double (inpt->y); + dx1 = in->dev_slope.x; + dy1 = in->dev_slope.y; + + /* outer point of outgoing line face */ + x2 = _cairo_fixed_to_double (outpt->x); + y2 = _cairo_fixed_to_double (outpt->y); + dx2 = out->dev_slope.x; + dy2 = out->dev_slope.y; + + /* + * Compute the location of the outer corner of the miter. + * That's pretty easy -- just the intersection of the two + * outer edges. We've got slopes and points on each + * of those edges. Compute my directly, then compute + * mx by using the edge with the larger dy; that avoids + * dividing by values close to zero. + */ + my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / + (dx1 * dy2 - dx2 * dy1)); + if (fabs (dy1) >= fabs (dy2)) + mx = (my - y1) * dx1 / dy1 + x1; + else + mx = (my - y2) * dx2 / dy2 + x2; + + /* + * When the two outer edges are nearly parallel, slight + * perturbations in the position of the outer points of the lines + * caused by representing them in fixed point form can cause the + * intersection point of the miter to move a large amount. If + * that moves the miter intersection from between the two faces, + * then draw a bevel instead. + */ + + ix = _cairo_fixed_to_double (in->point.x); + iy = _cairo_fixed_to_double (in->point.y); + + /* slope of one face */ + fdx1 = x1 - ix; fdy1 = y1 - iy; + + /* slope of the other face */ + fdx2 = x2 - ix; fdy2 = y2 - iy; + + /* slope from the intersection to the miter point */ + mdx = mx - ix; mdy = my - iy; + + /* + * Make sure the miter point line lies between the two + * faces by comparing the slopes + */ + if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + cairo_point_t p; + + p.x = _cairo_fixed_from_double (mx); + p.y = _cairo_fixed_from_double (my); + + *_cairo_contour_last_point (&outer->contour) = p; + return; + } + } + break; + } + + case CAIRO_LINE_JOIN_BEVEL: + break; + } + contour_add_point (stroker,outer, outpt); +} + +static void +add_cap (struct stroker *stroker, + const cairo_stroke_face_t *f, + struct stroke_contour *c) +{ + switch (stroker->style.line_cap) { + case CAIRO_LINE_CAP_ROUND: { + cairo_slope_t slope; + + slope.dx = -f->dev_vector.dx; + slope.dy = -f->dev_vector.dy; + + add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c); + break; + } + + case CAIRO_LINE_CAP_SQUARE: { + cairo_slope_t fvector; + cairo_point_t p; + double dx, dy; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->half_line_width; + dy *= stroker->half_line_width; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + p.x = f->ccw.x + fvector.dx; + p.y = f->ccw.y + fvector.dy; + contour_add_point (stroker, c, &p); + + p.x = f->cw.x + fvector.dx; + p.y = f->cw.y + fvector.dy; + contour_add_point (stroker, c, &p); + } + + case CAIRO_LINE_CAP_BUTT: + default: + break; + } + contour_add_point (stroker, c, &f->cw); +} + +static void +add_leading_cap (struct stroker *stroker, + const cairo_stroke_face_t *face, + struct stroke_contour *c) +{ + cairo_stroke_face_t reversed; + cairo_point_t t; + + reversed = *face; + + /* The initial cap needs an outward facing vector. Reverse everything */ + reversed.usr_vector.x = -reversed.usr_vector.x; + reversed.usr_vector.y = -reversed.usr_vector.y; + reversed.dev_vector.dx = -reversed.dev_vector.dx; + reversed.dev_vector.dy = -reversed.dev_vector.dy; + + t = reversed.cw; + reversed.cw = reversed.ccw; + reversed.ccw = t; + + add_cap (stroker, &reversed, c); +} + +static void +add_trailing_cap (struct stroker *stroker, + const cairo_stroke_face_t *face, + struct stroke_contour *c) +{ + add_cap (stroker, face, c); +} + +static inline double +normalize_slope (double *dx, double *dy) +{ + double dx0 = *dx, dy0 = *dy; + double mag; + + assert (dx0 != 0.0 || dy0 != 0.0); + + if (dx0 == 0.0) { + *dx = 0.0; + if (dy0 > 0.0) { + mag = dy0; + *dy = 1.0; + } else { + mag = -dy0; + *dy = -1.0; + } + } else if (dy0 == 0.0) { + *dy = 0.0; + if (dx0 > 0.0) { + mag = dx0; + *dx = 1.0; + } else { + mag = -dx0; + *dx = -1.0; + } + } else { + mag = hypot (dx0, dy0); + *dx = dx0 / mag; + *dy = dy0 / mag; + } + + return mag; +} + +static void +compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + struct stroker *stroker, + cairo_stroke_face_t *face) +{ + double face_dx, face_dy; + cairo_point_t offset_ccw, offset_cw; + double slope_dx, slope_dy; + + slope_dx = _cairo_fixed_to_double (dev_slope->dx); + slope_dy = _cairo_fixed_to_double (dev_slope->dy); + face->length = normalize_slope (&slope_dx, &slope_dy); + face->dev_slope.x = slope_dx; + face->dev_slope.y = slope_dy; + + /* + * rotate to get a line_width/2 vector along the face, note that + * the vector must be rotated the right direction in device space, + * but by 90° in user space. So, the rotation depends on + * whether the ctm reflects or not, and that can be determined + * by looking at the determinant of the matrix. + */ + if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { + /* Normalize the matrix! */ + cairo_matrix_transform_distance (stroker->ctm_inverse, + &slope_dx, &slope_dy); + normalize_slope (&slope_dx, &slope_dy); + + if (stroker->ctm_det_positive) { + face_dx = - slope_dy * stroker->half_line_width; + face_dy = slope_dx * stroker->half_line_width; + } else { + face_dx = slope_dy * stroker->half_line_width; + face_dy = - slope_dx * stroker->half_line_width; + } + + /* back to device space */ + cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); + } else { + face_dx = - slope_dy * stroker->half_line_width; + face_dy = slope_dx * stroker->half_line_width; + } + + offset_ccw.x = _cairo_fixed_from_double (face_dx); + offset_ccw.y = _cairo_fixed_from_double (face_dy); + offset_cw.x = -offset_ccw.x; + offset_cw.y = -offset_ccw.y; + + face->ccw = *point; + translate_point (&face->ccw, &offset_ccw); + + face->point = *point; + + face->cw = *point; + translate_point (&face->cw, &offset_cw); + + face->usr_vector.x = slope_dx; + face->usr_vector.y = slope_dy; + + face->dev_vector = *dev_slope; +} + +static void +add_caps (struct stroker *stroker) +{ + /* check for a degenerative sub_path */ + if (stroker->has_initial_sub_path && + ! stroker->has_first_face && + ! stroker->has_current_face && + stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) + { + /* pick an arbitrary slope to use */ + cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; + cairo_stroke_face_t face; + + /* arbitrarily choose first_point */ + compute_face (&stroker->first_point, &slope, stroker, &face); + + add_leading_cap (stroker, &face, &stroker->ccw); + add_trailing_cap (stroker, &face, &stroker->ccw); + + /* ensure the circle is complete */ + _cairo_contour_add_point (&stroker->ccw.contour, + _cairo_contour_first_point (&stroker->ccw.contour)); + + _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); + _cairo_contour_reset (&stroker->ccw.contour); + } else { + if (stroker->has_current_face) + add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw); + +#if DEBUG + { + FILE *file = fopen ("contours.txt", "a"); + _cairo_debug_print_contour (file, &stroker->path); + _cairo_debug_print_contour (file, &stroker->cw.contour); + _cairo_debug_print_contour (file, &stroker->ccw.contour); + fclose (file); + _cairo_contour_reset (&stroker->path); + } +#endif + + _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); + _cairo_contour_reset (&stroker->ccw.contour); + + if (stroker->has_first_face) { + _cairo_contour_add_point (&stroker->ccw.contour, + &stroker->first_face.cw); + add_leading_cap (stroker, &stroker->first_face, &stroker->ccw); +#if DEBUG + { + FILE *file = fopen ("contours.txt", "a"); + _cairo_debug_print_contour (file, &stroker->ccw.contour); + fclose (file); + } +#endif + + _cairo_polygon_add_contour (stroker->polygon, + &stroker->ccw.contour); + _cairo_contour_reset (&stroker->ccw.contour); + } + + _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); + _cairo_contour_reset (&stroker->cw.contour); + } +} + +static cairo_status_t +close_path (void *closure); + +static cairo_status_t +move_to (void *closure, + const cairo_point_t *point) +{ + struct stroker *stroker = closure; + + /* Cap the start and end of the previous sub path as needed */ + add_caps (stroker); + + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + stroker->has_initial_sub_path = FALSE; + + stroker->first_point = *point; + +#if DEBUG + _cairo_contour_add_point (&stroker->path, point); +#endif + + stroker->current_face.point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +line_to (void *closure, + const cairo_point_t *point) +{ + struct stroker *stroker = closure; + cairo_stroke_face_t start; + cairo_point_t *p1 = &stroker->current_face.point; + cairo_slope_t dev_slope; + + stroker->has_initial_sub_path = TRUE; + + if (p1->x == point->x && p1->y == point->y) + return CAIRO_STATUS_SUCCESS; + +#if DEBUG + _cairo_contour_add_point (&stroker->path, point); +#endif + + _cairo_slope_init (&dev_slope, p1, point); + compute_face (p1, &dev_slope, stroker, &start); + + if (stroker->has_current_face) { + int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, + &start.dev_vector); + if (clockwise) { + clockwise = clockwise < 0; + /* Join with final face from previous segment */ + if (! within_tolerance (&stroker->current_face.ccw, &start.ccw, + stroker->contour_tolerance) || + ! within_tolerance (&stroker->current_face.cw, &start.cw, + stroker->contour_tolerance)) + { + outer_join (stroker, &stroker->current_face, &start, clockwise); + inner_join (stroker, &stroker->current_face, &start, clockwise); + } + } + } else { + if (! stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = start; + stroker->has_first_face = TRUE; + } + stroker->has_current_face = TRUE; + + contour_add_point (stroker, &stroker->cw, &start.cw); + contour_add_point (stroker, &stroker->ccw, &start.ccw); + } + + stroker->current_face = start; + stroker->current_face.point = *point; + stroker->current_face.ccw.x += dev_slope.dx; + stroker->current_face.ccw.y += dev_slope.dy; + stroker->current_face.cw.x += dev_slope.dx; + stroker->current_face.cw.y += dev_slope.dy; + + contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); + contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +spline_to (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent) +{ + struct stroker *stroker = closure; + cairo_stroke_face_t face; + +#if DEBUG + _cairo_contour_add_point (&stroker->path, point); +#endif + if ((tangent->dx | tangent->dy) == 0) { + struct stroke_contour *outer; + cairo_point_t t; + int clockwise; + + face = stroker->current_face; + + face.usr_vector.x = -face.usr_vector.x; + face.usr_vector.y = -face.usr_vector.y; + face.dev_vector.dx = -face.dev_vector.dx; + face.dev_vector.dy = -face.dev_vector.dy; + + t = face.cw; + face.cw = face.ccw; + face.ccw = t; + + clockwise = join_is_clockwise (&stroker->current_face, &face); + if (clockwise) { + outer = &stroker->cw; + } else { + outer = &stroker->ccw; + } + + add_fan (stroker, + &stroker->current_face.dev_vector, + &face.dev_vector, + &stroker->current_face.point, + clockwise, outer); + } else { + compute_face (point, tangent, stroker, &face); + + if ((face.dev_slope.x * stroker->current_face.dev_slope.x + + face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) + { + struct stroke_contour *outer; + int clockwise = join_is_clockwise (&stroker->current_face, &face); + + stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x; + stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y; + contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); + + stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x; + stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y; + contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); + + if (clockwise) { + outer = &stroker->cw; + } else { + outer = &stroker->ccw; + } + add_fan (stroker, + &stroker->current_face.dev_vector, + &face.dev_vector, + &stroker->current_face.point, + clockwise, outer); + } + + contour_add_point (stroker, &stroker->cw, &face.cw); + contour_add_point (stroker, &stroker->ccw, &face.ccw); + } + + stroker->current_face = face; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + struct stroker *stroker = closure; + cairo_spline_t spline; + cairo_stroke_face_t face; + + if (stroker->has_bounds && + ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, + &stroker->bounds)) + return line_to (closure, d); + + if (! _cairo_spline_init (&spline, spline_to, stroker, + &stroker->current_face.point, b, c, d)) + return line_to (closure, d); + + compute_face (&stroker->current_face.point, &spline.initial_slope, + stroker, &face); + + if (stroker->has_current_face) { + int clockwise = join_is_clockwise (&stroker->current_face, &face); + /* Join with final face from previous segment */ + outer_join (stroker, &stroker->current_face, &face, clockwise); + inner_join (stroker, &stroker->current_face, &face, clockwise); + } else { + if (! stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = face; + stroker->has_first_face = TRUE; + } + stroker->has_current_face = TRUE; + + contour_add_point (stroker, &stroker->cw, &face.cw); + contour_add_point (stroker, &stroker->ccw, &face.ccw); + } + stroker->current_face = face; + + return _cairo_spline_decompose (&spline, stroker->tolerance); +} + +static cairo_status_t +close_path (void *closure) +{ + struct stroker *stroker = closure; + cairo_status_t status; + + status = line_to (stroker, &stroker->first_point); + if (unlikely (status)) + return status; + + if (stroker->has_first_face && stroker->has_current_face) { + /* Join first and final faces of sub path */ + outer_close (stroker, &stroker->current_face, &stroker->first_face); + inner_close (stroker, &stroker->current_face, &stroker->first_face); +#if 0 + *_cairo_contour_first_point (&stroker->ccw.contour) = + *_cairo_contour_last_point (&stroker->ccw.contour); + + *_cairo_contour_first_point (&stroker->cw.contour) = + *_cairo_contour_last_point (&stroker->cw.contour); +#endif + + _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); + _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); + +#if DEBUG + { + FILE *file = fopen ("contours.txt", "a"); + _cairo_debug_print_contour (file, &stroker->path); + _cairo_debug_print_contour (file, &stroker->cw.contour); + _cairo_debug_print_contour (file, &stroker->ccw.contour); + fclose (file); + + _cairo_contour_reset (&stroker->path); + } +#endif + _cairo_contour_reset (&stroker->cw.contour); + _cairo_contour_reset (&stroker->ccw.contour); + } else { + /* Cap the start and end of the sub path as needed */ + add_caps (stroker); + } + + stroker->has_initial_sub_path = FALSE; + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_fixed_stroke_to_polygon (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_polygon_t *polygon) +{ + struct stroker stroker; + cairo_status_t status; + + if (style->num_dashes) { + return _cairo_path_fixed_stroke_dashed_to_polygon (path, + style, + ctm, + ctm_inverse, + tolerance, + polygon); + } + + stroker.has_bounds = polygon->num_limits; + if (stroker.has_bounds) { + /* Extend the bounds in each direction to account for the maximum area + * we might generate trapezoids, to capture line segments that are + * outside of the bounds but which might generate rendering that's + * within bounds. + */ + double dx, dy; + cairo_fixed_t fdx, fdy; + int i; + + stroker.bounds = polygon->limits[0]; + for (i = 1; i < polygon->num_limits; i++) + _cairo_box_add_box (&stroker.bounds, &polygon->limits[i]); + + _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); + fdx = _cairo_fixed_from_double (dx); + fdy = _cairo_fixed_from_double (dy); + + stroker.bounds.p1.x -= fdx; + stroker.bounds.p2.x += fdx; + stroker.bounds.p1.y -= fdy; + stroker.bounds.p2.y += fdy; + } + + stroker.style = *style; + stroker.ctm = ctm; + 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. + */ + 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; + stroker.ctm_det_positive = + _cairo_matrix_compute_determinant (ctm) >= 0.0; + + stroker.pen.num_vertices = 0; + if (path->has_curve_to || + style->line_join == CAIRO_LINE_JOIN_ROUND || + style->line_cap == CAIRO_LINE_CAP_ROUND) { + status = _cairo_pen_init (&stroker.pen, + stroker.half_line_width, + tolerance, ctm); + if (unlikely (status)) + return status; + + /* If the line width is so small that the pen is reduced to a + single point, then we have nothing to do. */ + if (stroker.pen.num_vertices <= 1) + return CAIRO_STATUS_SUCCESS; + } + + stroker.has_current_face = FALSE; + stroker.has_first_face = FALSE; + stroker.has_initial_sub_path = FALSE; + +#if DEBUG + remove ("contours.txt"); + remove ("polygons.txt"); + _cairo_contour_init (&stroker.path, 0); +#endif + _cairo_contour_init (&stroker.cw.contour, 1); + _cairo_contour_init (&stroker.ccw.contour, -1); + tolerance *= CAIRO_FIXED_ONE; + tolerance *= tolerance; + stroker.contour_tolerance = tolerance; + stroker.polygon = polygon; + + status = _cairo_path_fixed_interpret (path, + move_to, + line_to, + curve_to, + close_path, + &stroker); + /* Cap the start and end of the final sub path as needed */ + if (likely (status == CAIRO_STATUS_SUCCESS)) + add_caps (&stroker); + + _cairo_contour_fini (&stroker.cw.contour); + _cairo_contour_fini (&stroker.ccw.contour); + if (stroker.pen.num_vertices) + _cairo_pen_fini (&stroker.pen); + +#if DEBUG + { + FILE *file = fopen ("polygons.txt", "a"); + _cairo_debug_print_polygon (file, polygon); + fclose (file); + } +#endif + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-path-stroke-traps.c b/gfx/cairo/cairo/src/cairo-path-stroke-traps.c new file mode 100644 index 000000000000..1363ffa86e02 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-stroke-traps.c @@ -0,0 +1,1148 @@ +/* -*- 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 © 2013 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-box-inline.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" +#include "cairo-stroke-dash-private.h" +#include "cairo-traps-private.h" + +#include + +struct stroker { + const cairo_stroke_style_t *style; + + const cairo_matrix_t *ctm; + const cairo_matrix_t *ctm_inverse; + double spline_cusp_tolerance; + double half_line_width; + double tolerance; + double ctm_determinant; + cairo_bool_t ctm_det_positive; + cairo_line_join_t line_join; + + cairo_traps_t *traps; + + cairo_pen_t pen; + + cairo_point_t first_point; + + cairo_bool_t has_initial_sub_path; + + cairo_bool_t has_current_face; + cairo_stroke_face_t current_face; + + cairo_bool_t has_first_face; + cairo_stroke_face_t first_face; + + cairo_stroker_dash_t dash; + + cairo_bool_t has_bounds; + cairo_box_t tight_bounds; + cairo_box_t line_bounds; + cairo_box_t join_bounds; +}; + +static cairo_status_t +stroker_init (struct stroker *stroker, + 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_traps_t *traps) +{ + cairo_status_t status; + + stroker->style = style; + stroker->ctm = ctm; + stroker->ctm_inverse = NULL; + if (! _cairo_matrix_is_identity (ctm_inverse)) + stroker->ctm_inverse = ctm_inverse; + stroker->line_join = style->line_join; + stroker->half_line_width = style->line_width / 2.0; + 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. + */ + 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; + + stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); + stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; + + status = _cairo_pen_init (&stroker->pen, + stroker->half_line_width, + tolerance, ctm); + if (unlikely (status)) + return status; + + stroker->has_current_face = FALSE; + stroker->has_first_face = FALSE; + stroker->has_initial_sub_path = FALSE; + + _cairo_stroker_dash_init (&stroker->dash, style); + + stroker->has_bounds = traps->num_limits; + if (stroker->has_bounds) { + /* Extend the bounds in each direction to account for the maximum area + * we might generate trapezoids, to capture line segments that are outside + * of the bounds but which might generate rendering that's within bounds. + */ + double dx, dy; + cairo_fixed_t fdx, fdy; + + stroker->tight_bounds = traps->bounds; + + _cairo_stroke_style_max_distance_from_path (stroker->style, path, + stroker->ctm, &dx, &dy); + + _cairo_stroke_style_max_line_distance_from_path (stroker->style, path, + stroker->ctm, &dx, &dy); + + fdx = _cairo_fixed_from_double (dx); + fdy = _cairo_fixed_from_double (dy); + + stroker->line_bounds = stroker->tight_bounds; + stroker->line_bounds.p1.x -= fdx; + stroker->line_bounds.p2.x += fdx; + stroker->line_bounds.p1.y -= fdy; + stroker->line_bounds.p2.y += fdy; + + _cairo_stroke_style_max_join_distance_from_path (stroker->style, path, + stroker->ctm, &dx, &dy); + + fdx = _cairo_fixed_from_double (dx); + fdy = _cairo_fixed_from_double (dy); + + stroker->join_bounds = stroker->tight_bounds; + stroker->join_bounds.p1.x -= fdx; + stroker->join_bounds.p2.x += fdx; + stroker->join_bounds.p1.y -= fdy; + stroker->join_bounds.p2.y += fdy; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +stroker_fini (struct stroker *stroker) +{ + _cairo_pen_fini (&stroker->pen); +} + +static void +translate_point (cairo_point_t *point, cairo_point_t *offset) +{ + point->x += offset->x; + point->y += offset->y; +} + +static int +join_is_clockwise (const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; +} + +static int +slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) +{ + double c = dx1 * dy2 - dx2 * dy1; + if (c > 0) return 1; + if (c < 0) return -1; + return 0; +} + +static cairo_bool_t +stroker_intersects_join (const struct stroker *stroker, + const cairo_point_t *in, + const cairo_point_t *out) +{ + cairo_line_t segment; + + if (! stroker->has_bounds) + return TRUE; + + segment.p1 = *in; + segment.p2 = *out; + return _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment); +} + +static void +join (struct stroker *stroker, + cairo_stroke_face_t *in, + cairo_stroke_face_t *out) +{ + int clockwise = join_is_clockwise (out, in); + cairo_point_t *inpt, *outpt; + + if (in->cw.x == out->cw.x && + in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && + in->ccw.y == out->ccw.y) + { + return; + } + + if (clockwise) { + inpt = &in->ccw; + outpt = &out->ccw; + } else { + inpt = &in->cw; + outpt = &out->cw; + } + + if (! stroker_intersects_join (stroker, inpt, outpt)) + return; + + switch (stroker->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) + { + int start, stop; + cairo_point_t tri[3], edges[4]; + cairo_pen_t *pen = &stroker->pen; + + edges[0] = in->cw; + edges[1] = in->ccw; + tri[0] = in->point; + tri[1] = *inpt; + if (clockwise) { + _cairo_pen_find_active_ccw_vertices (pen, + &in->dev_vector, &out->dev_vector, + &start, &stop); + while (start != stop) { + tri[2] = in->point; + translate_point (&tri[2], &pen->vertices[start].point); + edges[2] = in->point; + edges[3] = tri[2]; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, + tri, edges); + tri[1] = tri[2]; + edges[0] = edges[2]; + edges[1] = edges[3]; + + if (start-- == 0) + start += pen->num_vertices; + } + } else { + _cairo_pen_find_active_cw_vertices (pen, + &in->dev_vector, &out->dev_vector, + &start, &stop); + while (start != stop) { + tri[2] = in->point; + translate_point (&tri[2], &pen->vertices[start].point); + edges[2] = in->point; + edges[3] = tri[2]; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, + tri, edges); + tri[1] = tri[2]; + edges[0] = edges[2]; + edges[1] = edges[3]; + + if (++start == pen->num_vertices) + start = 0; + } + } + tri[2] = *outpt; + edges[2] = out->cw; + edges[3] = out->ccw; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, + tri, edges); + } else { + cairo_point_t t[] = { { in->point.x, in->point.y}, { inpt->x, inpt->y }, { outpt->x, outpt->y } }; + cairo_point_t e[] = { { in->cw.x, in->cw.y}, { in->ccw.x, in->ccw.y }, + { out->cw.x, out->cw.y}, { out->ccw.x, out->ccw.y } }; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, t, e); + } + break; + + case CAIRO_LINE_JOIN_MITER: + default: { + /* dot product of incoming slope vector with outgoing slope vector */ + double in_dot_out = (-in->usr_vector.x * out->usr_vector.x + + -in->usr_vector.y * out->usr_vector.y); + double ml = stroker->style->miter_limit; + + /* Check the miter limit -- lines meeting at an acute angle + * can generate long miters, the limit converts them to bevel + * + * Consider the miter join formed when two line segments + * meet at an angle psi: + * + * /.\ + * /. .\ + * /./ \.\ + * /./psi\.\ + * + * We can zoom in on the right half of that to see: + * + * |\ + * | \ psi/2 + * | \ + * | \ + * | \ + * | \ + * miter \ + * length \ + * | \ + * | .\ + * | . \ + * |. line \ + * \ width \ + * \ \ + * + * + * The right triangle in that figure, (the line-width side is + * shown faintly with three '.' characters), gives us the + * following expression relating miter length, angle and line + * width: + * + * 1 /sin (psi/2) = miter_length / line_width + * + * The right-hand side of this relationship is the same ratio + * in which the miter limit (ml) is expressed. We want to know + * when the miter length is within the miter limit. That is + * when the following condition holds: + * + * 1/sin(psi/2) <= ml + * 1 <= ml sin(psi/2) + * 1 <= ml² sin²(psi/2) + * 2 <= ml² 2 sin²(psi/2) + * 2·sin²(psi/2) = 1-cos(psi) + * 2 <= ml² (1-cos(psi)) + * + * in · out = |in| |out| cos (psi) + * + * in and out are both unit vectors, so: + * + * in · out = cos (psi) + * + * 2 <= ml² (1 - in · out) + * + */ + if (2 <= ml * ml * (1 - in_dot_out)) { + double x1, y1, x2, y2; + double mx, my; + double dx1, dx2, dy1, dy2; + cairo_point_t outer; + cairo_point_t quad[4]; + double ix, iy; + double fdx1, fdy1, fdx2, fdy2; + double mdx, mdy; + + /* + * we've got the points already transformed to device + * space, but need to do some computation with them and + * also need to transform the slope from user space to + * device space + */ + /* outer point of incoming line face */ + x1 = _cairo_fixed_to_double (inpt->x); + y1 = _cairo_fixed_to_double (inpt->y); + dx1 = in->usr_vector.x; + dy1 = in->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); + + /* outer point of outgoing line face */ + x2 = _cairo_fixed_to_double (outpt->x); + y2 = _cairo_fixed_to_double (outpt->y); + dx2 = out->usr_vector.x; + dy2 = out->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); + + /* + * Compute the location of the outer corner of the miter. + * That's pretty easy -- just the intersection of the two + * outer edges. We've got slopes and points on each + * of those edges. Compute my directly, then compute + * mx by using the edge with the larger dy; that avoids + * dividing by values close to zero. + */ + my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / + (dx1 * dy2 - dx2 * dy1)); + if (fabs (dy1) >= fabs (dy2)) + mx = (my - y1) * dx1 / dy1 + x1; + else + mx = (my - y2) * dx2 / dy2 + x2; + + /* + * When the two outer edges are nearly parallel, slight + * perturbations in the position of the outer points of the lines + * caused by representing them in fixed point form can cause the + * intersection point of the miter to move a large amount. If + * that moves the miter intersection from between the two faces, + * then draw a bevel instead. + */ + + ix = _cairo_fixed_to_double (in->point.x); + iy = _cairo_fixed_to_double (in->point.y); + + /* slope of one face */ + fdx1 = x1 - ix; fdy1 = y1 - iy; + + /* slope of the other face */ + fdx2 = x2 - ix; fdy2 = y2 - iy; + + /* slope from the intersection to the miter point */ + mdx = mx - ix; mdy = my - iy; + + /* + * Make sure the miter point line lies between the two + * faces by comparing the slopes + */ + if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + /* + * Draw the quadrilateral + */ + outer.x = _cairo_fixed_from_double (mx); + outer.y = _cairo_fixed_from_double (my); + + quad[0] = in->point; + quad[1] = *inpt; + quad[2] = outer; + quad[3] = *outpt; + + _cairo_traps_tessellate_convex_quad (stroker->traps, quad); + break; + } + } + } + /* fall through ... */ + case CAIRO_LINE_JOIN_BEVEL: { + cairo_point_t t[] = { { in->point.x, in->point.y }, { inpt->x, inpt->y }, { outpt->x, outpt->y } }; + cairo_point_t e[] = { { in->cw.x, in->cw.y }, { in->ccw.x, in->ccw.y }, + { out->cw.x, out->cw.y }, { out->ccw.x, out->ccw.y } }; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, t, e); + break; + } + } +} + +static void +add_cap (struct stroker *stroker, cairo_stroke_face_t *f) +{ + switch (stroker->style->line_cap) { + case CAIRO_LINE_CAP_ROUND: { + int start, stop; + cairo_slope_t in_slope, out_slope; + cairo_point_t tri[3], edges[4]; + cairo_pen_t *pen = &stroker->pen; + + in_slope = f->dev_vector; + out_slope.dx = -in_slope.dx; + out_slope.dy = -in_slope.dy; + _cairo_pen_find_active_cw_vertices (pen, &in_slope, &out_slope, + &start, &stop); + edges[0] = f->cw; + edges[1] = f->ccw; + tri[0] = f->point; + tri[1] = f->cw; + while (start != stop) { + tri[2] = f->point; + translate_point (&tri[2], &pen->vertices[start].point); + edges[2] = f->point; + edges[3] = tri[2]; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, + tri, edges); + + tri[1] = tri[2]; + edges[0] = edges[2]; + edges[1] = edges[3]; + if (++start == pen->num_vertices) + start = 0; + } + tri[2] = f->ccw; + edges[2] = f->cw; + edges[3] = f->ccw; + _cairo_traps_tessellate_triangle_with_edges (stroker->traps, + tri, edges); + break; + } + + case CAIRO_LINE_CAP_SQUARE: { + double dx, dy; + cairo_slope_t fvector; + cairo_point_t quad[4]; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->half_line_width; + dy *= stroker->half_line_width; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + quad[0] = f->cw; + quad[1].x = f->cw.x + fvector.dx; + quad[1].y = f->cw.y + fvector.dy; + quad[2].x = f->ccw.x + fvector.dx; + quad[2].y = f->ccw.y + fvector.dy; + quad[3] = f->ccw; + + _cairo_traps_tessellate_convex_quad (stroker->traps, quad); + break; + } + + case CAIRO_LINE_CAP_BUTT: + default: + break; + } +} + +static void +add_leading_cap (struct stroker *stroker, + cairo_stroke_face_t *face) +{ + cairo_stroke_face_t reversed; + cairo_point_t t; + + reversed = *face; + + /* The initial cap needs an outward facing vector. Reverse everything */ + reversed.usr_vector.x = -reversed.usr_vector.x; + reversed.usr_vector.y = -reversed.usr_vector.y; + reversed.dev_vector.dx = -reversed.dev_vector.dx; + reversed.dev_vector.dy = -reversed.dev_vector.dy; + t = reversed.cw; + reversed.cw = reversed.ccw; + reversed.ccw = t; + + add_cap (stroker, &reversed); +} + +static void +add_trailing_cap (struct stroker *stroker, cairo_stroke_face_t *face) +{ + add_cap (stroker, face); +} + +static inline double +normalize_slope (double *dx, double *dy) +{ + double dx0 = *dx, dy0 = *dy; + + if (dx0 == 0.0 && dy0 == 0.0) + return 0; + + if (dx0 == 0.0) { + *dx = 0.0; + if (dy0 > 0.0) { + *dy = 1.0; + return dy0; + } else { + *dy = -1.0; + return -dy0; + } + } else if (dy0 == 0.0) { + *dy = 0.0; + if (dx0 > 0.0) { + *dx = 1.0; + return dx0; + } else { + *dx = -1.0; + return -dx0; + } + } else { + double mag = hypot (dx0, dy0); + *dx = dx0 / mag; + *dy = dy0 / mag; + return mag; + } +} + +static void +compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + struct stroker *stroker, + cairo_stroke_face_t *face) +{ + double face_dx, face_dy; + cairo_point_t offset_ccw, offset_cw; + double slope_dx, slope_dy; + + slope_dx = _cairo_fixed_to_double (dev_slope->dx); + slope_dy = _cairo_fixed_to_double (dev_slope->dy); + face->length = normalize_slope (&slope_dx, &slope_dy); + face->dev_slope.x = slope_dx; + face->dev_slope.y = slope_dy; + + /* + * rotate to get a line_width/2 vector along the face, note that + * the vector must be rotated the right direction in device space, + * but by 90° in user space. So, the rotation depends on + * whether the ctm reflects or not, and that can be determined + * by looking at the determinant of the matrix. + */ + if (stroker->ctm_inverse) { + cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); + normalize_slope (&slope_dx, &slope_dy); + + if (stroker->ctm_det_positive) { + face_dx = - slope_dy * stroker->half_line_width; + face_dy = slope_dx * stroker->half_line_width; + } else { + face_dx = slope_dy * stroker->half_line_width; + face_dy = - slope_dx * stroker->half_line_width; + } + + /* back to device space */ + cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); + } else { + face_dx = - slope_dy * stroker->half_line_width; + face_dy = slope_dx * stroker->half_line_width; + } + + offset_ccw.x = _cairo_fixed_from_double (face_dx); + offset_ccw.y = _cairo_fixed_from_double (face_dy); + offset_cw.x = -offset_ccw.x; + offset_cw.y = -offset_ccw.y; + + face->ccw = *point; + translate_point (&face->ccw, &offset_ccw); + + face->point = *point; + + face->cw = *point; + translate_point (&face->cw, &offset_cw); + + face->usr_vector.x = slope_dx; + face->usr_vector.y = slope_dy; + + face->dev_vector = *dev_slope; +} + +static void +add_caps (struct stroker *stroker) +{ + /* check for a degenerative sub_path */ + if (stroker->has_initial_sub_path && + !stroker->has_first_face && + !stroker->has_current_face && + stroker->style->line_cap == CAIRO_LINE_CAP_ROUND) + { + /* pick an arbitrary slope to use */ + cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; + cairo_stroke_face_t face; + + /* arbitrarily choose first_point + * first_point and current_point should be the same */ + compute_face (&stroker->first_point, &slope, stroker, &face); + + add_leading_cap (stroker, &face); + add_trailing_cap (stroker, &face); + } + + if (stroker->has_first_face) + add_leading_cap (stroker, &stroker->first_face); + + if (stroker->has_current_face) + add_trailing_cap (stroker, &stroker->current_face); +} + +static cairo_bool_t +stroker_intersects_edge (const struct stroker *stroker, + const cairo_stroke_face_t *start, + const cairo_stroke_face_t *end) +{ + cairo_box_t box; + + if (! stroker->has_bounds) + return TRUE; + + if (_cairo_box_contains_point (&stroker->tight_bounds, &start->cw)) + return TRUE; + box.p2 = box.p1 = start->cw; + + if (_cairo_box_contains_point (&stroker->tight_bounds, &start->ccw)) + return TRUE; + _cairo_box_add_point (&box, &start->ccw); + + if (_cairo_box_contains_point (&stroker->tight_bounds, &end->cw)) + return TRUE; + _cairo_box_add_point (&box, &end->cw); + + if (_cairo_box_contains_point (&stroker->tight_bounds, &end->ccw)) + return TRUE; + _cairo_box_add_point (&box, &end->ccw); + + return (box.p2.x > stroker->tight_bounds.p1.x && + box.p1.x < stroker->tight_bounds.p2.x && + box.p2.y > stroker->tight_bounds.p1.y && + box.p1.y < stroker->tight_bounds.p2.y); +} + +static void +add_sub_edge (struct stroker *stroker, + const cairo_point_t *p1, const cairo_point_t *p2, + const cairo_slope_t *dev_slope, + cairo_stroke_face_t *start, cairo_stroke_face_t *end) +{ + cairo_point_t rectangle[4]; + + compute_face (p1, dev_slope, stroker, start); + + *end = *start; + end->point = *p2; + rectangle[0].x = p2->x - p1->x; + rectangle[0].y = p2->y - p1->y; + translate_point (&end->ccw, &rectangle[0]); + translate_point (&end->cw, &rectangle[0]); + + if (p1->x == p2->x && p1->y == p2->y) + return; + + if (! stroker_intersects_edge (stroker, start, end)) + return; + + rectangle[0] = start->cw; + rectangle[1] = start->ccw; + rectangle[2] = end->ccw; + rectangle[3] = end->cw; + + _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle); +} + +static cairo_status_t +move_to (void *closure, const cairo_point_t *point) +{ + struct stroker *stroker = closure; + + /* Cap the start and end of the previous sub path as needed */ + add_caps (stroker); + + stroker->first_point = *point; + stroker->current_face.point = *point; + + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + stroker->has_initial_sub_path = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +move_to_dashed (void *closure, const cairo_point_t *point) +{ + /* reset the dash pattern for new sub paths */ + struct stroker *stroker = closure; + + _cairo_stroker_dash_start (&stroker->dash); + return move_to (closure, point); +} + +static cairo_status_t +line_to (void *closure, const cairo_point_t *point) +{ + struct stroker *stroker = closure; + cairo_stroke_face_t start, end; + const cairo_point_t *p1 = &stroker->current_face.point; + const cairo_point_t *p2 = point; + cairo_slope_t dev_slope; + + stroker->has_initial_sub_path = TRUE; + + if (p1->x == p2->x && p1->y == p2->y) + return CAIRO_STATUS_SUCCESS; + + _cairo_slope_init (&dev_slope, p1, p2); + add_sub_edge (stroker, p1, p2, &dev_slope, &start, &end); + + if (stroker->has_current_face) { + /* Join with final face from previous segment */ + join (stroker, &stroker->current_face, &start); + } else if (!stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = start; + stroker->has_first_face = TRUE; + } + stroker->current_face = end; + stroker->has_current_face = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Dashed lines. Cap each dash end, join around turns when on + */ +static cairo_status_t +line_to_dashed (void *closure, const cairo_point_t *point) +{ + struct stroker *stroker = closure; + double mag, remain, step_length = 0; + double slope_dx, slope_dy; + double dx2, dy2; + cairo_stroke_face_t sub_start, sub_end; + const cairo_point_t *p1 = &stroker->current_face.point; + const cairo_point_t *p2 = point; + cairo_slope_t dev_slope; + cairo_line_t segment; + cairo_bool_t fully_in_bounds; + + stroker->has_initial_sub_path = stroker->dash.dash_starts_on; + + if (p1->x == p2->x && p1->y == p2->y) + return CAIRO_STATUS_SUCCESS; + + fully_in_bounds = TRUE; + if (stroker->has_bounds && + (! _cairo_box_contains_point (&stroker->join_bounds, p1) || + ! _cairo_box_contains_point (&stroker->join_bounds, p2))) + { + fully_in_bounds = FALSE; + } + + _cairo_slope_init (&dev_slope, p1, p2); + + slope_dx = _cairo_fixed_to_double (p2->x - p1->x); + slope_dy = _cairo_fixed_to_double (p2->y - p1->y); + + if (stroker->ctm_inverse) + cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); + mag = normalize_slope (&slope_dx, &slope_dy); + if (mag <= DBL_EPSILON) + return CAIRO_STATUS_SUCCESS; + + remain = mag; + segment.p1 = *p1; + while (remain) { + step_length = MIN (stroker->dash.dash_remain, remain); + remain -= step_length; + dx2 = slope_dx * (mag - remain); + dy2 = slope_dy * (mag - remain); + cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); + segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; + segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; + + if (stroker->dash.dash_on && + (fully_in_bounds || + (! stroker->has_first_face && stroker->dash.dash_starts_on) || + _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment))) + { + add_sub_edge (stroker, + &segment.p1, &segment.p2, + &dev_slope, + &sub_start, &sub_end); + + if (stroker->has_current_face) { + /* Join with final face from previous segment */ + join (stroker, &stroker->current_face, &sub_start); + + stroker->has_current_face = FALSE; + } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = sub_start; + stroker->has_first_face = TRUE; + } else { + /* Cap dash start if not connecting to a previous segment */ + add_leading_cap (stroker, &sub_start); + } + + if (remain) { + /* Cap dash end if not at end of segment */ + add_trailing_cap (stroker, &sub_end); + } else { + stroker->current_face = sub_end; + stroker->has_current_face = TRUE; + } + } else { + if (stroker->has_current_face) { + /* Cap final face from previous segment */ + add_trailing_cap (stroker, &stroker->current_face); + + stroker->has_current_face = FALSE; + } + } + + _cairo_stroker_dash_step (&stroker->dash, step_length); + segment.p1 = segment.p2; + } + + if (stroker->dash.dash_on && ! stroker->has_current_face) { + /* This segment ends on a transition to dash_on, compute a new face + * and add cap for the beginning of the next dash_on step. + * + * Note: this will create a degenerate cap if this is not the last line + * in the path. Whether this behaviour is desirable or not is debatable. + * On one side these degenerate caps can not be reproduced with regular + * path stroking. + * On the other hand, Acroread 7 also produces the degenerate caps. + */ + compute_face (point, &dev_slope, stroker, &stroker->current_face); + + add_leading_cap (stroker, &stroker->current_face); + + stroker->has_current_face = TRUE; + } else + stroker->current_face.point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +spline_to (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent) +{ + struct stroker *stroker = closure; + cairo_stroke_face_t face; + + if ((tangent->dx | tangent->dy) == 0) { + cairo_point_t t; + + face = stroker->current_face; + + face.usr_vector.x = -face.usr_vector.x; + face.usr_vector.y = -face.usr_vector.y; + face.dev_slope.x = -face.dev_slope.x; + face.dev_slope.y = -face.dev_slope.y; + face.dev_vector.dx = -face.dev_vector.dx; + face.dev_vector.dy = -face.dev_vector.dy; + + t = face.cw; + face.cw = face.ccw; + face.ccw = t; + + join (stroker, &stroker->current_face, &face); + } else { + cairo_point_t rectangle[4]; + + compute_face (&stroker->current_face.point, tangent, stroker, &face); + join (stroker, &stroker->current_face, &face); + + rectangle[0] = face.cw; + rectangle[1] = face.ccw; + + rectangle[2].x = point->x - face.point.x; + rectangle[2].y = point->y - face.point.y; + face.point = *point; + translate_point (&face.ccw, &rectangle[2]); + translate_point (&face.cw, &rectangle[2]); + + rectangle[2] = face.ccw; + rectangle[3] = face.cw; + + _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle); + } + + stroker->current_face = face; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + struct stroker *stroker = closure; + cairo_line_join_t line_join_save; + cairo_spline_t spline; + cairo_stroke_face_t face; + cairo_status_t status; + + if (stroker->has_bounds && + ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, + &stroker->line_bounds)) + return line_to (closure, d); + + if (! _cairo_spline_init (&spline, spline_to, stroker, + &stroker->current_face.point, b, c, d)) + return line_to (closure, d); + + compute_face (&stroker->current_face.point, &spline.initial_slope, + stroker, &face); + + if (stroker->has_current_face) { + /* Join with final face from previous segment */ + join (stroker, &stroker->current_face, &face); + } else { + if (! stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = face; + stroker->has_first_face = TRUE; + } + stroker->has_current_face = TRUE; + } + stroker->current_face = face; + + /* Temporarily modify the stroker to use round joins to guarantee + * smooth stroked curves. */ + line_join_save = stroker->line_join; + stroker->line_join = CAIRO_LINE_JOIN_ROUND; + + status = _cairo_spline_decompose (&spline, stroker->tolerance); + + stroker->line_join = line_join_save; + + return status; +} + +static cairo_status_t +curve_to_dashed (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + struct stroker *stroker = closure; + cairo_spline_t spline; + cairo_line_join_t line_join_save; + cairo_spline_add_point_func_t func; + cairo_status_t status; + + func = (cairo_spline_add_point_func_t)line_to_dashed; + + if (stroker->has_bounds && + ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, + &stroker->line_bounds)) + return func (closure, d, NULL); + + if (! _cairo_spline_init (&spline, func, stroker, + &stroker->current_face.point, b, c, d)) + return func (closure, d, NULL); + + /* Temporarily modify the stroker to use round joins to guarantee + * smooth stroked curves. */ + line_join_save = stroker->line_join; + stroker->line_join = CAIRO_LINE_JOIN_ROUND; + + status = _cairo_spline_decompose (&spline, stroker->tolerance); + + stroker->line_join = line_join_save; + + return status; +} + +static cairo_status_t +_close_path (struct stroker *stroker) +{ + if (stroker->has_first_face && stroker->has_current_face) { + /* Join first and final faces of sub path */ + join (stroker, &stroker->current_face, &stroker->first_face); + } else { + /* Cap the start and end of the sub path as needed */ + add_caps (stroker); + } + + stroker->has_initial_sub_path = FALSE; + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +close_path (void *closure) +{ + struct stroker *stroker = closure; + cairo_status_t status; + + status = line_to (stroker, &stroker->first_point); + if (unlikely (status)) + return status; + + return _close_path (stroker); +} + +static cairo_status_t +close_path_dashed (void *closure) +{ + struct stroker *stroker = closure; + cairo_status_t status; + + status = line_to_dashed (stroker, &stroker->first_point); + if (unlikely (status)) + return status; + + return _close_path (stroker); +} + +cairo_int_status_t +_cairo_path_fixed_stroke_to_traps (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_traps_t *traps) +{ + struct stroker stroker; + cairo_status_t status; + + status = stroker_init (&stroker, path, style, + ctm, ctm_inverse, tolerance, + traps); + if (unlikely (status)) + return status; + + if (stroker.dash.dashed) + status = _cairo_path_fixed_interpret (path, + move_to_dashed, + line_to_dashed, + curve_to_dashed, + close_path_dashed, + &stroker); + else + status = _cairo_path_fixed_interpret (path, + move_to, + line_to, + curve_to, + close_path, + &stroker); + assert(status == CAIRO_STATUS_SUCCESS); + add_caps (&stroker); + + stroker_fini (&stroker); + + return traps->status; +} diff --git a/gfx/cairo/cairo/src/cairo-path-stroke-tristrip.c b/gfx/cairo/cairo/src/cairo-path-stroke-tristrip.c new file mode 100644 index 000000000000..3178765608d0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-stroke-tristrip.c @@ -0,0 +1,1088 @@ +/* -*- 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 © 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#define _DEFAULT_SOURCE /* for hypot() */ +#include "cairoint.h" + +#include "cairo-box-inline.h" +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" +#include "cairo-tristrip-private.h" + +struct stroker { + cairo_stroke_style_t style; + + cairo_tristrip_t *strip; + + const cairo_matrix_t *ctm; + const cairo_matrix_t *ctm_inverse; + double tolerance; + cairo_bool_t ctm_det_positive; + + cairo_pen_t pen; + + cairo_bool_t has_sub_path; + + cairo_point_t first_point; + + cairo_bool_t has_current_face; + cairo_stroke_face_t current_face; + + cairo_bool_t has_first_face; + cairo_stroke_face_t first_face; + + cairo_box_t limit; + cairo_bool_t has_limits; +}; + +static inline double +normalize_slope (double *dx, double *dy); + +static void +compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + struct stroker *stroker, + cairo_stroke_face_t *face); + +static void +translate_point (cairo_point_t *point, const cairo_point_t *offset) +{ + point->x += offset->x; + point->y += offset->y; +} + +static int +slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) +{ + double c = (dx1 * dy2 - dx2 * dy1); + + if (c > 0) return 1; + if (c < 0) return -1; + return 0; +} + +static inline int +range_step (int i, int step, int max) +{ + i += step; + if (i < 0) + i = max - 1; + if (i >= max) + i = 0; + return i; +} + +/* + * Construct a fan around the midpoint using the vertices from pen between + * inpt and outpt. + */ +static void +add_fan (struct stroker *stroker, + const cairo_slope_t *in_vector, + const cairo_slope_t *out_vector, + const cairo_point_t *midpt, + const cairo_point_t *inpt, + const cairo_point_t *outpt, + cairo_bool_t clockwise) +{ + int start, stop, step, i, npoints; + + if (clockwise) { + step = 1; + + start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, + in_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, + in_vector) < 0) + start = range_step (start, 1, stroker->pen.num_vertices); + + stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, + out_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, + out_vector) > 0) + { + stop = range_step (stop, -1, stroker->pen.num_vertices); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, + in_vector) < 0) + return; + } + + npoints = stop - start; + } else { + step = -1; + + start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, + in_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, + in_vector) < 0) + start = range_step (start, -1, stroker->pen.num_vertices); + + stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, + out_vector); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, + out_vector) > 0) + { + stop = range_step (stop, 1, stroker->pen.num_vertices); + if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, + in_vector) < 0) + return; + } + + npoints = start - stop; + } + stop = range_step (stop, step, stroker->pen.num_vertices); + if (npoints < 0) + npoints += stroker->pen.num_vertices; + if (npoints <= 1) + return; + + for (i = start; + i != stop; + i = range_step (i, step, stroker->pen.num_vertices)) + { + cairo_point_t p = *midpt; + translate_point (&p, &stroker->pen.vertices[i].point); + //contour_add_point (stroker, c, &p); + } +} + +static int +join_is_clockwise (const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; +} + +static void +inner_join (struct stroker *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out, + int clockwise) +{ + const cairo_point_t *outpt; + + if (clockwise) { + outpt = &out->ccw; + } else { + outpt = &out->cw; + } + //contour_add_point (stroker, inner, &in->point); + //contour_add_point (stroker, inner, outpt); +} + +static void +inner_close (struct stroker *stroker, + const cairo_stroke_face_t *in, + cairo_stroke_face_t *out) +{ + const cairo_point_t *inpt; + + if (join_is_clockwise (in, out)) { + inpt = &out->ccw; + } else { + inpt = &out->cw; + } + + //contour_add_point (stroker, inner, &in->point); + //contour_add_point (stroker, inner, inpt); + //*_cairo_contour_first_point (&inner->contour) = + //*_cairo_contour_last_point (&inner->contour); +} + +static void +outer_close (struct stroker *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + const cairo_point_t *inpt, *outpt; + int clockwise; + + if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) + { + return; + } + clockwise = join_is_clockwise (in, out); + if (clockwise) { + inpt = &in->cw; + outpt = &out->cw; + } else { + inpt = &in->ccw; + outpt = &out->ccw; + } + + 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, inpt, outpt, + clockwise); + break; + + case CAIRO_LINE_JOIN_MITER: + default: { + /* dot product of incoming slope vector with outgoing slope vector */ + double in_dot_out = -in->usr_vector.x * out->usr_vector.x + + -in->usr_vector.y * out->usr_vector.y; + double ml = stroker->style.miter_limit; + + /* Check the miter limit -- lines meeting at an acute angle + * can generate long miters, the limit converts them to bevel + * + * Consider the miter join formed when two line segments + * meet at an angle psi: + * + * /.\ + * /. .\ + * /./ \.\ + * /./psi\.\ + * + * We can zoom in on the right half of that to see: + * + * |\ + * | \ psi/2 + * | \ + * | \ + * | \ + * | \ + * miter \ + * length \ + * | \ + * | .\ + * | . \ + * |. line \ + * \ width \ + * \ \ + * + * + * The right triangle in that figure, (the line-width side is + * shown faintly with three '.' characters), gives us the + * following expression relating miter length, angle and line + * width: + * + * 1 /sin (psi/2) = miter_length / line_width + * + * The right-hand side of this relationship is the same ratio + * in which the miter limit (ml) is expressed. We want to know + * when the miter length is within the miter limit. That is + * when the following condition holds: + * + * 1/sin(psi/2) <= ml + * 1 <= ml sin(psi/2) + * 1 <= ml² sin²(psi/2) + * 2 <= ml² 2 sin²(psi/2) + * 2·sin²(psi/2) = 1-cos(psi) + * 2 <= ml² (1-cos(psi)) + * + * in · out = |in| |out| cos (psi) + * + * in and out are both unit vectors, so: + * + * in · out = cos (psi) + * + * 2 <= ml² (1 - in · out) + * + */ + if (2 <= ml * ml * (1 - in_dot_out)) { + double x1, y1, x2, y2; + double mx, my; + double dx1, dx2, dy1, dy2; + double ix, iy; + double fdx1, fdy1, fdx2, fdy2; + double mdx, mdy; + + /* + * we've got the points already transformed to device + * space, but need to do some computation with them and + * also need to transform the slope from user space to + * device space + */ + /* outer point of incoming line face */ + x1 = _cairo_fixed_to_double (inpt->x); + y1 = _cairo_fixed_to_double (inpt->y); + dx1 = in->usr_vector.x; + dy1 = in->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); + + /* outer point of outgoing line face */ + x2 = _cairo_fixed_to_double (outpt->x); + y2 = _cairo_fixed_to_double (outpt->y); + dx2 = out->usr_vector.x; + dy2 = out->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); + + /* + * Compute the location of the outer corner of the miter. + * That's pretty easy -- just the intersection of the two + * outer edges. We've got slopes and points on each + * of those edges. Compute my directly, then compute + * mx by using the edge with the larger dy; that avoids + * dividing by values close to zero. + */ + my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / + (dx1 * dy2 - dx2 * dy1)); + if (fabs (dy1) >= fabs (dy2)) + mx = (my - y1) * dx1 / dy1 + x1; + else + mx = (my - y2) * dx2 / dy2 + x2; + + /* + * When the two outer edges are nearly parallel, slight + * perturbations in the position of the outer points of the lines + * caused by representing them in fixed point form can cause the + * intersection point of the miter to move a large amount. If + * that moves the miter intersection from between the two faces, + * then draw a bevel instead. + */ + + ix = _cairo_fixed_to_double (in->point.x); + iy = _cairo_fixed_to_double (in->point.y); + + /* slope of one face */ + fdx1 = x1 - ix; fdy1 = y1 - iy; + + /* slope of the other face */ + fdx2 = x2 - ix; fdy2 = y2 - iy; + + /* slope from the intersection to the miter point */ + mdx = mx - ix; mdy = my - iy; + + /* + * Make sure the miter point line lies between the two + * faces by comparing the slopes + */ + if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + cairo_point_t p; + + p.x = _cairo_fixed_from_double (mx); + p.y = _cairo_fixed_from_double (my); + + //*_cairo_contour_last_point (&outer->contour) = p; + //*_cairo_contour_first_point (&outer->contour) = p; + return; + } + } + break; + } + + case CAIRO_LINE_JOIN_BEVEL: + break; + } + //contour_add_point (stroker, outer, outpt); +} + +static void +outer_join (struct stroker *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out, + int clockwise) +{ + const cairo_point_t *inpt, *outpt; + + if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && + in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) + { + return; + } + if (clockwise) { + inpt = &in->cw; + outpt = &out->cw; + } else { + inpt = &in->ccw; + outpt = &out->ccw; + } + + 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, inpt, outpt, + clockwise); + break; + + case CAIRO_LINE_JOIN_MITER: + default: { + /* dot product of incoming slope vector with outgoing slope vector */ + double in_dot_out = -in->usr_vector.x * out->usr_vector.x + + -in->usr_vector.y * out->usr_vector.y; + double ml = stroker->style.miter_limit; + + /* Check the miter limit -- lines meeting at an acute angle + * can generate long miters, the limit converts them to bevel + * + * Consider the miter join formed when two line segments + * meet at an angle psi: + * + * /.\ + * /. .\ + * /./ \.\ + * /./psi\.\ + * + * We can zoom in on the right half of that to see: + * + * |\ + * | \ psi/2 + * | \ + * | \ + * | \ + * | \ + * miter \ + * length \ + * | \ + * | .\ + * | . \ + * |. line \ + * \ width \ + * \ \ + * + * + * The right triangle in that figure, (the line-width side is + * shown faintly with three '.' characters), gives us the + * following expression relating miter length, angle and line + * width: + * + * 1 /sin (psi/2) = miter_length / line_width + * + * The right-hand side of this relationship is the same ratio + * in which the miter limit (ml) is expressed. We want to know + * when the miter length is within the miter limit. That is + * when the following condition holds: + * + * 1/sin(psi/2) <= ml + * 1 <= ml sin(psi/2) + * 1 <= ml² sin²(psi/2) + * 2 <= ml² 2 sin²(psi/2) + * 2·sin²(psi/2) = 1-cos(psi) + * 2 <= ml² (1-cos(psi)) + * + * in · out = |in| |out| cos (psi) + * + * in and out are both unit vectors, so: + * + * in · out = cos (psi) + * + * 2 <= ml² (1 - in · out) + * + */ + if (2 <= ml * ml * (1 - in_dot_out)) { + double x1, y1, x2, y2; + double mx, my; + double dx1, dx2, dy1, dy2; + double ix, iy; + double fdx1, fdy1, fdx2, fdy2; + double mdx, mdy; + + /* + * we've got the points already transformed to device + * space, but need to do some computation with them and + * also need to transform the slope from user space to + * device space + */ + /* outer point of incoming line face */ + x1 = _cairo_fixed_to_double (inpt->x); + y1 = _cairo_fixed_to_double (inpt->y); + dx1 = in->usr_vector.x; + dy1 = in->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); + + /* outer point of outgoing line face */ + x2 = _cairo_fixed_to_double (outpt->x); + y2 = _cairo_fixed_to_double (outpt->y); + dx2 = out->usr_vector.x; + dy2 = out->usr_vector.y; + cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); + + /* + * Compute the location of the outer corner of the miter. + * That's pretty easy -- just the intersection of the two + * outer edges. We've got slopes and points on each + * of those edges. Compute my directly, then compute + * mx by using the edge with the larger dy; that avoids + * dividing by values close to zero. + */ + my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / + (dx1 * dy2 - dx2 * dy1)); + if (fabs (dy1) >= fabs (dy2)) + mx = (my - y1) * dx1 / dy1 + x1; + else + mx = (my - y2) * dx2 / dy2 + x2; + + /* + * When the two outer edges are nearly parallel, slight + * perturbations in the position of the outer points of the lines + * caused by representing them in fixed point form can cause the + * intersection point of the miter to move a large amount. If + * that moves the miter intersection from between the two faces, + * then draw a bevel instead. + */ + + ix = _cairo_fixed_to_double (in->point.x); + iy = _cairo_fixed_to_double (in->point.y); + + /* slope of one face */ + fdx1 = x1 - ix; fdy1 = y1 - iy; + + /* slope of the other face */ + fdx2 = x2 - ix; fdy2 = y2 - iy; + + /* slope from the intersection to the miter point */ + mdx = mx - ix; mdy = my - iy; + + /* + * Make sure the miter point line lies between the two + * faces by comparing the slopes + */ + if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + cairo_point_t p; + + p.x = _cairo_fixed_from_double (mx); + p.y = _cairo_fixed_from_double (my); + + //*_cairo_contour_last_point (&outer->contour) = p; + return; + } + } + break; + } + + case CAIRO_LINE_JOIN_BEVEL: + break; + } + //contour_add_point (stroker,outer, outpt); +} + +static void +add_cap (struct stroker *stroker, + const cairo_stroke_face_t *f) +{ + switch (stroker->style.line_cap) { + case CAIRO_LINE_CAP_ROUND: { + cairo_slope_t slope; + + slope.dx = -f->dev_vector.dx; + slope.dy = -f->dev_vector.dy; + + add_fan (stroker, &f->dev_vector, &slope, + &f->point, &f->ccw, &f->cw, + FALSE); + break; + } + + case CAIRO_LINE_CAP_SQUARE: { + double dx, dy; + cairo_slope_t fvector; + cairo_point_t quad[4]; + + dx = f->usr_vector.x; + dy = f->usr_vector.y; + dx *= stroker->style.line_width / 2.0; + dy *= stroker->style.line_width / 2.0; + cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); + fvector.dx = _cairo_fixed_from_double (dx); + fvector.dy = _cairo_fixed_from_double (dy); + + quad[0] = f->ccw; + quad[1].x = f->ccw.x + fvector.dx; + quad[1].y = f->ccw.y + fvector.dy; + quad[2].x = f->cw.x + fvector.dx; + quad[2].y = f->cw.y + fvector.dy; + quad[3] = f->cw; + + //contour_add_point (stroker, c, &quad[1]); + //contour_add_point (stroker, c, &quad[2]); + } + + case CAIRO_LINE_CAP_BUTT: + default: + break; + } + //contour_add_point (stroker, c, &f->cw); +} + +static void +add_leading_cap (struct stroker *stroker, + const cairo_stroke_face_t *face) +{ + cairo_stroke_face_t reversed; + cairo_point_t t; + + reversed = *face; + + /* The initial cap needs an outward facing vector. Reverse everything */ + reversed.usr_vector.x = -reversed.usr_vector.x; + reversed.usr_vector.y = -reversed.usr_vector.y; + reversed.dev_vector.dx = -reversed.dev_vector.dx; + reversed.dev_vector.dy = -reversed.dev_vector.dy; + + t = reversed.cw; + reversed.cw = reversed.ccw; + reversed.ccw = t; + + add_cap (stroker, &reversed); +} + +static void +add_trailing_cap (struct stroker *stroker, + const cairo_stroke_face_t *face) +{ + add_cap (stroker, face); +} + +static inline double +normalize_slope (double *dx, double *dy) +{ + double dx0 = *dx, dy0 = *dy; + double mag; + + assert (dx0 != 0.0 || dy0 != 0.0); + + if (dx0 == 0.0) { + *dx = 0.0; + if (dy0 > 0.0) { + mag = dy0; + *dy = 1.0; + } else { + mag = -dy0; + *dy = -1.0; + } + } else if (dy0 == 0.0) { + *dy = 0.0; + if (dx0 > 0.0) { + mag = dx0; + *dx = 1.0; + } else { + mag = -dx0; + *dx = -1.0; + } + } else { + mag = hypot (dx0, dy0); + *dx = dx0 / mag; + *dy = dy0 / mag; + } + + return mag; +} + +static void +compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + struct stroker *stroker, + cairo_stroke_face_t *face) +{ + double face_dx, face_dy; + cairo_point_t offset_ccw, offset_cw; + double slope_dx, slope_dy; + + slope_dx = _cairo_fixed_to_double (dev_slope->dx); + slope_dy = _cairo_fixed_to_double (dev_slope->dy); + face->length = normalize_slope (&slope_dx, &slope_dy); + face->dev_slope.x = slope_dx; + face->dev_slope.y = slope_dy; + + /* + * rotate to get a line_width/2 vector along the face, note that + * the vector must be rotated the right direction in device space, + * but by 90° in user space. So, the rotation depends on + * whether the ctm reflects or not, and that can be determined + * by looking at the determinant of the matrix. + */ + if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { + /* Normalize the matrix! */ + cairo_matrix_transform_distance (stroker->ctm_inverse, + &slope_dx, &slope_dy); + normalize_slope (&slope_dx, &slope_dy); + + if (stroker->ctm_det_positive) { + face_dx = - slope_dy * (stroker->style.line_width / 2.0); + face_dy = slope_dx * (stroker->style.line_width / 2.0); + } else { + face_dx = slope_dy * (stroker->style.line_width / 2.0); + face_dy = - slope_dx * (stroker->style.line_width / 2.0); + } + + /* back to device space */ + cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); + } else { + face_dx = - slope_dy * (stroker->style.line_width / 2.0); + face_dy = slope_dx * (stroker->style.line_width / 2.0); + } + + offset_ccw.x = _cairo_fixed_from_double (face_dx); + offset_ccw.y = _cairo_fixed_from_double (face_dy); + offset_cw.x = -offset_ccw.x; + offset_cw.y = -offset_ccw.y; + + face->ccw = *point; + translate_point (&face->ccw, &offset_ccw); + + face->point = *point; + + face->cw = *point; + translate_point (&face->cw, &offset_cw); + + face->usr_vector.x = slope_dx; + face->usr_vector.y = slope_dy; + + face->dev_vector = *dev_slope; +} + +static void +add_caps (struct stroker *stroker) +{ + /* check for a degenerative sub_path */ + if (stroker->has_sub_path && + ! stroker->has_first_face && + ! stroker->has_current_face && + stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) + { + /* pick an arbitrary slope to use */ + cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; + cairo_stroke_face_t face; + + /* arbitrarily choose first_point */ + compute_face (&stroker->first_point, &slope, stroker, &face); + + add_leading_cap (stroker, &face); + add_trailing_cap (stroker, &face); + + /* ensure the circle is complete */ + //_cairo_contour_add_point (&stroker->ccw.contour, + //_cairo_contour_first_point (&stroker->ccw.contour)); + } else { + if (stroker->has_current_face) + add_trailing_cap (stroker, &stroker->current_face); + + //_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); + //_cairo_contour_reset (&stroker->ccw.contour); + + if (stroker->has_first_face) { + //_cairo_contour_add_point (&stroker->ccw.contour, + //&stroker->first_face.cw); + add_leading_cap (stroker, &stroker->first_face); + //_cairo_polygon_add_contour (stroker->polygon, + //&stroker->ccw.contour); + //_cairo_contour_reset (&stroker->ccw.contour); + } + } +} + +static cairo_status_t +move_to (void *closure, + const cairo_point_t *point) +{ + struct stroker *stroker = closure; + + /* Cap the start and end of the previous sub path as needed */ + add_caps (stroker); + + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + stroker->has_sub_path = FALSE; + + stroker->first_point = *point; + + stroker->current_face.point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +line_to (void *closure, + const cairo_point_t *point) +{ + struct stroker *stroker = closure; + cairo_stroke_face_t start; + cairo_point_t *p1 = &stroker->current_face.point; + cairo_slope_t dev_slope; + + stroker->has_sub_path = TRUE; + + if (p1->x == point->x && p1->y == point->y) + return CAIRO_STATUS_SUCCESS; + + _cairo_slope_init (&dev_slope, p1, point); + compute_face (p1, &dev_slope, stroker, &start); + + if (stroker->has_current_face) { + int clockwise = join_is_clockwise (&stroker->current_face, &start); + /* Join with final face from previous segment */ + outer_join (stroker, &stroker->current_face, &start, clockwise); + inner_join (stroker, &stroker->current_face, &start, clockwise); + } else { + if (! stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = start; + _cairo_tristrip_move_to (stroker->strip, &start.cw); + stroker->has_first_face = TRUE; + } + stroker->has_current_face = TRUE; + + _cairo_tristrip_add_point (stroker->strip, &start.cw); + _cairo_tristrip_add_point (stroker->strip, &start.ccw); + } + + stroker->current_face = start; + stroker->current_face.point = *point; + stroker->current_face.ccw.x += dev_slope.dx; + stroker->current_face.ccw.y += dev_slope.dy; + stroker->current_face.cw.x += dev_slope.dx; + stroker->current_face.cw.y += dev_slope.dy; + + _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw); + _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +spline_to (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent) +{ + struct stroker *stroker = closure; + cairo_stroke_face_t face; + + if (tangent->dx == 0 && tangent->dy == 0) { + const cairo_point_t *inpt, *outpt; + cairo_point_t t; + int clockwise; + + face = stroker->current_face; + + face.usr_vector.x = -face.usr_vector.x; + face.usr_vector.y = -face.usr_vector.y; + face.dev_vector.dx = -face.dev_vector.dx; + face.dev_vector.dy = -face.dev_vector.dy; + + t = face.cw; + face.cw = face.ccw; + face.ccw = t; + + clockwise = join_is_clockwise (&stroker->current_face, &face); + if (clockwise) { + inpt = &stroker->current_face.cw; + outpt = &face.cw; + } else { + inpt = &stroker->current_face.ccw; + outpt = &face.ccw; + } + + add_fan (stroker, + &stroker->current_face.dev_vector, + &face.dev_vector, + &stroker->current_face.point, inpt, outpt, + clockwise); + } else { + compute_face (point, tangent, stroker, &face); + + if (face.dev_slope.x * stroker->current_face.dev_slope.x + + face.dev_slope.y * stroker->current_face.dev_slope.y < 0) + { + const cairo_point_t *inpt, *outpt; + int clockwise = join_is_clockwise (&stroker->current_face, &face); + + stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x; + stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y; + //contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); + + stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x; + stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y; + //contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); + + if (clockwise) { + inpt = &stroker->current_face.cw; + outpt = &face.cw; + } else { + inpt = &stroker->current_face.ccw; + outpt = &face.ccw; + } + add_fan (stroker, + &stroker->current_face.dev_vector, + &face.dev_vector, + &stroker->current_face.point, inpt, outpt, + clockwise); + } + + _cairo_tristrip_add_point (stroker->strip, &face.cw); + _cairo_tristrip_add_point (stroker->strip, &face.ccw); + } + + stroker->current_face = face; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + struct stroker *stroker = closure; + cairo_spline_t spline; + cairo_stroke_face_t face; + + if (stroker->has_limits) { + if (! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, + &stroker->limit)) + return line_to (closure, d); + } + + if (! _cairo_spline_init (&spline, spline_to, stroker, + &stroker->current_face.point, b, c, d)) + return line_to (closure, d); + + compute_face (&stroker->current_face.point, &spline.initial_slope, + stroker, &face); + + if (stroker->has_current_face) { + int clockwise = join_is_clockwise (&stroker->current_face, &face); + /* Join with final face from previous segment */ + outer_join (stroker, &stroker->current_face, &face, clockwise); + inner_join (stroker, &stroker->current_face, &face, clockwise); + } else { + if (! stroker->has_first_face) { + /* Save sub path's first face in case needed for closing join */ + stroker->first_face = face; + _cairo_tristrip_move_to (stroker->strip, &face.cw); + stroker->has_first_face = TRUE; + } + stroker->has_current_face = TRUE; + + _cairo_tristrip_add_point (stroker->strip, &face.cw); + _cairo_tristrip_add_point (stroker->strip, &face.ccw); + } + stroker->current_face = face; + + return _cairo_spline_decompose (&spline, stroker->tolerance); +} + +static cairo_status_t +close_path (void *closure) +{ + struct stroker *stroker = closure; + cairo_status_t status; + + status = line_to (stroker, &stroker->first_point); + if (unlikely (status)) + return status; + + if (stroker->has_first_face && stroker->has_current_face) { + /* Join first and final faces of sub path */ + outer_close (stroker, &stroker->current_face, &stroker->first_face); + inner_close (stroker, &stroker->current_face, &stroker->first_face); + } else { + /* Cap the start and end of the sub path as needed */ + add_caps (stroker); + } + + stroker->has_sub_path = FALSE; + stroker->has_first_face = FALSE; + stroker->has_current_face = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_path_fixed_stroke_to_tristrip (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_tristrip_t *strip) +{ + struct stroker stroker; + cairo_int_status_t status; + int i; + + if (style->num_dashes) + return CAIRO_INT_STATUS_UNSUPPORTED; + + stroker.style = *style; + stroker.ctm = ctm; + stroker.ctm_inverse = ctm_inverse; + stroker.tolerance = tolerance; + + stroker.ctm_det_positive = + _cairo_matrix_compute_determinant (ctm) >= 0.0; + + status = _cairo_pen_init (&stroker.pen, + style->line_width / 2.0, + tolerance, ctm); + if (unlikely (status)) + return status; + + if (stroker.pen.num_vertices <= 1) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + stroker.has_current_face = FALSE; + stroker.has_first_face = FALSE; + stroker.has_sub_path = FALSE; + + stroker.has_limits = strip->num_limits > 0; + stroker.limit = strip->limits[0]; + for (i = 1; i < strip->num_limits; i++) + _cairo_box_add_box (&stroker.limit, &strip->limits[i]); + + stroker.strip = strip; + + status = _cairo_path_fixed_interpret (path, + move_to, + line_to, + curve_to, + close_path, + &stroker); + /* Cap the start and end of the final sub path as needed */ + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + add_caps (&stroker); + + _cairo_pen_fini (&stroker.pen); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-path-stroke.c b/gfx/cairo/cairo/src/cairo-path-stroke.c index 505b6ab6a51b..64cec8f272ee 100644 --- a/gfx/cairo/cairo/src/cairo-path-stroke.c +++ b/gfx/cairo/cairo/src/cairo-path-stroke.c @@ -36,32 +36,25 @@ * Chris Wilson */ -#define _BSD_SOURCE /* for hypot() */ +#define _DEFAULT_SOURCE /* for hypot() */ #include "cairoint.h" +#include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-slope-private.h" - -typedef struct _cairo_stroker_dash { - cairo_bool_t dashed; - unsigned int dash_index; - cairo_bool_t dash_on; - cairo_bool_t dash_starts_on; - double dash_remain; - - double dash_offset; - const double *dashes; - unsigned int num_dashes; -} cairo_stroker_dash_t; +#include "cairo-stroke-dash-private.h" +#include "cairo-traps-private.h" typedef struct cairo_stroker { cairo_stroke_style_t style; const cairo_matrix_t *ctm; const cairo_matrix_t *ctm_inverse; + double half_line_width; double tolerance; + double spline_cusp_tolerance; double ctm_determinant; cairo_bool_t ctm_det_positive; @@ -97,99 +90,9 @@ typedef struct cairo_stroker { cairo_box_t bounds; } cairo_stroker_t; -static void -_cairo_stroker_dash_start (cairo_stroker_dash_t *dash) -{ - double offset; - cairo_bool_t on = TRUE; - unsigned int i = 0; - - if (! dash->dashed) - return; - - offset = dash->dash_offset; - - /* We stop searching for a starting point as soon as the - offset reaches zero. Otherwise when an initial dash - segment shrinks to zero it will be skipped over. */ - while (offset > 0.0 && offset >= dash->dashes[i]) { - offset -= dash->dashes[i]; - on = !on; - if (++i == dash->num_dashes) - i = 0; - } - - dash->dash_index = i; - dash->dash_on = dash->dash_starts_on = on; - dash->dash_remain = dash->dashes[i] - offset; -} - -static void -_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) -{ - dash->dash_remain -= step; - if (dash->dash_remain <= 0.) { - if (++dash->dash_index == dash->num_dashes) - dash->dash_index = 0; - - dash->dash_on = ! dash->dash_on; - dash->dash_remain = dash->dashes[dash->dash_index]; - } -} - -static void -_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, - const cairo_stroke_style_t *style) -{ - dash->dashed = style->dash != NULL; - if (! dash->dashed) - return; - - dash->dashes = style->dash; - dash->num_dashes = style->num_dashes; - dash->dash_offset = style->dash_offset; - - _cairo_stroker_dash_start (dash); -} - -static cairo_status_t -_cairo_stroker_init (cairo_stroker_t *stroker, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance) -{ - cairo_status_t status; - - stroker->style = *stroke_style; - stroker->ctm = ctm; - stroker->ctm_inverse = ctm_inverse; - stroker->tolerance = tolerance; - - stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); - stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; - - status = _cairo_pen_init (&stroker->pen, - stroke_style->line_width / 2.0, - tolerance, ctm); - if (unlikely (status)) - return status; - - stroker->has_bounds = FALSE; - - stroker->has_current_face = FALSE; - stroker->has_first_face = FALSE; - stroker->has_initial_sub_path = FALSE; - - _cairo_stroker_dash_init (&stroker->dash, stroke_style); - - stroker->add_external_edge = NULL; - - return CAIRO_STATUS_SUCCESS; -} - static void _cairo_stroker_limit (cairo_stroker_t *stroker, + const cairo_path_fixed_t *path, const cairo_box_t *boxes, int num_boxes) { @@ -204,8 +107,8 @@ _cairo_stroker_limit (cairo_stroker_t *stroker, * of the bounds but which might generate rendering that's within bounds. */ - _cairo_stroke_style_max_distance_from_path (&stroker->style, stroker->ctm, - &dx, &dy); + _cairo_stroke_style_max_distance_from_path (&stroker->style, path, + stroker->ctm, &dx, &dy); fdx = _cairo_fixed_from_double (dx); fdy = _cairo_fixed_from_double (dy); @@ -217,6 +120,59 @@ _cairo_stroker_limit (cairo_stroker_t *stroker, stroker->bounds.p2.y += fdy; } +static cairo_status_t +_cairo_stroker_init (cairo_stroker_t *stroker, + 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, + const cairo_box_t *limits, + int num_limits) +{ + cairo_status_t status; + + stroker->style = *stroke_style; + stroker->ctm = ctm; + stroker->ctm_inverse = ctm_inverse; + stroker->tolerance = tolerance; + stroker->half_line_width = stroke_style->line_width / 2.0; + + /* 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. + */ + 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; + + stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm); + stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0; + + status = _cairo_pen_init (&stroker->pen, + stroker->half_line_width, tolerance, ctm); + if (unlikely (status)) + return status; + + stroker->has_current_face = FALSE; + stroker->has_first_face = FALSE; + stroker->has_initial_sub_path = FALSE; + + _cairo_stroker_dash_init (&stroker->dash, stroke_style); + + stroker->add_external_edge = NULL; + + stroker->has_bounds = FALSE; + if (num_limits) + _cairo_stroker_limit (stroker, path, limits, num_limits); + + return CAIRO_STATUS_SUCCESS; +} + static void _cairo_stroker_fini (cairo_stroker_t *stroker) { @@ -243,11 +199,11 @@ _cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in, } /** - * _cairo_slope_compare_sgn + * _cairo_slope_compare_sgn: * * Return -1, 0 or 1 depending on the relative slopes of * two lines. - */ + **/ static int _cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2) { @@ -283,100 +239,117 @@ _tessellate_fan (cairo_stroker_t *stroker, cairo_bool_t clockwise) { cairo_point_t stack_points[64], *points = stack_points; - int start, stop, step, i, npoints; + cairo_pen_t *pen = &stroker->pen; + int start, stop, num_points = 0; cairo_status_t status; - if (clockwise) { - step = -1; - - start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, - in_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, - in_vector) < 0) - start = _range_step (start, -1, stroker->pen.num_vertices); - - stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, - out_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, - out_vector) > 0) - { - stop = _range_step (stop, 1, stroker->pen.num_vertices); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, - in_vector) < 0) - { - goto BEVEL; - } - } - - npoints = start - stop; - } else { - step = 1; - - start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, - in_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, - in_vector) < 0) - start = _range_step (start, 1, stroker->pen.num_vertices); - - stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, - out_vector); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, - out_vector) > 0) - { - stop = _range_step (stop, -1, stroker->pen.num_vertices); - if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, - in_vector) < 0) - { - goto BEVEL; - } - } - - npoints = stop - start; - } - stop = _range_step (stop, step, stroker->pen.num_vertices); - - if (npoints < 0) - npoints += stroker->pen.num_vertices; - npoints += 3; - - if (npoints <= 1) + if (stroker->has_bounds && + ! _cairo_box_contains_point (&stroker->bounds, midpt)) goto BEVEL; - if (npoints > ARRAY_LENGTH (stack_points)) { - points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t)); - if (unlikely (points == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } + assert (stroker->pen.num_vertices); + if (clockwise) { + _cairo_pen_find_active_ccw_vertices (pen, + in_vector, out_vector, + &start, &stop); + if (stroker->add_external_edge) { + cairo_point_t last; + last = *inpt; + while (start != stop) { + cairo_point_t p = *midpt; + _translate_point (&p, &pen->vertices[start].point); - /* Construct the fan. */ - npoints = 0; - points[npoints++] = *inpt; - for (i = start; - i != stop; - i = _range_step (i, step, stroker->pen.num_vertices)) - { - points[npoints] = *midpt; - _translate_point (&points[npoints], &stroker->pen.vertices[i].point); - npoints++; - } - points[npoints++] = *outpt; - - if (stroker->add_external_edge != NULL) { - for (i = 0; i < npoints - 1; i++) { - if (clockwise) { status = stroker->add_external_edge (stroker->closure, - &points[i], &points[i+1]); - } else { - status = stroker->add_external_edge (stroker->closure, - &points[i+1], &points[i]); + &last, &p); + if (unlikely (status)) + return status; + last = p; + + if (start-- == 0) + start += pen->num_vertices; } - if (unlikely (status)) - break; + status = stroker->add_external_edge (stroker->closure, + &last, outpt); + } else { + if (start == stop) + goto BEVEL; + + num_points = stop - start; + if (num_points < 0) + num_points += pen->num_vertices; + num_points += 2; + if (num_points > ARRAY_LENGTH(stack_points)) { + points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); + if (unlikely (points == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + points[0] = *inpt; + num_points = 1; + while (start != stop) { + points[num_points] = *midpt; + _translate_point (&points[num_points], &pen->vertices[start].point); + num_points++; + + if (start-- == 0) + start += pen->num_vertices; + } + points[num_points++] = *outpt; } } else { + _cairo_pen_find_active_cw_vertices (pen, + in_vector, out_vector, + &start, &stop); + if (stroker->add_external_edge) { + cairo_point_t last; + last = *inpt; + while (start != stop) { + cairo_point_t p = *midpt; + _translate_point (&p, &pen->vertices[start].point); + + status = stroker->add_external_edge (stroker->closure, + &p, &last); + if (unlikely (status)) + return status; + last = p; + + if (++start == pen->num_vertices) + start = 0; + } + status = stroker->add_external_edge (stroker->closure, + outpt, &last); + } else { + if (start == stop) + goto BEVEL; + + num_points = stop - start; + if (num_points < 0) + num_points += pen->num_vertices; + num_points += 2; + if (num_points > ARRAY_LENGTH(stack_points)) { + points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); + if (unlikely (points == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + points[0] = *inpt; + num_points = 1; + while (start != stop) { + points[num_points] = *midpt; + _translate_point (&points[num_points], &pen->vertices[start].point); + num_points++; + + if (++start == pen->num_vertices) + start = 0; + } + points[num_points++] = *outpt; + } + } + + if (num_points) { status = stroker->add_triangle_fan (stroker->closure, - midpt, points, npoints); + midpt, points, num_points); } if (points != stack_points) @@ -678,8 +651,8 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, dx = f->usr_vector.x; dy = f->usr_vector.y; - dx *= stroker->style.line_width / 2.0; - dy *= stroker->style.line_width / 2.0; + dx *= stroker->half_line_width; + dy *= stroker->half_line_width; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); @@ -801,9 +774,12 @@ _compute_normalized_device_slope (double *dx, double *dy, } static void -_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, - double slope_dx, double slope_dy, - cairo_stroker_t *stroker, cairo_stroke_face_t *face) +_compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + double slope_dx, + double slope_dy, + cairo_stroker_t *stroker, + cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; @@ -817,13 +793,13 @@ _compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, */ if (stroker->ctm_det_positive) { - face_dx = - slope_dy * (stroker->style.line_width / 2.0); - face_dy = slope_dx * (stroker->style.line_width / 2.0); + face_dx = - slope_dy * stroker->half_line_width; + face_dy = slope_dx * stroker->half_line_width; } else { - face_dx = slope_dy * (stroker->style.line_width / 2.0); - face_dy = - slope_dx * (stroker->style.line_width / 2.0); + face_dx = slope_dy * stroker->half_line_width; + face_dy = - slope_dx * stroker->half_line_width; } /* back to device space */ @@ -857,7 +833,7 @@ _cairo_stroker_add_caps (cairo_stroker_t *stroker) if (stroker->has_initial_sub_path && ! stroker->has_first_face && ! stroker->has_current_face - && stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND) + && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) { /* pick an arbitrary slope to use */ double dx = 1.0, dy = 0.0; @@ -1019,6 +995,91 @@ _cairo_stroker_line_to (void *closure, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_stroker_spline_to (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent) +{ + cairo_stroker_t *stroker = closure; + cairo_stroke_face_t new_face; + double slope_dx, slope_dy; + cairo_point_t points[3]; + cairo_point_t intersect_point; + + stroker->has_initial_sub_path = TRUE; + + if (stroker->current_point.x == point->x && + stroker->current_point.y == point->y) + return CAIRO_STATUS_SUCCESS; + + slope_dx = _cairo_fixed_to_double (tangent->dx); + slope_dy = _cairo_fixed_to_double (tangent->dy); + + if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + return CAIRO_STATUS_SUCCESS; + + _compute_face (point, tangent, + slope_dx, slope_dy, + stroker, &new_face); + + assert (stroker->has_current_face); + + if ((new_face.dev_slope.x * stroker->current_face.dev_slope.x + + new_face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) { + + const cairo_point_t *inpt, *outpt; + int clockwise = _cairo_stroker_join_is_clockwise (&new_face, + &stroker->current_face); + + if (clockwise) { + inpt = &stroker->current_face.cw; + outpt = &new_face.cw; + } else { + inpt = &stroker->current_face.ccw; + outpt = &new_face.ccw; + } + + _tessellate_fan (stroker, + &stroker->current_face.dev_vector, + &new_face.dev_vector, + &stroker->current_face.point, + inpt, outpt, + clockwise); + } + + if (_slow_segment_intersection (&stroker->current_face.cw, + &stroker->current_face.ccw, + &new_face.cw, + &new_face.ccw, + &intersect_point)) { + points[0] = stroker->current_face.ccw; + points[1] = new_face.ccw; + points[2] = intersect_point; + stroker->add_triangle (stroker->closure, points); + + points[0] = stroker->current_face.cw; + points[1] = new_face.cw; + stroker->add_triangle (stroker->closure, points); + } else { + points[0] = stroker->current_face.ccw; + points[1] = stroker->current_face.cw; + points[2] = new_face.cw; + stroker->add_triangle (stroker->closure, points); + + points[0] = stroker->current_face.ccw; + points[1] = new_face.cw; + points[2] = new_face.ccw; + stroker->add_triangle (stroker->closure, points); + } + + stroker->current_face = new_face; + stroker->has_current_face = TRUE; + stroker->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + /* * Dashed lines. Cap each dash end, join around turns when on */ @@ -1175,18 +1236,27 @@ _cairo_stroker_curve_to (void *closure, cairo_line_join_t line_join_save; cairo_stroke_face_t face; double slope_dx, slope_dy; - cairo_path_fixed_line_to_func_t *line_to; + cairo_spline_add_point_func_t line_to; + cairo_spline_add_point_func_t spline_to; cairo_status_t status = CAIRO_STATUS_SUCCESS; line_to = stroker->dash.dashed ? - _cairo_stroker_line_to_dashed : - _cairo_stroker_line_to; + (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : + (cairo_spline_add_point_func_t) _cairo_stroker_line_to; + + /* spline_to is only capable of rendering non-degenerate splines. */ + spline_to = stroker->dash.dashed ? + (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : + (cairo_spline_add_point_func_t) _cairo_stroker_spline_to; if (! _cairo_spline_init (&spline, - line_to, stroker, + spline_to, + stroker, &stroker->current_point, b, c, d)) { - return line_to (closure, d); + cairo_slope_t fallback_slope; + _cairo_slope_init (&fallback_slope, &stroker->current_point, d); + return line_to (closure, d, &fallback_slope); } /* If the line width is so small that the pen is reduced to a @@ -1307,8 +1377,9 @@ _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, cairo_stroker_t stroker; cairo_status_t status; - status = _cairo_stroker_init (&stroker, stroke_style, - ctm, ctm_inverse, tolerance); + status = _cairo_stroker_init (&stroker, path, stroke_style, + ctm, ctm_inverse, tolerance, + NULL, 0); if (unlikely (status)) return status; @@ -1318,7 +1389,6 @@ _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, stroker.closure = closure; status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_stroker_move_to, stroker.dash.dashed ? _cairo_stroker_line_to_dashed : @@ -1340,29 +1410,26 @@ BAIL: } cairo_status_t -_cairo_path_fixed_stroke_to_polygon (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_polygon_t *polygon) +_cairo_path_fixed_stroke_dashed_to_polygon (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_polygon_t *polygon) { cairo_stroker_t stroker; cairo_status_t status; - status = _cairo_stroker_init (&stroker, stroke_style, - ctm, ctm_inverse, tolerance); + status = _cairo_stroker_init (&stroker, path, stroke_style, + ctm, ctm_inverse, tolerance, + polygon->limits, polygon->num_limits); if (unlikely (status)) return status; stroker.add_external_edge = _cairo_polygon_add_external_edge, stroker.closure = polygon; - if (polygon->num_limits) - _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits); - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_stroker_move_to, stroker.dash.dashed ? _cairo_stroker_line_to_dashed : @@ -1383,35 +1450,18 @@ BAIL: return status; } -cairo_status_t -_cairo_path_fixed_stroke_to_traps (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_traps_t *traps) +cairo_int_status_t +_cairo_path_fixed_stroke_polygon_to_traps (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_traps_t *traps) { - cairo_status_t status; + cairo_int_status_t status; cairo_polygon_t polygon; - /* Before we do anything else, we attempt the rectilinear - * stroker. It's careful to generate trapezoids that align to - * device-pixel boundaries when possible. Many backends can render - * those much faster than non-aligned trapezoids, (by using clip - * regions, etc.) */ - if (path->is_rectilinear) { - status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, - stroke_style, - ctm, - traps); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - _cairo_polygon_init (&polygon); - if (traps->num_limits) - _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits); - + _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, @@ -1433,711 +1483,3 @@ BAIL: return status; } - -typedef struct _segment_t { - cairo_point_t p1, p2; - cairo_bool_t is_horizontal; - cairo_bool_t has_join; -} segment_t; - -typedef struct _cairo_rectilinear_stroker { - const cairo_stroke_style_t *stroke_style; - const cairo_matrix_t *ctm; - - cairo_fixed_t half_line_width; - cairo_bool_t do_traps; - void *container; - cairo_point_t current_point; - cairo_point_t first_point; - cairo_bool_t open_sub_path; - - cairo_stroker_dash_t dash; - - cairo_bool_t has_bounds; - cairo_box_t bounds; - - int num_segments; - int segments_size; - segment_t *segments; - segment_t segments_embedded[8]; /* common case is a single rectangle */ -} cairo_rectilinear_stroker_t; - -static void -_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, - const cairo_box_t *boxes, - int num_boxes) -{ - stroker->has_bounds = TRUE; - _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); - - stroker->bounds.p1.x -= stroker->half_line_width; - stroker->bounds.p2.x += stroker->half_line_width; - - stroker->bounds.p1.y -= stroker->half_line_width; - stroker->bounds.p2.y += stroker->half_line_width; -} - -static cairo_bool_t -_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_bool_t do_traps, - void *container) -{ - /* This special-case rectilinear stroker only supports - * miter-joined lines (not curves) and a translation-only matrix - * (though it could probably be extended to support a matrix with - * uniform, integer scaling). - * - * It also only supports horizontal and vertical line_to - * elements. But we don't catch that here, but instead return - * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any - * non-rectilinear line_to is encountered. - */ - if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) - return FALSE; - - /* If the miter limit turns right angles into bevels, then we - * can't use this optimization. Remember, the ratio is - * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, - * which we round for safety. */ - if (stroke_style->miter_limit < M_SQRT2) - return FALSE; - - if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || - stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) - { - return FALSE; - } - - if (! _cairo_matrix_has_unity_scale (ctm)) - return FALSE; - - stroker->stroke_style = stroke_style; - stroker->ctm = ctm; - - stroker->half_line_width = - _cairo_fixed_from_double (stroke_style->line_width / 2.0); - stroker->open_sub_path = FALSE; - stroker->segments = stroker->segments_embedded; - stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); - stroker->num_segments = 0; - - _cairo_stroker_dash_init (&stroker->dash, stroke_style); - - stroker->has_bounds = FALSE; - - stroker->do_traps = do_traps; - stroker->container = container; - - return TRUE; -} - -static void -_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) -{ - if (stroker->segments != stroker->segments_embedded) - free (stroker->segments); -} - -static cairo_status_t -_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, - const cairo_point_t *p1, - const cairo_point_t *p2, - cairo_bool_t is_horizontal, - cairo_bool_t has_join) -{ - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (stroker->num_segments == stroker->segments_size) { - int new_size = stroker->segments_size * 2; - segment_t *new_segments; - - if (stroker->segments == stroker->segments_embedded) { - new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); - if (unlikely (new_segments == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - memcpy (new_segments, stroker->segments, - stroker->num_segments * sizeof (segment_t)); - } else { - new_segments = _cairo_realloc_ab (stroker->segments, - new_size, sizeof (segment_t)); - if (unlikely (new_segments == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - stroker->segments_size = new_size; - stroker->segments = new_segments; - } - - stroker->segments[stroker->num_segments].p1 = *p1; - stroker->segments[stroker->num_segments].p2 = *p2; - stroker->segments[stroker->num_segments].has_join = has_join; - stroker->segments[stroker->num_segments].is_horizontal = is_horizontal; - stroker->num_segments++; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) -{ - cairo_status_t status; - cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; - cairo_fixed_t half_line_width = stroker->half_line_width; - int i; - - for (i = 0; i < stroker->num_segments; i++) { - cairo_point_t *a, *b; - cairo_bool_t lengthen_initial, shorten_final, lengthen_final; - - a = &stroker->segments[i].p1; - b = &stroker->segments[i].p2; - - /* For each segment we generate a single rectangular - * trapezoid. This rectangle is based on a perpendicular - * extension (by half the line width) of the segment endpoints - * after some adjustments of the endpoints to account for caps - * and joins. - */ - - /* We adjust the initial point of the segment to extend the - * rectangle to include the previous cap or join, (this - * adjustment applies to all segments except for the first - * segment of open, butt-capped paths). - */ - lengthen_initial = TRUE; - if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) - lengthen_initial = FALSE; - - /* The adjustment of the final point is trickier. For all but - * the last segment we shorten the segment at the final - * endpoint to not overlap with the subsequent join. For the - * last segment we do the same shortening if the path is - * closed. If the path is open and butt-capped we do no - * adjustment, while if it's open and square-capped we do a - * lengthening adjustment instead to include the cap. - */ - shorten_final = TRUE; - lengthen_final = FALSE; - if (i == stroker->num_segments - 1 && stroker->open_sub_path) { - shorten_final = FALSE; - if (line_cap == CAIRO_LINE_CAP_SQUARE) - lengthen_final = TRUE; - } - - /* Perform the adjustments of the endpoints. */ - if (a->y == b->y) { - if (a->x < b->x) { - if (lengthen_initial) - a->x -= half_line_width; - if (shorten_final) - b->x -= half_line_width; - else if (lengthen_final) - b->x += half_line_width; - } else { - if (lengthen_initial) - a->x += half_line_width; - if (shorten_final) - b->x += half_line_width; - else if (lengthen_final) - b->x -= half_line_width; - } - - if (a->x > b->x) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - } else { - if (a->y < b->y) { - if (lengthen_initial) - a->y -= half_line_width; - if (shorten_final) - b->y -= half_line_width; - else if (lengthen_final) - b->y += half_line_width; - } else { - if (lengthen_initial) - a->y += half_line_width; - if (shorten_final) - b->y += half_line_width; - else if (lengthen_final) - b->y -= half_line_width; - } - - if (a->y > b->y) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - } - - /* Form the rectangle by expanding by half the line width in - * either perpendicular direction. */ - if (a->y == b->y) { - a->y -= half_line_width; - b->y += half_line_width; - } else { - a->x -= half_line_width; - b->x += half_line_width; - } - - if (stroker->do_traps) { - status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); - } else { - cairo_box_t box; - - box.p1 = *a; - box.p2 = *b; - - status = _cairo_boxes_add (stroker->container, &box); - } - if (unlikely (status)) - return status; - } - - stroker->num_segments = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) -{ - cairo_status_t status; - cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; - cairo_fixed_t half_line_width = stroker->half_line_width; - int i; - - for (i = 0; i < stroker->num_segments; i++) { - cairo_point_t *a, *b; - cairo_bool_t is_horizontal; - - a = &stroker->segments[i].p1; - b = &stroker->segments[i].p2; - - is_horizontal = stroker->segments[i].is_horizontal; - - /* Handle the joins for a potentially degenerate segment. */ - if (line_cap == CAIRO_LINE_CAP_BUTT && - stroker->segments[i].has_join && - (i != stroker->num_segments - 1 || - (! stroker->open_sub_path && stroker->dash.dash_starts_on))) - { - cairo_point_t p1 = stroker->segments[i].p1; - cairo_point_t p2 = stroker->segments[i].p2; - cairo_slope_t out_slope; - int j = (i + 1) % stroker->num_segments; - - _cairo_slope_init (&out_slope, - &stroker->segments[j].p1, - &stroker->segments[j].p2); - - if (is_horizontal) { - if (p1.x <= p2.x) { - p1.x = p2.x; - p2.x += half_line_width; - } else { - p1.x = p2.x - half_line_width; - } - if (out_slope.dy >= 0) - p1.y -= half_line_width; - if (out_slope.dy <= 0) - p2.y += half_line_width; - } else { - if (p1.y <= p2.y) { - p1.y = p2.y; - p2.y += half_line_width; - } else { - p1.y = p2.y - half_line_width; - } - if (out_slope.dx >= 0) - p1.x -= half_line_width; - if (out_slope.dx <= 0) - p2.x += half_line_width; - } - - if (stroker->do_traps) { - status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2); - } else { - cairo_box_t box; - - box.p1 = p1; - box.p2 = p2; - - status = _cairo_boxes_add (stroker->container, &box); - } - if (unlikely (status)) - return status; - } - - /* Perform the adjustments of the endpoints. */ - if (is_horizontal) { - if (line_cap == CAIRO_LINE_CAP_SQUARE) { - if (a->x <= b->x) { - a->x -= half_line_width; - b->x += half_line_width; - } else { - a->x += half_line_width; - b->x -= half_line_width; - } - } - - if (a->x > b->x) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - - a->y -= half_line_width; - b->y += half_line_width; - } else { - if (line_cap == CAIRO_LINE_CAP_SQUARE) { - if (a->y <= b->y) { - a->y -= half_line_width; - b->y += half_line_width; - } else { - a->y += half_line_width; - b->y -= half_line_width; - } - } - - if (a->y > b->y) { - cairo_point_t *t; - - t = a; - a = b; - b = t; - } - - a->x -= half_line_width; - b->x += half_line_width; - } - - if (a->x == b->x && a->y == b->y) - continue; - - if (stroker->do_traps) { - status = _cairo_traps_tessellate_rectangle (stroker->container, a, b); - } else { - cairo_box_t box; - - box.p1 = *a; - box.p2 = *b; - - status = _cairo_boxes_add (stroker->container, &box); - } - if (unlikely (status)) - return status; - } - - stroker->num_segments = 0; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_move_to (void *closure, - const cairo_point_t *point) -{ - cairo_rectilinear_stroker_t *stroker = closure; - cairo_status_t status; - - if (stroker->dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (stroker); - if (unlikely (status)) - return status; - - /* reset the dash pattern for new sub paths */ - _cairo_stroker_dash_start (&stroker->dash); - - stroker->current_point = *point; - stroker->first_point = *point; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_line_to (void *closure, - const cairo_point_t *b) -{ - cairo_rectilinear_stroker_t *stroker = closure; - cairo_point_t *a = &stroker->current_point; - cairo_status_t status; - - /* We only support horizontal or vertical elements. */ - assert (a->x == b->x || a->y == b->y); - - /* We don't draw anything for degenerate paths. */ - if (a->x == b->x && a->y == b->y) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, - a->y == b->y, - TRUE); - - stroker->current_point = *b; - stroker->open_sub_path = TRUE; - - return status; -} - -static cairo_status_t -_cairo_rectilinear_stroker_line_to_dashed (void *closure, - const cairo_point_t *point) -{ - cairo_rectilinear_stroker_t *stroker = closure; - const cairo_point_t *a = &stroker->current_point; - const cairo_point_t *b = point; - cairo_bool_t fully_in_bounds; - double sign, remain; - cairo_fixed_t mag; - cairo_status_t status; - cairo_line_t segment; - cairo_bool_t dash_on = FALSE; - cairo_bool_t is_horizontal; - - /* We don't draw anything for degenerate paths. */ - if (a->x == b->x && a->y == b->y) - return CAIRO_STATUS_SUCCESS; - - /* We only support horizontal or vertical elements. */ - assert (a->x == b->x || a->y == b->y); - - fully_in_bounds = TRUE; - if (stroker->has_bounds && - (! _cairo_box_contains_point (&stroker->bounds, a) || - ! _cairo_box_contains_point (&stroker->bounds, b))) - { - fully_in_bounds = FALSE; - } - - is_horizontal = a->y == b->y; - if (is_horizontal) - mag = b->x - a->x; - else - mag = b->y - a->y; - if (mag < 0) { - remain = _cairo_fixed_to_double (-mag); - sign = 1.; - } else { - remain = _cairo_fixed_to_double (mag); - sign = -1.; - } - - segment.p2 = segment.p1 = *a; - while (remain > 0.) { - double step_length; - - step_length = MIN (stroker->dash.dash_remain, remain); - remain -= step_length; - - mag = _cairo_fixed_from_double (sign*remain); - if (is_horizontal) - segment.p2.x = b->x + mag; - else - segment.p2.y = b->y + mag; - - if (stroker->dash.dash_on && - (fully_in_bounds || - _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) - { - status = _cairo_rectilinear_stroker_add_segment (stroker, - &segment.p1, - &segment.p2, - is_horizontal, - remain <= 0.); - if (unlikely (status)) - return status; - - dash_on = TRUE; - } - else - { - dash_on = FALSE; - } - - _cairo_stroker_dash_step (&stroker->dash, step_length); - segment.p1 = segment.p2; - } - - if (stroker->dash.dash_on && ! dash_on && - (fully_in_bounds || - _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) - { - - /* This segment ends on a transition to dash_on, compute a new face - * and add cap for the beginning of the next dash_on step. - */ - - status = _cairo_rectilinear_stroker_add_segment (stroker, - &segment.p1, - &segment.p1, - is_horizontal, - TRUE); - if (unlikely (status)) - return status; - } - - stroker->current_point = *point; - stroker->open_sub_path = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_rectilinear_stroker_close_path (void *closure) -{ - cairo_rectilinear_stroker_t *stroker = closure; - cairo_status_t status; - - /* We don't draw anything for degenerate paths. */ - if (! stroker->open_sub_path) - return CAIRO_STATUS_SUCCESS; - - if (stroker->dash.dashed) { - status = _cairo_rectilinear_stroker_line_to_dashed (stroker, - &stroker->first_point); - } else { - status = _cairo_rectilinear_stroker_line_to (stroker, - &stroker->first_point); - } - if (unlikely (status)) - return status; - - stroker->open_sub_path = FALSE; - - if (stroker->dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (stroker); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_traps_t *traps) -{ - cairo_rectilinear_stroker_t rectilinear_stroker; - cairo_int_status_t status; - - assert (path->is_rectilinear); - - if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, - stroke_style, ctm, - TRUE, traps)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (traps->num_limits) { - _cairo_rectilinear_stroker_limit (&rectilinear_stroker, - traps->limits, - traps->num_limits); - } - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_rectilinear_stroker_move_to, - rectilinear_stroker.dash.dashed ? - _cairo_rectilinear_stroker_line_to_dashed : - _cairo_rectilinear_stroker_line_to, - NULL, - _cairo_rectilinear_stroker_close_path, - &rectilinear_stroker); - if (unlikely (status)) - goto BAIL; - - if (rectilinear_stroker.dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); - - traps->is_rectilinear = 1; - traps->is_rectangular = 1; - /* As we incrementally tessellate, we do not eliminate self-intersections */ - traps->has_intersections = traps->num_traps > 1; -BAIL: - _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - - if (unlikely (status)) - _cairo_traps_clear (traps); - - return status; -} - -cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_boxes_t *boxes) -{ - cairo_rectilinear_stroker_t rectilinear_stroker; - cairo_int_status_t status; - - assert (path->is_rectilinear); - - if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, - stroke_style, ctm, - FALSE, boxes)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (boxes->num_limits) { - _cairo_rectilinear_stroker_limit (&rectilinear_stroker, - boxes->limits, - boxes->num_limits); - } - - status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, - _cairo_rectilinear_stroker_move_to, - rectilinear_stroker.dash.dashed ? - _cairo_rectilinear_stroker_line_to_dashed : - _cairo_rectilinear_stroker_line_to, - NULL, - _cairo_rectilinear_stroker_close_path, - &rectilinear_stroker); - if (unlikely (status)) - goto BAIL; - - if (rectilinear_stroker.dash.dashed) - status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); - else - status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); - if (unlikely (status)) - goto BAIL; - - /* As we incrementally tessellate, we do not eliminate self-intersections */ - status = _cairo_bentley_ottmann_tessellate_boxes (boxes, - CAIRO_FILL_RULE_WINDING, - boxes); - if (unlikely (status)) - goto BAIL; - - _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - - return CAIRO_STATUS_SUCCESS; - -BAIL: - _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - _cairo_boxes_clear (boxes); - return status; -} diff --git a/gfx/cairo/cairo/src/cairo-path.c b/gfx/cairo/cairo/src/cairo-path.c index 28182c0e4003..566e86f5a8ec 100644 --- a/gfx/cairo/cairo/src/cairo-path.c +++ b/gfx/cairo/cairo/src/cairo-path.c @@ -37,6 +37,7 @@ #include "cairoint.h" #include "cairo-private.h" +#include "cairo-backend-private.h" #include "cairo-error-private.h" #include "cairo-path-private.h" #include "cairo-path-fixed-private.h" @@ -48,14 +49,13 @@ * * Paths are the most basic drawing tools and are primarily used to implicitly * generate simple masks. - */ + **/ static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; /* Closure for path interpretation. */ typedef struct cairo_path_count { int count; - cairo_point_t current_point; } cpc_t; static cairo_status_t @@ -66,8 +66,6 @@ _cpc_move_to (void *closure, cpc->count += 2; - cpc->current_point = *point; - return CAIRO_STATUS_SUCCESS; } @@ -79,8 +77,6 @@ _cpc_line_to (void *closure, cpc->count += 2; - cpc->current_point = *point; - return CAIRO_STATUS_SUCCESS; } @@ -94,8 +90,6 @@ _cpc_curve_to (void *closure, cpc->count += 4; - cpc->current_point = *p3; - return CAIRO_STATUS_SUCCESS; } @@ -119,12 +113,9 @@ _cairo_path_count (cairo_path_t *path, cpc_t cpc; cpc.count = 0; - cpc.current_point.x = 0; - cpc.current_point.y = 0; if (flatten) { status = _cairo_path_fixed_interpret_flat (path_fixed, - CAIRO_DIRECTION_FORWARD, _cpc_move_to, _cpc_line_to, _cpc_close_path, @@ -132,7 +123,6 @@ _cairo_path_count (cairo_path_t *path, tolerance); } else { status = _cairo_path_fixed_interpret (path_fixed, - CAIRO_DIRECTION_FORWARD, _cpc_move_to, _cpc_line_to, _cpc_curve_to, @@ -149,8 +139,7 @@ _cairo_path_count (cairo_path_t *path, /* Closure for path interpretation. */ typedef struct cairo_path_populate { cairo_path_data_t *data; - cairo_gstate_t *gstate; - cairo_point_t current_point; + cairo_t *cr; } cpp_t; static cairo_status_t @@ -164,7 +153,7 @@ _cpp_move_to (void *closure, x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x, &y); + _cairo_backend_to_user (cpp->cr, &x, &y); data->header.type = CAIRO_PATH_MOVE_TO; data->header.length = 2; @@ -175,8 +164,6 @@ _cpp_move_to (void *closure, cpp->data += data->header.length; - cpp->current_point = *point; - return CAIRO_STATUS_SUCCESS; } @@ -191,7 +178,7 @@ _cpp_line_to (void *closure, x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x, &y); + _cairo_backend_to_user (cpp->cr, &x, &y); data->header.type = CAIRO_PATH_LINE_TO; data->header.length = 2; @@ -202,8 +189,6 @@ _cpp_line_to (void *closure, cpp->data += data->header.length; - cpp->current_point = *point; - return CAIRO_STATUS_SUCCESS; } @@ -221,15 +206,15 @@ _cpp_curve_to (void *closure, x1 = _cairo_fixed_to_double (p1->x); y1 = _cairo_fixed_to_double (p1->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x1, &y1); + _cairo_backend_to_user (cpp->cr, &x1, &y1); x2 = _cairo_fixed_to_double (p2->x); y2 = _cairo_fixed_to_double (p2->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x2, &y2); + _cairo_backend_to_user (cpp->cr, &x2, &y2); x3 = _cairo_fixed_to_double (p3->x); y3 = _cairo_fixed_to_double (p3->y); - _cairo_gstate_backend_to_user (cpp->gstate, &x3, &y3); + _cairo_backend_to_user (cpp->cr, &x3, &y3); data->header.type = CAIRO_PATH_CURVE_TO; data->header.length = 4; @@ -246,8 +231,6 @@ _cpp_curve_to (void *closure, cpp->data += data->header.length; - cpp->current_point = *p3; - return CAIRO_STATUS_SUCCESS; } @@ -268,29 +251,24 @@ _cpp_close_path (void *closure) static cairo_status_t _cairo_path_populate (cairo_path_t *path, cairo_path_fixed_t *path_fixed, - cairo_gstate_t *gstate, + cairo_t *cr, cairo_bool_t flatten) { cairo_status_t status; cpp_t cpp; cpp.data = path->data; - cpp.gstate = gstate; - cpp.current_point.x = 0; - cpp.current_point.y = 0; + cpp.cr = cr; if (flatten) { - double tolerance = _cairo_gstate_get_tolerance (gstate); status = _cairo_path_fixed_interpret_flat (path_fixed, - CAIRO_DIRECTION_FORWARD, _cpp_move_to, _cpp_line_to, _cpp_close_path, &cpp, - tolerance); + cairo_get_tolerance (cr)); } else { status = _cairo_path_fixed_interpret (path_fixed, - CAIRO_DIRECTION_FORWARD, _cpp_move_to, _cpp_line_to, _cpp_curve_to, @@ -316,7 +294,7 @@ _cairo_path_create_in_error (cairo_status_t status) if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_path_t*) &_cairo_path_nil; - path = malloc (sizeof (cairo_path_t)); + path = _cairo_malloc (sizeof (cairo_path_t)); if (unlikely (path == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; @@ -331,19 +309,19 @@ _cairo_path_create_in_error (cairo_status_t status) static cairo_path_t * _cairo_path_create_internal (cairo_path_fixed_t *path_fixed, - cairo_gstate_t *gstate, + cairo_t *cr, cairo_bool_t flatten) { cairo_path_t *path; - path = malloc (sizeof (cairo_path_t)); + path = _cairo_malloc (sizeof (cairo_path_t)); if (unlikely (path == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_path_t*) &_cairo_path_nil; } path->num_data = _cairo_path_count (path, path_fixed, - _cairo_gstate_get_tolerance (gstate), + cairo_get_tolerance (cr), flatten); if (path->num_data < 0) { free (path); @@ -359,8 +337,7 @@ _cairo_path_create_internal (cairo_path_fixed_t *path_fixed, return (cairo_path_t*) &_cairo_path_nil; } - path->status = _cairo_path_populate (path, path_fixed, - gstate, flatten); + path->status = _cairo_path_populate (path, path_fixed, cr, flatten); } else { path->data = NULL; path->status = CAIRO_STATUS_SUCCESS; @@ -382,6 +359,8 @@ _cairo_path_create_internal (cairo_path_fixed_t *path_fixed, * pointer to a #cairo_path_t returned by a cairo function. Any path * that is created manually (ie. outside of cairo) should be destroyed * manually as well. + * + * Since: 1.0 **/ void cairo_path_destroy (cairo_path_t *path) @@ -389,19 +368,19 @@ cairo_path_destroy (cairo_path_t *path) if (path == NULL || path == &_cairo_path_nil) return; - if (path->data) - free (path->data); + free (path->data); free (path); } +slim_hidden_def (cairo_path_destroy); /** * _cairo_path_create: * @path: a fixed-point, device-space path to be converted and copied - * @gstate: the current graphics state + * @cr: the current graphics context * * Creates a user-space #cairo_path_t copy of the given device-space - * @path. The @gstate parameter provides the inverse CTM for the + * @path. The @cr parameter provides the inverse CTM for the * conversion. * * Return value: the new copy of the path. If there is insufficient @@ -410,19 +389,19 @@ cairo_path_destroy (cairo_path_t *path) * data==%NULL. **/ cairo_path_t * -_cairo_path_create (cairo_path_fixed_t *path, - cairo_gstate_t *gstate) +_cairo_path_create (cairo_path_fixed_t *path, + cairo_t *cr) { - return _cairo_path_create_internal (path, gstate, FALSE); + return _cairo_path_create_internal (path, cr, FALSE); } /** * _cairo_path_create_flat: * @path: a fixed-point, device-space path to be flattened, converted and copied - * @gstate: the current graphics state + * @cr: the current graphics context * * Creates a flattened, user-space #cairo_path_t copy of the given - * device-space @path. The @gstate parameter provide the inverse CTM + * device-space @path. The @cr parameter provide the inverse CTM * for the conversion, as well as the tolerance value to control the * accuracy of the flattening. * @@ -433,9 +412,9 @@ _cairo_path_create (cairo_path_fixed_t *path, **/ cairo_path_t * _cairo_path_create_flat (cairo_path_fixed_t *path, - cairo_gstate_t *gstate) + cairo_t *cr) { - return _cairo_path_create_internal (path, gstate, TRUE); + return _cairo_path_create_internal (path, cr, TRUE); } /** @@ -453,17 +432,6 @@ _cairo_path_append_to_context (const cairo_path_t *path, cairo_t *cr) { const cairo_path_data_t *p, *end; - cairo_fixed_t x1_fixed, y1_fixed; - cairo_fixed_t x2_fixed, y2_fixed; - cairo_fixed_t x3_fixed, y3_fixed; - cairo_matrix_t user_to_backend; - cairo_status_t status; - double x, y; - - user_to_backend = cr->gstate->ctm; - cairo_matrix_multiply (&user_to_backend, - &user_to_backend, - &cr->gstate->target->device_transform); end = &path->data[path->num_data]; for (p = &path->data[0]; p < end; p += p->header.length) { @@ -472,64 +440,39 @@ _cairo_path_append_to_context (const cairo_path_t *path, if (unlikely (p->header.length < 2)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - x = p[1].point.x, y = p[1].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x1_fixed = _cairo_fixed_from_double (x); - y1_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed); + cairo_move_to (cr, p[1].point.x, p[1].point.y); break; case CAIRO_PATH_LINE_TO: if (unlikely (p->header.length < 2)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - x = p[1].point.x, y = p[1].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x1_fixed = _cairo_fixed_from_double (x); - y1_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed); + cairo_line_to (cr, p[1].point.x, p[1].point.y); break; case CAIRO_PATH_CURVE_TO: if (unlikely (p->header.length < 4)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - x = p[1].point.x, y = p[1].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x1_fixed = _cairo_fixed_from_double (x); - y1_fixed = _cairo_fixed_from_double (y); - - x = p[2].point.x, y = p[2].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x2_fixed = _cairo_fixed_from_double (x); - y2_fixed = _cairo_fixed_from_double (y); - - x = p[3].point.x, y = p[3].point.y; - cairo_matrix_transform_point (&user_to_backend, &x, &y); - x3_fixed = _cairo_fixed_from_double (x); - y3_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_curve_to (cr->path, - x1_fixed, y1_fixed, - x2_fixed, y2_fixed, - x3_fixed, y3_fixed); + cairo_curve_to (cr, + p[1].point.x, p[1].point.y, + p[2].point.x, p[2].point.y, + p[3].point.x, p[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: if (unlikely (p->header.length < 1)) return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); - status = _cairo_path_fixed_close_path (cr->path); + cairo_close_path (cr); break; default: return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); } - if (unlikely (status)) - return status; + if (unlikely (cr->status)) + return cr->status; } return CAIRO_STATUS_SUCCESS; diff --git a/gfx/cairo/cairo/src/cairo-pattern-inline.h b/gfx/cairo/cairo/src/cairo-pattern-inline.h new file mode 100644 index 000000000000..97e8ea034e42 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pattern-inline.h @@ -0,0 +1,65 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 D. Worth + */ + +#ifndef CAIRO_PATTERN_INLINE_H +#define CAIRO_PATTERN_INLINE_H + +#include "cairo-pattern-private.h" + +#include "cairo-list-inline.h" + +CAIRO_BEGIN_DECLS + +static inline void +_cairo_pattern_add_observer (cairo_pattern_t *pattern, + cairo_pattern_observer_t *observer, + void (*func) (cairo_pattern_observer_t *, + cairo_pattern_t *, + unsigned int)) +{ + observer->notify = func; + cairo_list_add (&observer->link, &pattern->observers); +} + +static inline cairo_surface_t * +_cairo_pattern_get_source (const cairo_surface_pattern_t *pattern, + cairo_rectangle_int_t *extents) +{ + return _cairo_surface_get_source (pattern->surface, extents); +} + +CAIRO_END_DECLS + +#endif /* CAIRO_PATTERN_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-pattern-private.h b/gfx/cairo/cairo/src/cairo-pattern-private.h new file mode 100644 index 000000000000..26d584e686ad --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pattern-private.h @@ -0,0 +1,375 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 D. Worth + */ + +#ifndef CAIRO_PATTERN_PRIVATE_H +#define CAIRO_PATTERN_PRIVATE_H + +#include "cairo-error-private.h" +#include "cairo-types-private.h" +#include "cairo-list-private.h" +#include "cairo-surface-private.h" + +#include /* FILE* */ + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_pattern_observer cairo_pattern_observer_t; + +enum { + CAIRO_PATTERN_NOTIFY_MATRIX = 0x1, + CAIRO_PATTERN_NOTIFY_FILTER = 0x2, + CAIRO_PATTERN_NOTIFY_EXTEND = 0x4, + CAIRO_PATTERN_NOTIFY_OPACITY = 0x9, +}; + +struct _cairo_pattern_observer { + void (*notify) (cairo_pattern_observer_t *, + cairo_pattern_t *pattern, + unsigned int flags); + cairo_list_t link; +}; + +struct _cairo_pattern { + cairo_reference_count_t ref_count; + cairo_status_t status; + cairo_user_data_array_t user_data; + cairo_list_t observers; + + cairo_pattern_type_t type; + + cairo_filter_t filter; + cairo_extend_t extend; + cairo_bool_t has_component_alpha; + + cairo_matrix_t matrix; + double opacity; +}; + +struct _cairo_solid_pattern { + cairo_pattern_t base; + cairo_color_t color; +}; + +typedef struct _cairo_surface_pattern { + cairo_pattern_t base; + + cairo_surface_t *surface; +} cairo_surface_pattern_t; + +typedef struct _cairo_gradient_stop { + double offset; + cairo_color_stop_t color; +} cairo_gradient_stop_t; + +typedef struct _cairo_gradient_pattern { + cairo_pattern_t base; + + unsigned int n_stops; + unsigned int stops_size; + cairo_gradient_stop_t *stops; + cairo_gradient_stop_t stops_embedded[2]; +} cairo_gradient_pattern_t; + +typedef struct _cairo_linear_pattern { + cairo_gradient_pattern_t base; + + cairo_point_double_t pd1; + cairo_point_double_t pd2; +} cairo_linear_pattern_t; + +typedef struct _cairo_radial_pattern { + cairo_gradient_pattern_t base; + + cairo_circle_double_t cd1; + cairo_circle_double_t cd2; +} cairo_radial_pattern_t; + +typedef union { + cairo_gradient_pattern_t base; + + cairo_linear_pattern_t linear; + cairo_radial_pattern_t radial; +} cairo_gradient_pattern_union_t; + +/* + * A mesh patch is a tensor-product patch (bicubic Bezier surface + * patch). It has 16 control points. Each set of 4 points along the + * sides of the 4x4 grid of control points is a Bezier curve that + * defines one side of the patch. A color is assigned to each + * corner. The inner 4 points provide additional control over the + * shape and the color mapping. + * + * Cairo uses the same convention as the PDF Reference for numbering + * the points and side of the patch. + * + * + * Side 1 + * + * p[0][3] p[1][3] p[2][3] p[3][3] + * Side 0 p[0][2] p[1][2] p[2][2] p[3][2] Side 2 + * p[0][1] p[1][1] p[2][1] p[3][1] + * p[0][0] p[1][0] p[2][0] p[3][0] + * + * Side 3 + * + * + * Point Color + * ------------------------- + * points[0][0] colors[0] + * points[0][3] colors[1] + * points[3][3] colors[2] + * points[3][0] colors[3] + */ + +typedef struct _cairo_mesh_patch { + cairo_point_double_t points[4][4]; + cairo_color_t colors[4]; +} cairo_mesh_patch_t; + +typedef struct _cairo_mesh_pattern { + cairo_pattern_t base; + + cairo_array_t patches; + cairo_mesh_patch_t *current_patch; + int current_side; + cairo_bool_t has_control_point[4]; + cairo_bool_t has_color[4]; +} cairo_mesh_pattern_t; + +typedef struct _cairo_raster_source_pattern { + cairo_pattern_t base; + + cairo_content_t content; + cairo_rectangle_int_t extents; + + cairo_raster_source_acquire_func_t acquire; + cairo_raster_source_release_func_t release; + cairo_raster_source_snapshot_func_t snapshot; + cairo_raster_source_copy_func_t copy; + cairo_raster_source_finish_func_t finish; + + /* an explicit pre-allocated member in preference to the general user-data */ + void *user_data; +} cairo_raster_source_pattern_t; + +typedef union { + cairo_pattern_t base; + + cairo_solid_pattern_t solid; + cairo_surface_pattern_t surface; + cairo_gradient_pattern_union_t gradient; + cairo_mesh_pattern_t mesh; + cairo_raster_source_pattern_t raster_source; +} cairo_pattern_union_t; + +/* cairo-pattern.c */ + +cairo_private cairo_pattern_t * +_cairo_pattern_create_in_error (cairo_status_t status); + +cairo_private cairo_status_t +_cairo_pattern_create_copy (cairo_pattern_t **pattern, + const cairo_pattern_t *other); + +cairo_private void +_cairo_pattern_init (cairo_pattern_t *pattern, + cairo_pattern_type_t type); + +cairo_private cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private void +_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private cairo_status_t +_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private void +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + const cairo_color_t *color); + +cairo_private void +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface); + +cairo_private void +_cairo_pattern_fini (cairo_pattern_t *pattern); + +cairo_private cairo_pattern_t * +_cairo_pattern_create_solid (const cairo_color_t *color); + +cairo_private void +_cairo_pattern_transform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm_inverse); + +cairo_private void +_cairo_pattern_pretransform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm); + +cairo_private cairo_bool_t +_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_pattern_is_opaque (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_bool_t +_cairo_pattern_is_clear (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents, + cairo_color_t *color); + +cairo_private cairo_bool_t +_cairo_pattern_is_constant_alpha (const cairo_pattern_t *abstract_pattern, + const cairo_rectangle_int_t *extents, + double *alpha); + +cairo_private void +_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient, + double max_value, + cairo_matrix_t *out_matrix, + cairo_circle_double_t out_circle[2]); + +cairo_private cairo_bool_t +_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial); + +cairo_private void +_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient, + double x0, double y0, + double x1, double y1, + double tolerance, + double out_range[2]); + +cairo_private void +_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient, + double t, + cairo_circle_double_t *out_circle); + +cairo_private void +_cairo_pattern_alpha_range (const cairo_pattern_t *pattern, + double *out_min, + double *out_max); + +cairo_private cairo_bool_t +_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh, + double *out_xmin, + double *out_ymin, + double *out_xmax, + double *out_ymax); + +cairo_private void +_cairo_pattern_sampled_area (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *sample); + +cairo_private void +_cairo_pattern_get_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_bool_t is_vector); + +cairo_private cairo_int_status_t +_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents); + +cairo_private unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern); + +cairo_private unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_linear_pattern_t *linear); + +cairo_private unsigned long +_cairo_radial_pattern_hash (unsigned long hash, + const cairo_radial_pattern_t *radial); + +cairo_private cairo_bool_t +_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, + const cairo_linear_pattern_t *b); + +cairo_private unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, + const cairo_radial_pattern_t *b); + +cairo_private cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, + const cairo_pattern_t *b); + +cairo_private cairo_filter_t +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern); + +/* cairo-mesh-pattern-rasterizer.c */ + +cairo_private void +_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh, + void *data, + int width, + int height, + int stride, + double x_offset, + double y_offset); + +cairo_private cairo_surface_t * +_cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern, + cairo_surface_t *target, + const cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern, + cairo_surface_t *surface); + +cairo_private cairo_status_t +_cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern); + +cairo_private cairo_status_t +_cairo_raster_source_pattern_init_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other); + +cairo_private void +_cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern); + +cairo_private void +_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern); + +CAIRO_END_DECLS + +#endif /* CAIRO_PATTERN_PRIVATE */ diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c index 0c51804f8cde..32811af59542 100644 --- a/gfx/cairo/cairo/src/cairo-pattern.c +++ b/gfx/cairo/cairo/src/cairo-pattern.c @@ -29,8 +29,20 @@ */ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-path-private.h" +#include "cairo-pattern-private.h" +#include "cairo-recording-surface-inline.h" +#include "cairo-surface-snapshot-inline.h" + +#include + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ /** * SECTION:cairo-pattern @@ -39,73 +51,113 @@ * @See_Also: #cairo_t, #cairo_surface_t * * #cairo_pattern_t is the paint with which cairo draws. - * The primary use of patterns is as the source for all cairo drawing - * operations, although they can also be used as masks, that is, as the + * The primary use of patterns is as the source for all cairo drawing + * operations, although they can also be used as masks, that is, as the * brush too. * * A cairo pattern is created by using one of the many constructors, - * of the form cairo_pattern_create_type() + * of the form + * cairo_pattern_create_type() * or implicitly through - * cairo_set_source_type() functions. - */ + * cairo_set_source_type() + * functions. + **/ -#if HAS_FREED_POOL -static freed_pool_t freed_pattern_pool[4]; -#endif +static freed_pool_t freed_pattern_pool[5]; static const cairo_solid_pattern_t _cairo_pattern_nil = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NO_MEMORY, /* status */ { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ + CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ + FALSE, /* has component alpha */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + 1.0 /* opacity */ + } }; static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_NULL_POINTER, /* status */ { 0, 0, 0, NULL }, /* user_data */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ + CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ + FALSE, /* has component alpha */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + 1.0 /* opacity */ + } }; const cairo_solid_pattern_t _cairo_pattern_black = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_FILTER_NEAREST, /* filter */ + CAIRO_EXTEND_REPEAT, /* extend */ + FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + 1.0 /* opacity */ + }, { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */ }; const cairo_solid_pattern_t _cairo_pattern_clear = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_FILTER_NEAREST, /* filter */ + CAIRO_EXTEND_REPEAT, /* extend */ + FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + 1.0 /* opacity */ + }, { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */ }; const cairo_solid_pattern_t _cairo_pattern_white = { - { CAIRO_PATTERN_TYPE_SOLID, /* type */ + { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ CAIRO_STATUS_SUCCESS, /* status */ { 0, 0, 0, NULL }, /* user_data */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_FILTER_NEAREST, /* filter */ + CAIRO_EXTEND_REPEAT, /* extend */ + FALSE, /* has component alpha */ { 1., 0., 0., 1., 0., 0., }, /* matrix */ - CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */ + 1.0 /* opacity */ + }, { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */ }; +static void +_cairo_pattern_notify_observers (cairo_pattern_t *pattern, + unsigned int flags) +{ + cairo_pattern_observer_t *pos; + + cairo_list_foreach_entry (pos, cairo_pattern_observer_t, &pattern->observers, link) + pos->notify (pos, pattern, flags); +} + /** * _cairo_pattern_set_error: * @pattern: a pattern @@ -137,7 +189,7 @@ _cairo_pattern_set_error (cairo_pattern_t *pattern, return _cairo_error (status); } -static void +void _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) { #if HAVE_VALGRIND @@ -154,6 +206,11 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) case CAIRO_PATTERN_TYPE_RADIAL: VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); break; + case CAIRO_PATTERN_TYPE_MESH: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + break; } #endif @@ -166,16 +223,20 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) _cairo_user_data_array_init (&pattern->user_data); - if (type == CAIRO_PATTERN_TYPE_SURFACE) + if (type == CAIRO_PATTERN_TYPE_SURFACE || + type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT; else pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT; pattern->filter = CAIRO_FILTER_DEFAULT; + pattern->opacity = 1.0; pattern->has_component_alpha = FALSE; cairo_matrix_init_identity (&pattern->matrix); + + cairo_list_init (&pattern->observers); } static cairo_status_t @@ -219,10 +280,24 @@ _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t *pattern, + const cairo_mesh_pattern_t *other) +{ + *pattern = *other; + + _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t)); + return _cairo_array_append_multiple (&pattern->patches, + _cairo_array_index_const (&other->patches, 0), + _cairo_array_num_elements (&other->patches)); +} + cairo_status_t _cairo_pattern_init_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other) { + cairo_status_t status; + if (other->status) return _cairo_pattern_set_error (pattern, other->status); @@ -248,7 +323,6 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, case CAIRO_PATTERN_TYPE_RADIAL: { cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; - cairo_status_t status; if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); @@ -261,11 +335,29 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, return status; } break; + case CAIRO_PATTERN_TYPE_MESH: { + cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern; + cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t))); + + status = _cairo_mesh_pattern_init_copy (dst, src); + if (unlikely (status)) + return status; + + } break; + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { + status = _cairo_raster_source_pattern_init_copy (pattern, other); + if (unlikely (status)) + return status; + } break; } /* The reference count and user_data array are unique to the copy. */ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); + cairo_list_init (&pattern->observers); return CAIRO_STATUS_SUCCESS; } @@ -293,12 +385,19 @@ _cairo_pattern_init_static_copy (cairo_pattern_t *pattern, case CAIRO_PATTERN_TYPE_RADIAL: size = sizeof (cairo_radial_pattern_t); break; + case CAIRO_PATTERN_TYPE_MESH: + size = sizeof (cairo_mesh_pattern_t); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + size = sizeof (cairo_raster_source_pattern_t); + break; } memcpy (pattern, other, size); CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); _cairo_user_data_array_init (&pattern->user_data); + cairo_list_init (&pattern->observers); } cairo_status_t @@ -324,11 +423,11 @@ _cairo_pattern_init_snapshot (cairo_pattern_t *pattern, cairo_surface_destroy (surface); - if (surface_pattern->surface->status) - return surface_pattern->surface->status; - } + status = surface_pattern->surface->status; + } else if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + status = _cairo_raster_source_pattern_snapshot (pattern); - return CAIRO_STATUS_SUCCESS; + return status; } void @@ -353,21 +452,35 @@ _cairo_pattern_fini (cairo_pattern_t *pattern) if (gradient->stops && gradient->stops != gradient->stops_embedded) free (gradient->stops); } break; + case CAIRO_PATTERN_TYPE_MESH: { + cairo_mesh_pattern_t *mesh = + (cairo_mesh_pattern_t *) pattern; + + _cairo_array_fini (&mesh->patches); + } break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + _cairo_raster_source_pattern_finish (pattern); + break; } #if HAVE_VALGRIND switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t)); + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t)); + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t)); + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: - VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t)); + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_MESH: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: break; } #endif @@ -385,16 +498,22 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern_out, switch (other->type) { case CAIRO_PATTERN_TYPE_SOLID: - pattern = malloc (sizeof (cairo_solid_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: - pattern = malloc (sizeof (cairo_surface_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: - pattern = malloc (sizeof (cairo_linear_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: - pattern = malloc (sizeof (cairo_radial_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_radial_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_MESH: + pattern = _cairo_malloc (sizeof (cairo_mesh_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + pattern = _cairo_malloc (sizeof (cairo_raster_source_pattern_t)); break; default: ASSERT_NOT_REACHED; @@ -414,7 +533,6 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern_out, return CAIRO_STATUS_SUCCESS; } - void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, const cairo_color_t *color) @@ -450,31 +568,31 @@ _cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, pattern->stops = NULL; } -void +static void _cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, double x0, double y0, double x1, double y1) { _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR); - pattern->p1.x = _cairo_fixed_from_double (x0); - pattern->p1.y = _cairo_fixed_from_double (y0); - pattern->p2.x = _cairo_fixed_from_double (x1); - pattern->p2.y = _cairo_fixed_from_double (y1); + pattern->pd1.x = x0; + pattern->pd1.y = y0; + pattern->pd2.x = x1; + pattern->pd2.y = y1; } -void +static void _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, double cx0, double cy0, double radius0, double cx1, double cy1, double radius1) { _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL); - pattern->c1.x = _cairo_fixed_from_double (cx0); - pattern->c1.y = _cairo_fixed_from_double (cy0); - pattern->r1 = _cairo_fixed_from_double (fabs (radius0)); - pattern->c2.x = _cairo_fixed_from_double (cx1); - pattern->c2.y = _cairo_fixed_from_double (cy1); - pattern->r2 = _cairo_fixed_from_double (fabs (radius1)); + pattern->cd1.center.x = cx0; + pattern->cd1.center.y = cy0; + pattern->cd1.radius = fabs (radius0); + pattern->cd2.center.x = cx1; + pattern->cd2.center.y = cy1; + pattern->cd2.radius = fabs (radius1); } cairo_pattern_t * @@ -486,7 +604,7 @@ _cairo_pattern_create_solid (const cairo_color_t *color) _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); if (unlikely (pattern == NULL)) { /* None cached, need to create a new pattern. */ - pattern = malloc (sizeof (cairo_solid_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_solid_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil; @@ -535,21 +653,13 @@ _cairo_pattern_create_in_error (cairo_status_t status) * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). + * + * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_rgb (double red, double green, double blue) { - cairo_color_t color; - - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - - _cairo_color_init_rgb (&color, red, green, blue); - - CAIRO_MUTEX_INITIALIZE (); - - return _cairo_pattern_create_solid (&color); + return cairo_pattern_create_rgba (red, green, blue, 1.0); } slim_hidden_def (cairo_pattern_create_rgb); @@ -573,6 +683,8 @@ slim_hidden_def (cairo_pattern_create_rgb); * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). + * + * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_rgba (double red, double green, double blue, @@ -607,6 +719,8 @@ slim_hidden_def (cairo_pattern_create_rgba); * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). + * + * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface) @@ -624,7 +738,7 @@ cairo_pattern_create_for_surface (cairo_surface_t *surface) pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); if (unlikely (pattern == NULL)) { - pattern = malloc (sizeof (cairo_surface_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_surface_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *)&_cairo_pattern_nil.base; @@ -665,6 +779,8 @@ slim_hidden_def (cairo_pattern_create_for_surface); * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). + * + * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_linear (double x0, double y0, double x1, double y1) @@ -674,7 +790,7 @@ cairo_pattern_create_linear (double x0, double y0, double x1, double y1) pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); if (unlikely (pattern == NULL)) { - pattern = malloc (sizeof (cairo_linear_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_linear_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil.base; @@ -716,6 +832,8 @@ cairo_pattern_create_linear (double x0, double y0, double x1, double y1) * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). + * + * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, @@ -726,7 +844,7 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0, pattern = _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); if (unlikely (pattern == NULL)) { - pattern = malloc (sizeof (cairo_radial_pattern_t)); + pattern = _cairo_malloc (sizeof (cairo_radial_pattern_t)); if (unlikely (pattern == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_pattern_t *) &_cairo_pattern_nil.base; @@ -741,6 +859,186 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0, return &pattern->base.base; } +/* This order is specified in the diagram in the documentation for + * cairo_pattern_create_mesh() */ +static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 }; +static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 }; +static const int mesh_control_point_i[4] = { 1, 1, 2, 2 }; +static const int mesh_control_point_j[4] = { 1, 2, 2, 1 }; + +/** + * cairo_pattern_create_mesh: + * + * Create a new mesh pattern. + * + * Mesh patterns are tensor-product patch meshes (type 7 shadings in + * PDF). Mesh patterns may also be used to create other types of + * shadings that are special cases of tensor-product patch meshes such + * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded + * triangle meshes (type 4 and 5 shadings in PDF). + * + * Mesh patterns consist of one or more tensor-product patches, which + * should be defined before using the mesh pattern. Using a mesh + * pattern with a partially defined patch as source or mask will put + * the context in an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2, + * 3) and by 4 additional control points (P0, P1, P2, P3) that provide + * further control over the patch and complete the definition of the + * tensor-product patch. The corner C0 is the first point of the + * patch. + * + * Degenerate sides are permitted so straight lines may be used. A + * zero length line on one side may be used to create 3 sided patches. + * + * + * C1 Side 1 C2 + * +---------------+ + * | | + * | P1 P2 | + * | | + * Side 0 | | Side 2 + * | | + * | | + * | P0 P3 | + * | | + * +---------------+ + * C0 Side 3 C3 + * + * + * Each patch is constructed by first calling + * cairo_mesh_pattern_begin_patch(), then cairo_mesh_pattern_move_to() + * to specify the first point in the patch (C0). Then the sides are + * specified with calls to cairo_mesh_pattern_curve_to() and + * cairo_mesh_pattern_line_to(). + * + * The four additional control points (P0, P1, P2, P3) in a patch can + * be specified with cairo_mesh_pattern_set_control_point(). + * + * At each corner of the patch (C0, C1, C2, C3) a color may be + * specified with cairo_mesh_pattern_set_corner_color_rgb() or + * cairo_mesh_pattern_set_corner_color_rgba(). Any corner whose color + * is not explicitly specified defaults to transparent black. + * + * A Coons patch is a special case of the tensor-product patch where + * the control points are implicitly defined by the sides of the + * patch. The default value for any control point not specified is the + * implicit value for a Coons patch, i.e. if no control points are + * specified the patch is a Coons patch. + * + * A triangle is a special case of the tensor-product patch where the + * control points are implicitly defined by the sides of the patch, + * all the sides are lines and one of them has length 0, i.e. if the + * patch is specified using just 3 lines, it is a triangle. If the + * corners connected by the 0-length side have the same color, the + * patch is a Gouraud-shaded triangle. + * + * Patches may be oriented differently to the above diagram. For + * example the first point could be at the top left. The diagram only + * shows the relationship between the sides, corners and control + * points. Regardless of where the first point is located, when + * specifying colors, corner 0 will always be the first point, corner + * 1 the point between side 0 and side 1 etc. + * + * Calling cairo_mesh_pattern_end_patch() completes the current + * patch. If less than 4 sides have been defined, the first missing + * side is defined as a line from the current point to the first point + * of the patch (C0) and the other sides are degenerate lines from C0 + * to C0. The corners between the added sides will all be coincident + * with C0 of the patch and their color will be set to be the same as + * the color of C0. + * + * Additional patches may be added with additional calls to + * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch(). + * + * + * cairo_pattern_t *pattern = cairo_pattern_create_mesh (); + * + * /* Add a Coons patch */ + * cairo_mesh_pattern_begin_patch (pattern); + * cairo_mesh_pattern_move_to (pattern, 0, 0); + * cairo_mesh_pattern_curve_to (pattern, 30, -30, 60, 30, 100, 0); + * cairo_mesh_pattern_curve_to (pattern, 60, 30, 130, 60, 100, 100); + * cairo_mesh_pattern_curve_to (pattern, 60, 70, 30, 130, 0, 100); + * cairo_mesh_pattern_curve_to (pattern, 30, 70, -30, 30, 0, 0); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0); + * cairo_mesh_pattern_end_patch (pattern); + * + * /* Add a Gouraud-shaded triangle */ + * cairo_mesh_pattern_begin_patch (pattern) + * cairo_mesh_pattern_move_to (pattern, 100, 100); + * cairo_mesh_pattern_line_to (pattern, 130, 130); + * cairo_mesh_pattern_line_to (pattern, 130, 70); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0); + * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1); + * cairo_mesh_pattern_end_patch (pattern) + * + * + * When two patches overlap, the last one that has been added is drawn + * over the first one. + * + * When a patch folds over itself, points are sorted depending on + * their parameter coordinates inside the patch. The v coordinate + * ranges from 0 to 1 when moving from side 3 to side 1; the u + * coordinate ranges from 0 to 1 when going from side 0 to side + * 2. Points with higher v coordinate hide points with lower v + * coordinate. When two points have the same v coordinate, the one + * with higher u coordinate is above. This means that points nearer to + * side 1 are above points nearer to side 3; when this is not + * sufficient to decide which point is above (for example when both + * points belong to side 1 or side 3) points nearer to side 2 are + * above points nearer to side 0. + * + * For a complete definition of tensor-product patches, see the PDF + * specification (ISO32000), which describes the parametrization in + * detail. + * + * Note: The coordinates are always in pattern space. For a new + * pattern, pattern space is identical to user space, but the + * relationship between the spaces can be changed with + * cairo_pattern_set_matrix(). + * + * 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 finished with + * it. + * + * This function will always return a valid pointer, but if an error + * occurred the pattern status will be set to an error. To inspect the + * status of a pattern use cairo_pattern_status(). + * + * Since: 1.12 + **/ +cairo_pattern_t * +cairo_pattern_create_mesh (void) +{ + cairo_mesh_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]); + if (unlikely (pattern == NULL)) { + pattern = _cairo_malloc (sizeof (cairo_mesh_pattern_t)); + if (unlikely (pattern == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_pattern_t *) &_cairo_pattern_nil.base; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH); + _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t)); + pattern->current_patch = NULL; + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + return &pattern->base; +} + /** * cairo_pattern_reference: * @pattern: a #cairo_pattern_t @@ -749,10 +1047,12 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0, * @pattern from being destroyed until a matching call to * cairo_pattern_destroy() is made. * - * The number of references to a #cairo_pattern_t can be get using - * cairo_pattern_get_reference_count(). + * Use cairo_pattern_get_reference_count() to get the number of + * references to a #cairo_pattern_t. * * Return value: the referenced #cairo_pattern_t. + * + * Since: 1.0 **/ cairo_pattern_t * cairo_pattern_reference (cairo_pattern_t *pattern) @@ -773,8 +1073,8 @@ slim_hidden_def (cairo_pattern_reference); * cairo_pattern_get_type: * @pattern: a #cairo_pattern_t * - * This function returns the type a pattern. - * See #cairo_pattern_type_t for available types. + * Get the pattern's type. See #cairo_pattern_type_t for available + * types. * * Return value: The type of @pattern. * @@ -793,8 +1093,11 @@ cairo_pattern_get_type (cairo_pattern_t *pattern) * Checks whether an error has previously occurred for this * pattern. * - * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, or - * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, + * %CAIRO_STATUS_INVALID_MATRIX, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH, + * or %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.0 **/ cairo_status_t cairo_pattern_status (cairo_pattern_t *pattern) @@ -809,6 +1112,8 @@ cairo_pattern_status (cairo_pattern_t *pattern) * Decreases the reference count on @pattern by one. If the result is * zero, then @pattern and all associated resources are freed. See * cairo_pattern_reference(). + * + * Since: 1.0 **/ void cairo_pattern_destroy (cairo_pattern_t *pattern) @@ -828,7 +1133,10 @@ cairo_pattern_destroy (cairo_pattern_t *pattern) _cairo_pattern_fini (pattern); /* maintain a small cache of freed patterns */ - _freed_pool_put (&freed_pattern_pool[type], pattern); + if (type < ARRAY_LENGTH (freed_pattern_pool)) + _freed_pool_put (&freed_pattern_pool[type], pattern); + else + free (pattern); } slim_hidden_def (cairo_pattern_destroy); @@ -906,6 +1214,451 @@ cairo_pattern_set_user_data (cairo_pattern_t *pattern, key, user_data, destroy); } +/** + * cairo_mesh_pattern_begin_patch: + * @pattern: a #cairo_pattern_t + * + * Begin a patch in a mesh pattern. + * + * After calling this function, the patch shape should be defined with + * cairo_mesh_pattern_move_to(), cairo_mesh_pattern_line_to() and + * cairo_mesh_pattern_curve_to(). + * + * After defining the patch, cairo_mesh_pattern_end_patch() must be + * called before using @pattern as a source or mask. + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a + * current patch, it will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern) +{ + cairo_mesh_pattern_t *mesh; + cairo_status_t status; + cairo_mesh_patch_t *current_patch; + int i; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + if (unlikely (mesh->current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + status = _cairo_array_allocate (&mesh->patches, 1, (void **) ¤t_patch); + if (unlikely (status)) { + _cairo_pattern_set_error (pattern, status); + return; + } + + mesh->current_patch = current_patch; + mesh->current_side = -2; /* no current point */ + + for (i = 0; i < 4; i++) + mesh->has_control_point[i] = FALSE; + + for (i = 0; i < 4; i++) + mesh->has_color[i] = FALSE; +} + + +static void +_calc_control_point (cairo_mesh_patch_t *patch, int control_point) +{ + /* The Coons patch is a special case of the Tensor Product patch + * where the four control points are: + * + * P11 = S(1/3, 1/3) + * P12 = S(1/3, 2/3) + * P21 = S(2/3, 1/3) + * P22 = S(2/3, 2/3) + * + * where S is the gradient surface. + * + * When one or more control points has not been specified + * calculated the Coons patch control points are substituted. If + * no control points are specified the gradient will be a Coons + * patch. + * + * The equations below are defined in the ISO32000 standard. + */ + cairo_point_double_t *p[3][3]; + int cp_i, cp_j, i, j; + + cp_i = mesh_control_point_i[control_point]; + cp_j = mesh_control_point_j[control_point]; + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j]; + + p[0][0]->x = (- 4 * p[1][1]->x + + 6 * (p[1][0]->x + p[0][1]->x) + - 2 * (p[1][2]->x + p[2][1]->x) + + 3 * (p[2][0]->x + p[0][2]->x) + - 1 * p[2][2]->x) * (1. / 9); + + p[0][0]->y = (- 4 * p[1][1]->y + + 6 * (p[1][0]->y + p[0][1]->y) + - 2 * (p[1][2]->y + p[2][1]->y) + + 3 * (p[2][0]->y + p[0][2]->y) + - 1 * p[2][2]->y) * (1. / 9); +} + +/** + * cairo_mesh_pattern_end_patch: + * @pattern: a #cairo_pattern_t + * + * Indicates the end of the current patch in a mesh pattern. + * + * If the current patch has less than 4 sides, it is closed with a + * straight line from the current point to the first point of the + * patch as if cairo_mesh_pattern_line_to() was used. + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current + * patch or the current patch has no current point, @pattern will be + * put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern) +{ + cairo_mesh_pattern_t *mesh; + cairo_mesh_patch_t *current_patch; + int i; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + current_patch = mesh->current_patch; + if (unlikely (!current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + if (unlikely (mesh->current_side == -2)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + while (mesh->current_side < 3) { + int corner_num; + + cairo_mesh_pattern_line_to (pattern, + current_patch->points[0][0].x, + current_patch->points[0][0].y); + + corner_num = mesh->current_side + 1; + if (corner_num < 4 && ! mesh->has_color[corner_num]) { + current_patch->colors[corner_num] = current_patch->colors[0]; + mesh->has_color[corner_num] = TRUE; + } + } + + for (i = 0; i < 4; i++) { + if (! mesh->has_control_point[i]) + _calc_control_point (current_patch, i); + } + + for (i = 0; i < 4; i++) { + if (! mesh->has_color[i]) + current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT; + } + + mesh->current_patch = NULL; +} + +/** + * cairo_mesh_pattern_curve_to: + * @pattern: a #cairo_pattern_t + * @x1: the X coordinate of the first control point + * @y1: the Y coordinate of the first control point + * @x2: the X coordinate of the second control point + * @y2: the Y coordinate of the second control point + * @x3: the X coordinate of the end of the curve + * @y3: the Y coordinate of the end of the curve + * + * Adds a cubic Bézier spline to the current patch from the current + * point to position (@x3, @y3) in pattern-space coordinates, using + * (@x1, @y1) and (@x2, @y2) as the control points. + * + * If the current patch has no current point before the call to + * cairo_mesh_pattern_curve_to(), this function will behave as if + * preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1, + * @y1). + * + * After this call the current point will be (@x3, @y3). + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current + * patch or the current patch already has 4 sides, @pattern will be + * put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + cairo_mesh_pattern_t *mesh; + int current_point, i, j; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + if (unlikely (!mesh->current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + if (unlikely (mesh->current_side == 3)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + if (mesh->current_side == -2) + cairo_mesh_pattern_move_to (pattern, x1, y1); + + assert (mesh->current_side >= -1); + assert (pattern->status == CAIRO_STATUS_SUCCESS); + + mesh->current_side++; + + current_point = 3 * mesh->current_side; + + current_point++; + i = mesh_path_point_i[current_point]; + j = mesh_path_point_j[current_point]; + mesh->current_patch->points[i][j].x = x1; + mesh->current_patch->points[i][j].y = y1; + + current_point++; + i = mesh_path_point_i[current_point]; + j = mesh_path_point_j[current_point]; + mesh->current_patch->points[i][j].x = x2; + mesh->current_patch->points[i][j].y = y2; + + current_point++; + if (current_point < 12) { + i = mesh_path_point_i[current_point]; + j = mesh_path_point_j[current_point]; + mesh->current_patch->points[i][j].x = x3; + mesh->current_patch->points[i][j].y = y3; + } +} +slim_hidden_def (cairo_mesh_pattern_curve_to); + +/** + * cairo_mesh_pattern_line_to: + * @pattern: a #cairo_pattern_t + * @x: the X coordinate of the end of the new line + * @y: the Y coordinate of the end of the new line + * + * Adds a line to the current patch from the current point to position + * (@x, @y) in pattern-space coordinates. + * + * If there is no current point before the call to + * cairo_mesh_pattern_line_to() this function will behave as + * cairo_mesh_pattern_move_to(@pattern, @x, @y). + * + * After this call the current point will be (@x, @y). + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current + * patch or the current patch already has 4 sides, @pattern will be + * put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_line_to (cairo_pattern_t *pattern, + double x, double y) +{ + cairo_mesh_pattern_t *mesh; + cairo_point_double_t last_point; + int last_point_idx, i, j; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + if (unlikely (!mesh->current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + if (unlikely (mesh->current_side == 3)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + if (mesh->current_side == -2) { + cairo_mesh_pattern_move_to (pattern, x, y); + return; + } + + last_point_idx = 3 * (mesh->current_side + 1); + i = mesh_path_point_i[last_point_idx]; + j = mesh_path_point_j[last_point_idx]; + + last_point = mesh->current_patch->points[i][j]; + + cairo_mesh_pattern_curve_to (pattern, + (2 * last_point.x + x) * (1. / 3), + (2 * last_point.y + y) * (1. / 3), + (last_point.x + 2 * x) * (1. / 3), + (last_point.y + 2 * y) * (1. / 3), + x, y); +} +slim_hidden_def (cairo_mesh_pattern_line_to); + +/** + * cairo_mesh_pattern_move_to: + * @pattern: a #cairo_pattern_t + * @x: the X coordinate of the new position + * @y: the Y coordinate of the new position + * + * Define the first point of the current patch in a mesh pattern. + * + * After this call the current point will be (@x, @y). + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current + * patch or the current patch already has at least one side, @pattern + * will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_move_to (cairo_pattern_t *pattern, + double x, double y) +{ + cairo_mesh_pattern_t *mesh; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + if (unlikely (!mesh->current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + if (unlikely (mesh->current_side >= 0)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + mesh->current_side = -1; + mesh->current_patch->points[0][0].x = x; + mesh->current_patch->points[0][0].y = y; +} +slim_hidden_def (cairo_mesh_pattern_move_to); + +/** + * cairo_mesh_pattern_set_control_point: + * @pattern: a #cairo_pattern_t + * @point_num: the control point to set the position for + * @x: the X coordinate of the control point + * @y: the Y coordinate of the control point + * + * Set an internal control point of the current patch. + * + * Valid values for @point_num are from 0 to 3 and identify the + * control points as explained in cairo_pattern_create_mesh(). + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid, + * @pattern will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, + * @pattern will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern, + unsigned int point_num, + double x, + double y) +{ + cairo_mesh_pattern_t *mesh; + int i, j; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + if (unlikely (point_num > 3)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + if (unlikely (!mesh->current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + i = mesh_control_point_i[point_num]; + j = mesh_control_point_j[point_num]; + + mesh->current_patch->points[i][j].x = x; + mesh->current_patch->points[i][j].y = y; + mesh->has_control_point[point_num] = TRUE; +} + /* make room for at least one more color stop */ static cairo_status_t _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) @@ -948,7 +1701,127 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) } static void -_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, +_cairo_mesh_pattern_set_corner_color (cairo_mesh_pattern_t *mesh, + unsigned int corner_num, + double red, double green, double blue, + double alpha) +{ + cairo_color_t *color; + + assert (mesh->current_patch); + assert (corner_num <= 3); + + color = &mesh->current_patch->colors[corner_num]; + color->red = red; + color->green = green; + color->blue = blue; + color->alpha = alpha; + + color->red_short = _cairo_color_double_to_short (red); + color->green_short = _cairo_color_double_to_short (green); + color->blue_short = _cairo_color_double_to_short (blue); + color->alpha_short = _cairo_color_double_to_short (alpha); + + mesh->has_color[corner_num] = TRUE; +} + +/** + * cairo_mesh_pattern_set_corner_color_rgb: + * @pattern: a #cairo_pattern_t + * @corner_num: the corner to set the color for + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Sets the color of a corner of the current patch in a mesh pattern. + * + * The color is specified in the same way as in cairo_set_source_rgb(). + * + * Valid values for @corner_num are from 0 to 3 and identify the + * corners as explained in cairo_pattern_create_mesh(). + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid, + * @pattern will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, + * @pattern will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern, + unsigned int corner_num, + double red, double green, double blue) +{ + cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0); +} + +/** + * cairo_mesh_pattern_set_corner_color_rgba: + * @pattern: a #cairo_pattern_t + * @corner_num: the corner to set the color for + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Sets the color of a corner of the current patch in a mesh pattern. + * + * The color is specified in the same way as in cairo_set_source_rgba(). + * + * Valid values for @corner_num are from 0 to 3 and identify the + * corners as explained in cairo_pattern_create_mesh(). + * + * Note: If @pattern is not a mesh pattern then @pattern will be put + * into an error status with a status of + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid, + * @pattern will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, + * @pattern will be put into an error status with a status of + * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. + * + * Since: 1.12 + **/ +void +cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern, + unsigned int corner_num, + double red, double green, double blue, + double alpha) +{ + cairo_mesh_pattern_t *mesh; + + if (unlikely (pattern->status)) + return; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + return; + } + + if (unlikely (corner_num > 3)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX); + return; + } + + mesh = (cairo_mesh_pattern_t *) pattern; + if (unlikely (!mesh->current_patch)) { + _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); + return; + } + + red = _cairo_restrict_value (red, 0.0, 1.0); + green = _cairo_restrict_value (green, 0.0, 1.0); + blue = _cairo_restrict_value (blue, 0.0, 1.0); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_mesh_pattern_set_corner_color (mesh, corner_num, red, green, blue, alpha); +} +slim_hidden_def (cairo_mesh_pattern_set_corner_color_rgba); + +static void +_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, double offset, double red, double green, @@ -1020,6 +1893,8 @@ _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, * Note: If the pattern is not a gradient pattern, (eg. a linear or * radial pattern), then the pattern will be put into an error status * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. + * + * Since: 1.0 **/ void cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, @@ -1028,23 +1903,7 @@ cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double green, double blue) { - if (pattern->status) - return; - - if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && - pattern->type != CAIRO_PATTERN_TYPE_RADIAL) - { - _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - return; - } - - offset = _cairo_restrict_value (offset, 0.0, 1.0); - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - - _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, - offset, red, green, blue, 1.0); + cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0); } /** @@ -1073,7 +1932,9 @@ cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, * Note: If the pattern is not a gradient pattern, (eg. a linear or * radial pattern), then the pattern will be put into an error status * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. - */ + * + * Since: 1.0 + **/ void cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double offset, @@ -1101,6 +1962,7 @@ cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, offset, red, green, blue, alpha); } +slim_hidden_def (cairo_pattern_add_color_stop_rgba); /** * cairo_pattern_set_matrix: @@ -1133,6 +1995,8 @@ cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, * * Also, please note the discussion of the user-space locking * semantics of cairo_set_source(). + * + * Since: 1.0 **/ void cairo_pattern_set_matrix (cairo_pattern_t *pattern, @@ -1148,6 +2012,7 @@ cairo_pattern_set_matrix (cairo_pattern_t *pattern, return; pattern->matrix = *matrix; + _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX); inverse = *matrix; status = cairo_matrix_invert (&inverse); @@ -1162,6 +2027,8 @@ slim_hidden_def (cairo_pattern_set_matrix); * @matrix: return value for the matrix * * Stores the pattern's transformation matrix into @matrix. + * + * Since: 1.0 **/ void cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) @@ -1188,6 +2055,8 @@ cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) * cairo_set_source_surface (cr, image, x, y); * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); * + * + * Since: 1.0 **/ void cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) @@ -1196,6 +2065,7 @@ cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) return; pattern->filter = filter; + _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER); } /** @@ -1206,6 +2076,8 @@ cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) * for details on each filter. * * Return value: the current filter used for resizing the pattern. + * + * Since: 1.0 **/ cairo_filter_t cairo_pattern_get_filter (cairo_pattern_t *pattern) @@ -1225,6 +2097,8 @@ cairo_pattern_get_filter (cairo_pattern_t *pattern) * * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns * and %CAIRO_EXTEND_PAD for gradient patterns. + * + * Since: 1.0 **/ void cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) @@ -1233,6 +2107,7 @@ cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) return; pattern->extend = extend; + _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND); } /** @@ -1244,6 +2119,8 @@ cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) * * Return value: the current extend strategy used for drawing the * pattern. + * + * Since: 1.0 **/ cairo_extend_t cairo_pattern_get_extend (cairo_pattern_t *pattern) @@ -1252,6 +2129,16 @@ cairo_pattern_get_extend (cairo_pattern_t *pattern) } slim_hidden_def (cairo_pattern_get_extend); +void +_cairo_pattern_pretransform (cairo_pattern_t *pattern, + const cairo_matrix_t *ctm) +{ + if (pattern->status) + return; + + cairo_matrix_multiply (&pattern->matrix, &pattern->matrix, ctm); +} + void _cairo_pattern_transform (cairo_pattern_t *pattern, const cairo_matrix_t *ctm_inverse) @@ -1262,517 +2149,619 @@ _cairo_pattern_transform (cairo_pattern_t *pattern, cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); } -static void -_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, - double offset_x, - double offset_y, - int width, - int height, - cairo_bool_t *is_horizontal, - cairo_bool_t *is_vertical) -{ - cairo_point_double_t point0, point1; - double a, b, c, d, tx, ty; - double scale, start, dx, dy; - cairo_fixed_t factors[3]; - int i; - - /* To classify a pattern as horizontal or vertical, we first - * compute the (fixed point) factors at the corners of the - * pattern. We actually only need 3/4 corners, so we skip the - * fourth. - */ - point0.x = _cairo_fixed_to_double (pattern->p1.x); - point0.y = _cairo_fixed_to_double (pattern->p1.y); - point1.x = _cairo_fixed_to_double (pattern->p2.x); - point1.y = _cairo_fixed_to_double (pattern->p2.y); - - _cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); - - dx = point1.x - point0.x; - dy = point1.y - point0.y; - scale = dx * dx + dy * dy; - scale = (scale) ? 1.0 / scale : 1.0; - - start = dx * point0.x + dy * point0.y; - - for (i = 0; i < 3; i++) { - double qx_device = (i % 2) * (width - 1) + offset_x; - double qy_device = (i / 2) * (height - 1) + offset_y; - - /* transform fragment into pattern space */ - double qx = a * qx_device + c * qy_device + tx; - double qy = b * qx_device + d * qy_device + ty; - - factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale); - } - - /* We consider a pattern to be vertical if the fixed point factor - * at the two upper corners is the same. We could accept a small - * change, but determining what change is acceptable would require - * sorting the stops in the pattern and looking at the differences. - * - * Horizontal works the same way with the two left corners. - */ - - *is_vertical = factors[1] == factors[0]; - *is_horizontal = factors[2] == factors[0]; -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attr) -{ - cairo_image_surface_t *image; - pixman_image_t *pixman_image; - pixman_transform_t pixman_transform; - cairo_status_t status; - cairo_bool_t repeat = FALSE; - cairo_bool_t opaque = TRUE; - - pixman_gradient_stop_t pixman_stops_static[2]; - pixman_gradient_stop_t *pixman_stops = pixman_stops_static; - unsigned int i; - int clone_offset_x, clone_offset_y; - cairo_matrix_t matrix = pattern->base.matrix; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { - pixman_stops = _cairo_malloc_ab (pattern->n_stops, - sizeof(pixman_gradient_stop_t)); - if (unlikely (pixman_stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < pattern->n_stops; i++) { - pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); - pixman_stops[i].color.red = pattern->stops[i].color.red_short; - pixman_stops[i].color.green = pattern->stops[i].color.green_short; - pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; - pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; - if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha)) - opaque = FALSE; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) - { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - pixman_point_fixed_t p1, p2; - double x0, y0, x1, y1, maxabs; - - /* - * Transform the matrix to avoid overflow when converting between - * cairo_fixed_t and pixman_fixed_t (without incurring performance - * loss when the transformation is unnecessary). - * - * Having a function to compute the required transformation to - * "normalize" a given bounding box would be generally useful - - * cf linear patterns, gradient patterns, surface patterns... - */ - x0 = _cairo_fixed_to_double (linear->p1.x); - y0 = _cairo_fixed_to_double (linear->p1.y); - x1 = _cairo_fixed_to_double (linear->p2.x); - y1 = _cairo_fixed_to_double (linear->p2.y); - cairo_matrix_transform_point (&matrix, &x0, &y0); - cairo_matrix_transform_point (&matrix, &x1, &y1); - maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1))); - -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ - if (maxabs > PIXMAN_MAX_INT) - { - double sf; - cairo_matrix_t scale; - - sf = PIXMAN_MAX_INT / maxabs; - - p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); - p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); - p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); - p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); - - /* cairo_matrix_scale does a pre-scale, we want a post-scale */ - cairo_matrix_init_scale (&scale, sf, sf); - cairo_matrix_multiply (&matrix, &matrix, &scale); - } - else - { - p1.x = _cairo_fixed_to_16_16 (linear->p1.x); - p1.y = _cairo_fixed_to_16_16 (linear->p1.y); - p2.x = _cairo_fixed_to_16_16 (linear->p2.x); - p2.y = _cairo_fixed_to_16_16 (linear->p2.y); - } - - pixman_image = pixman_image_create_linear_gradient (&p1, &p2, - pixman_stops, - pattern->n_stops); - } - else - { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - pixman_point_fixed_t c1, c2; - pixman_fixed_t r1, r2; - - c1.x = _cairo_fixed_to_16_16 (radial->c1.x); - c1.y = _cairo_fixed_to_16_16 (radial->c1.y); - r1 = _cairo_fixed_to_16_16 (radial->r1); - - c2.x = _cairo_fixed_to_16_16 (radial->c2.x); - c2.y = _cairo_fixed_to_16_16 (radial->c2.y); - r2 = _cairo_fixed_to_16_16 (radial->r2); - - pixman_image = pixman_image_create_radial_gradient (&c1, &c2, - r1, r2, - pixman_stops, - pattern->n_stops); - } - - if (pixman_stops != pixman_stops_static) - free (pixman_stops); - - if (unlikely (pixman_image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (_cairo_surface_is_image (dst)) - { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_for_pixman_image (pixman_image, - PIXMAN_a8r8g8b8); - if (image->base.status) - { - pixman_image_unref (pixman_image); - return image->base.status; - } - - attr->x_offset = attr->y_offset = 0; - attr->matrix = matrix; - attr->extend = pattern->base.extend; - attr->filter = CAIRO_FILTER_NEAREST; - attr->has_component_alpha = pattern->base.has_component_alpha; - - *out = &image->base; - - return CAIRO_STATUS_SUCCESS; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_bool_t is_horizontal; - cairo_bool_t is_vertical; - - _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern, - x, y, width, height, - &is_horizontal, &is_vertical); - if (is_horizontal) { - height = 1; - repeat = TRUE; - } - /* width-1 repeating patterns are quite slow with scan-line based - * compositing code, so we use a wider strip and spend some extra - * expense in computing the gradient. It's possible that for narrow - * gradients we'd be better off using a 2 or 4 pixel strip; the - * wider the gradient, the more it's worth spending extra time - * computing a sample. - */ - if (is_vertical && width > 8) { - width = 8; - repeat = TRUE; - } - } - - if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR, - NULL, 0)) - { - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - image = (cairo_image_surface_t *) - cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - if (image->base.status) { - pixman_image_unref (pixman_image); - return image->base.status; - } - - _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform, - width/2., height/2.); - if (!pixman_image_set_transform (pixman_image, &pixman_transform)) { - cairo_surface_destroy (&image->base); - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - switch (pattern->base.extend) { - case CAIRO_EXTEND_NONE: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NONE); - break; - case CAIRO_EXTEND_REPEAT: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NORMAL); - break; - case CAIRO_EXTEND_REFLECT: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_REFLECT); - break; - case CAIRO_EXTEND_PAD: - pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_PAD); - break; - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - pixman_image, - NULL, - image->pixman_image, - x, y, - 0, 0, - 0, 0, - width, height); - - pixman_image_unref (pixman_image); - - _cairo_debug_check_image_surface_is_defined (&image->base); - - status = _cairo_surface_clone_similar (dst, &image->base, - 0, 0, width, height, - &clone_offset_x, - &clone_offset_y, - out); - - cairo_surface_destroy (&image->base); - - attr->x_offset = -x; - attr->y_offset = -y; - cairo_matrix_init_identity (&attr->matrix); - attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; - attr->filter = CAIRO_FILTER_NEAREST; - attr->has_component_alpha = pattern->base.has_component_alpha; - - return status; -} - -/* We maintain a small cache here, because we don't want to constantly - * recreate surfaces for simple solid colors. */ -#define MAX_SURFACE_CACHE_SIZE 16 -static struct { - struct _cairo_pattern_solid_surface_cache{ - cairo_color_t color; - cairo_surface_t *surface; - } cache[MAX_SURFACE_CACHE_SIZE]; - int size; -} solid_surface_cache; - -static cairo_bool_t -_cairo_pattern_solid_surface_matches ( - const struct _cairo_pattern_solid_surface_cache *cache, - const cairo_solid_pattern_t *pattern, - cairo_surface_t *dst) -{ - if (cairo_surface_get_content (cache->surface) != _cairo_color_get_content (&pattern->color)) - return FALSE; - - if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1) - return FALSE; - - if (! _cairo_surface_is_similar (cache->surface, dst)) - return FALSE; - - return TRUE; -} - -static cairo_bool_t -_cairo_pattern_solid_surface_matches_color ( - const struct _cairo_pattern_solid_surface_cache *cache, - const cairo_solid_pattern_t *pattern, - cairo_surface_t *dst) -{ - if (! _cairo_color_equal (&cache->color, &pattern->color)) - return FALSE; - - return _cairo_pattern_solid_surface_matches (cache, pattern, dst); -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - cairo_surface_t **out, - cairo_surface_attributes_t *attribs) -{ - static int i; - - cairo_surface_t *surface, *to_destroy = NULL; - cairo_status_t status; - - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - - /* Check cache first */ - if (i < solid_surface_cache.size && - _cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], - pattern, - dst)) - { - goto DONE; - } - - for (i = 0 ; i < solid_surface_cache.size; i++) { - if (_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], - pattern, - dst)) - { - goto DONE; - } - } - - /* Choose a surface to repaint/evict */ - surface = NULL; - if (solid_surface_cache.size == MAX_SURFACE_CACHE_SIZE) { - i = rand () % MAX_SURFACE_CACHE_SIZE; - surface = solid_surface_cache.cache[i].surface; - - if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i], - pattern, - dst)) - { - /* Reuse the surface instead of evicting */ - status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); - if (unlikely (status)) - goto EVICT; - - cairo_surface_reference (surface); - } - else - { - EVICT: - surface = NULL; - } - } - - if (surface == NULL) { - /* Not cached, need to create new */ - surface = _cairo_surface_create_solid_pattern_surface (dst, pattern); - if (surface == NULL) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto UNLOCK; - } - if (unlikely (surface->status)) { - status = surface->status; - goto UNLOCK; - } - - if (unlikely (! _cairo_surface_is_similar (surface, dst))) - { - /* In the rare event of a substitute surface being returned, - * don't cache the fallback. - */ - *out = surface; - goto NOCACHE; - } - } - - if (i == solid_surface_cache.size) - solid_surface_cache.size++; - - to_destroy = solid_surface_cache.cache[i].surface; - solid_surface_cache.cache[i].surface = surface; - solid_surface_cache.cache[i].color = pattern->color; - -DONE: - *out = cairo_surface_reference (solid_surface_cache.cache[i].surface); - -NOCACHE: - attribs->x_offset = attribs->y_offset = 0; - cairo_matrix_init_identity (&attribs->matrix); - attribs->extend = CAIRO_EXTEND_REPEAT; - attribs->filter = CAIRO_FILTER_NEAREST; - attribs->has_component_alpha = pattern->base.has_component_alpha; - - status = CAIRO_STATUS_SUCCESS; - -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); - - if (to_destroy) - cairo_surface_destroy (to_destroy); - - return status; -} - -static void -_cairo_pattern_reset_solid_surface_cache (void) -{ - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - - /* remove surfaces starting from the end so that solid_surface_cache.cache - * is always in a consistent state when we release the mutex. */ - while (solid_surface_cache.size) { - cairo_surface_t *surface; - - solid_surface_cache.size--; - surface = solid_surface_cache.cache[solid_surface_cache.size].surface; - solid_surface_cache.cache[solid_surface_cache.size].surface = NULL; - - /* release the lock to avoid the possibility of a recursive - * deadlock when the surface destroy closure gets called */ - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); - cairo_surface_destroy (surface); - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - } - - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); -} - -static void -_extents_to_linear_parameter (const cairo_linear_pattern_t *linear, - const cairo_rectangle_int_t *extents, - double t[2]) -{ - double t0, tdx, tdy; - double p1x, p1y, pdx, pdy, invsqnorm; - - p1x = _cairo_fixed_to_double (linear->p1.x); - p1y = _cairo_fixed_to_double (linear->p1.y); - pdx = _cairo_fixed_to_double (linear->p2.x) - p1x; - pdy = _cairo_fixed_to_double (linear->p2.y) - p1y; - invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); - pdx *= invsqnorm; - pdy *= invsqnorm; - - t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy; - tdx = extents->width * pdx; - tdy = extents->height * pdy; - - t[0] = t[1] = t0; - if (tdx < 0) - t[0] += tdx; - else - t[1] += tdx; - - if (tdy < 0) - t[0] += tdy; - else - t[1] += tdy; -} - static cairo_bool_t _linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) { - return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y; + return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON && + fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON; } static cairo_bool_t _radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial) { - return radial->r1 == radial->r2 && - (radial->r1 == 0 /* && radial->r2 == 0 */ || - (radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y)); + /* A radial pattern is considered degenerate if it can be + * represented as a solid or clear pattern. This corresponds to + * one of the two cases: + * + * 1) The radii are both very small: + * |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON + * + * 2) The two circles have about the same radius and are very + * close to each other (approximately a cylinder gradient that + * doesn't move with the parameter): + * |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON + * + * These checks are consistent with the assumptions used in + * _cairo_radial_pattern_box_to_parameter (). + */ + + return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON && + (MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON || + MAX (fabs (radial->cd1.center.x - radial->cd2.center.x), + fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON); +} + +static void +_cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear, + double x0, double y0, + double x1, double y1, + double range[2]) +{ + double t0, tdx, tdy; + double p1x, p1y, pdx, pdy, invsqnorm; + + assert (! _linear_pattern_is_degenerate (linear)); + + /* + * Linear gradients are othrogonal to the line passing through + * their extremes. Because of convexity, the parameter range can + * be computed as the convex hull (one the real line) of the + * parameter values of the 4 corners of the box. + * + * The parameter value t for a point (x,y) can be computed as: + * + * t = (p2 - p1) . (x,y) / |p2 - p1|^2 + * + * t0 is the t value for the top left corner + * tdx is the difference between left and right corners + * tdy is the difference between top and bottom corners + */ + + p1x = linear->pd1.x; + p1y = linear->pd1.y; + pdx = linear->pd2.x - p1x; + pdy = linear->pd2.y - p1y; + invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); + pdx *= invsqnorm; + pdy *= invsqnorm; + + t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy; + tdx = (x1 - x0) * pdx; + tdy = (y1 - y0) * pdy; + + /* + * Because of the linearity of the t value, tdx can simply be + * added the t0 to move along the top edge. After this, range[0] + * and range[1] represent the parameter range for the top edge, so + * extending it to include the whole box simply requires adding + * tdy to the correct extreme. + */ + + range[0] = range[1] = t0; + if (tdx < 0) + range[0] += tdx; + else + range[1] += tdx; + + if (tdy < 0) + range[0] += tdy; + else + range[1] += tdy; +} + +static cairo_bool_t +_extend_range (double range[2], double value, cairo_bool_t valid) +{ + if (!valid) + range[0] = range[1] = value; + else if (value < range[0]) + range[0] = value; + else if (value > range[1]) + range[1] = value; + + return TRUE; +} + +/* + * _cairo_radial_pattern_focus_is_inside: + * + * Returns %TRUE if and only if the focus point exists and is + * contained in one of the two extreme circles. This condition is + * equivalent to one of the two extreme circles being completely + * contained in the other one. + * + * Note: if the focus is on the border of one of the two circles (in + * which case the circles are tangent in the focus point), it is not + * considered as contained in the circle, hence this function returns + * %FALSE. + * + */ +cairo_bool_t +_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial) +{ + double cx, cy, cr, dx, dy, dr; + + cx = radial->cd1.center.x; + cy = radial->cd1.center.y; + cr = radial->cd1.radius; + dx = radial->cd2.center.x - cx; + dy = radial->cd2.center.y - cy; + dr = radial->cd2.radius - cr; + + return dx*dx + dy*dy < dr*dr; +} + +static void +_cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial, + double x0, double y0, + double x1, double y1, + double tolerance, + double range[2]) +{ + double cx, cy, cr, dx, dy, dr; + double a, x_focus, y_focus; + double mindr, minx, miny, maxx, maxy; + cairo_bool_t valid; + + assert (! _radial_pattern_is_degenerate (radial)); + assert (x0 < x1); + assert (y0 < y1); + + tolerance = MAX (tolerance, DBL_EPSILON); + + range[0] = range[1] = 0; + valid = FALSE; + + x_focus = y_focus = 0; /* silence gcc */ + + cx = radial->cd1.center.x; + cy = radial->cd1.center.y; + cr = radial->cd1.radius; + dx = radial->cd2.center.x - cx; + dy = radial->cd2.center.y - cy; + dr = radial->cd2.radius - cr; + + /* translate by -(cx, cy) to simplify computations */ + x0 -= cx; + y0 -= cy; + x1 -= cx; + y1 -= cy; + + /* enlarge boundaries slightly to avoid rounding problems in the + * parameter range computation */ + x0 -= DBL_EPSILON; + y0 -= DBL_EPSILON; + x1 += DBL_EPSILON; + y1 += DBL_EPSILON; + + /* enlarge boundaries even more to avoid rounding problems when + * testing if a point belongs to the box */ + minx = x0 - DBL_EPSILON; + miny = y0 - DBL_EPSILON; + maxx = x1 + DBL_EPSILON; + maxy = y1 + DBL_EPSILON; + + /* we don't allow negative radiuses, so we will be checking that + * t*dr >= mindr to consider t valid */ + mindr = -(cr + DBL_EPSILON); + + /* + * After the previous transformations, the start circle is + * centered in the origin and has radius cr. A 1-unit change in + * the t parameter corresponds to dx,dy,dr changes in the x,y,r of + * the circle (center coordinates, radius). + * + * To compute the minimum range needed to correctly draw the + * pattern, we start with an empty range and extend it to include + * the circles touching the bounding box or within it. + */ + + /* + * Focus, the point where the circle has radius == 0. + * + * r = cr + t * dr = 0 + * t = -cr / dr + * + * If the radius is constant (dr == 0) there is no focus (the + * gradient represents a cylinder instead of a cone). + */ + if (fabs (dr) >= DBL_EPSILON) { + double t_focus; + + t_focus = -cr / dr; + x_focus = t_focus * dx; + y_focus = t_focus * dy; + if (minx <= x_focus && x_focus <= maxx && + miny <= y_focus && y_focus <= maxy) + { + valid = _extend_range (range, t_focus, valid); + } + } + + /* + * Circles externally tangent to box edges. + * + * All circles have center in (dx, dy) * t + * + * If the circle is tangent to the line defined by the edge of the + * box, then at least one of the following holds true: + * + * (dx*t) + (cr + dr*t) == x0 (left edge) + * (dx*t) - (cr + dr*t) == x1 (right edge) + * (dy*t) + (cr + dr*t) == y0 (top edge) + * (dy*t) - (cr + dr*t) == y1 (bottom edge) + * + * The solution is only valid if the tangent point is actually on + * the edge, i.e. if its y coordinate is in [y0,y1] for left/right + * edges and if its x coordinate is in [x0,x1] for top/bottom + * edges. + * + * For the first equation: + * + * (dx + dr) * t = x0 - cr + * t = (x0 - cr) / (dx + dr) + * y = dy * t + * + * in the code this becomes: + * + * t_edge = (num) / (den) + * v = (delta) * t_edge + * + * If the denominator in t is 0, the pattern is tangent to a line + * parallel to the edge under examination. The corner-case where + * the boundary line is the same as the edge is handled by the + * focus point case and/or by the a==0 case. + */ +#define T_EDGE(num,den,delta,lower,upper) \ + if (fabs (den) >= DBL_EPSILON) { \ + double t_edge, v; \ + \ + t_edge = (num) / (den); \ + v = t_edge * (delta); \ + if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \ + valid = _extend_range (range, t_edge, valid); \ + } + + /* circles tangent (externally) to left/right/top/bottom edge */ + T_EDGE (x0 - cr, dx + dr, dy, miny, maxy); + T_EDGE (x1 + cr, dx - dr, dy, miny, maxy); + T_EDGE (y0 - cr, dy + dr, dx, minx, maxx); + T_EDGE (y1 + cr, dy - dr, dx, minx, maxx); + +#undef T_EDGE + + /* + * Circles passing through a corner. + * + * A circle passing through the point (x,y) satisfies: + * + * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2 + * + * If we set: + * a = dx^2 + dy^2 - dr^2 + * b = x*dx + y*dy + cr*dr + * c = x^2 + y^2 - cr^2 + * we have: + * a*t^2 - 2*b*t + c == 0 + */ + a = dx * dx + dy * dy - dr * dr; + if (fabs (a) < DBL_EPSILON * DBL_EPSILON) { + double b, maxd2; + + /* Ensure that gradients with both a and dr small are + * considered degenerate. + * The floating point version of the degeneracy test implemented + * in _radial_pattern_is_degenerate() is: + * + * 1) The circles are practically the same size: + * |dr| < DBL_EPSILON + * AND + * 2a) The circles are both very small: + * min (r0, r1) < DBL_EPSILON + * OR + * 2b) The circles are very close to each other: + * max (|dx|, |dy|) < 2 * DBL_EPSILON + * + * Assuming that the gradient is not degenerate, we want to + * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON. + * + * If the gradient is not degenerate yet it has |dr| < + * DBL_EPSILON, (2b) is false, thus: + * + * max (|dx|, |dy|) >= 2*DBL_EPSILON + * which implies: + * 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 + * + * From the definition of a, we get: + * a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2 + * dx^2 + dy^2 - DBL_EPSILON^2 < dr^2 + * 3*DBL_EPSILON^2 < dr^2 + * + * which is inconsistent with the hypotheses, thus |dr| < + * DBL_EPSILON is false or the gradient is degenerate. + */ + assert (fabs (dr) >= DBL_EPSILON); + + /* + * If a == 0, all the circles are tangent to a line in the + * focus point. If this line is within the box extents, we + * should add the circle with infinite radius, but this would + * make the range unbounded, so we add the smallest circle whose + * distance to the desired (degenerate) circle within the + * bounding box does not exceed tolerance. + * + * The equation of the line is b==0, i.e.: + * x*dx + y*dy + cr*dr == 0 + * + * We compute the intersection of the line with the box and + * keep the intersection with maximum square distance (maxd2) + * from the focus point. + * + * In the code the intersection is represented in another + * coordinate system, whose origin is the focus point and + * which has a u,v axes, which are respectively orthogonal and + * parallel to the edge being intersected. + * + * The intersection is valid only if it belongs to the box, + * otherwise it is ignored. + * + * For example: + * + * y = y0 + * x*dx + y0*dy + cr*dr == 0 + * x = -(y0*dy + cr*dr) / dx + * + * which in (u,v) is: + * u = y0 - y_focus + * v = -(y0*dy + cr*dr) / dx - x_focus + * + * In the code: + * u = (edge) - (u_origin) + * v = -((edge) * (delta) + cr*dr) / (den) - v_focus + */ +#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \ + if (fabs (den) >= DBL_EPSILON) { \ + double v; \ + \ + v = -((edge) * (delta) + cr * dr) / (den); \ + if ((lower) <= v && v <= (upper)) { \ + double u, d2; \ + \ + u = (edge) - (u_origin); \ + v -= (v_origin); \ + d2 = u*u + v*v; \ + if (maxd2 < d2) \ + maxd2 = d2; \ + } \ + } + + maxd2 = 0; + + /* degenerate circles (lines) passing through each edge */ + T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus); + T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus); + T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus); + T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus); + +#undef T_EDGE + + /* + * The limit circle can be transformed rigidly to the y=0 line + * and the circles tangent to it in (0,0) are: + * + * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0 + * + * y is the distance from the line, in our case tolerance; + * x is the distance along the line, i.e. sqrt(maxd2), + * so: + * + * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance) + * t = (r - cr) / dr = + * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr) + */ + if (maxd2 > 0) { + double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr; + t_limit /= 2 * tolerance * dr; + valid = _extend_range (range, t_limit, valid); + } + + /* + * Nondegenerate, nonlimit circles passing through the corners. + * + * a == 0 && a*t^2 - 2*b*t + c == 0 + * + * t = c / (2*b) + * + * The b == 0 case has just been handled, so we only have to + * compute this if b != 0. + */ +#define T_CORNER(x,y) \ + b = (x) * dx + (y) * dy + cr * dr; \ + if (fabs (b) >= DBL_EPSILON) { \ + double t_corner; \ + double x2 = (x) * (x); \ + double y2 = (y) * (y); \ + double cr2 = (cr) * (cr); \ + double c = x2 + y2 - cr2; \ + \ + t_corner = 0.5 * c / b; \ + if (t_corner * dr >= mindr) \ + valid = _extend_range (range, t_corner, valid); \ + } + + /* circles touching each corner */ + T_CORNER (x0, y0); + T_CORNER (x0, y1); + T_CORNER (x1, y0); + T_CORNER (x1, y1); + +#undef T_CORNER + } else { + double inva, b, c, d; + + inva = 1 / a; + + /* + * Nondegenerate, nonlimit circles passing through the corners. + * + * a != 0 && a*t^2 - 2*b*t + c == 0 + * + * t = (b +- sqrt (b*b - a*c)) / a + * + * If the argument of sqrt() is negative, then no circle + * passes through the corner. + */ +#define T_CORNER(x,y) \ + b = (x) * dx + (y) * dy + cr * dr; \ + c = (x) * (x) + (y) * (y) - cr * cr; \ + d = b * b - a * c; \ + if (d >= 0) { \ + double t_corner; \ + \ + d = sqrt (d); \ + t_corner = (b + d) * inva; \ + if (t_corner * dr >= mindr) \ + valid = _extend_range (range, t_corner, valid); \ + t_corner = (b - d) * inva; \ + if (t_corner * dr >= mindr) \ + valid = _extend_range (range, t_corner, valid); \ + } + + /* circles touching each corner */ + T_CORNER (x0, y0); + T_CORNER (x0, y1); + T_CORNER (x1, y0); + T_CORNER (x1, y1); + +#undef T_CORNER + } +} + +/** + * _cairo_gradient_pattern_box_to_parameter: + * + * Compute a interpolation range sufficient to draw (within the given + * tolerance) the gradient in the given box getting the same result as + * using the (-inf, +inf) range. + * + * Assumes that the pattern is not degenerate. This can be guaranteed + * by simplifying it to a solid clear if _cairo_pattern_is_clear or to + * a solid color if _cairo_gradient_pattern_is_solid. + * + * The range isn't guaranteed to be minimal, but it tries to. + **/ +void +_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient, + double x0, double y0, + double x1, double y1, + double tolerance, + double out_range[2]) +{ + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + _cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient, + x0, y0, x1, y1, out_range); + } else { + _cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient, + x0, y0, x1, y1, tolerance, out_range); + } +} + +/** + * _cairo_gradient_pattern_interpolate: + * + * Interpolate between the start and end objects of linear or radial + * gradients. The interpolated object is stored in out_circle, with + * the radius being zero in the linear gradient case. + **/ +void +_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient, + double t, + cairo_circle_double_t *out_circle) +{ + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + +#define lerp(a,b) (a)*(1-t) + (b)*t + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x); + out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y); + out_circle->radius = 0; + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; + out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x); + out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y); + out_circle->radius = lerp (radial->cd1.radius , radial->cd2.radius); + } + +#undef lerp +} + + +/** + * _cairo_gradient_pattern_fit_to_range: + * + * Scale the extremes of a gradient to guarantee that the coordinates + * and their deltas are within the range (-max_value, max_value). The + * new extremes are stored in out_circle. + * + * The pattern matrix is scaled to guarantee that the aspect of the + * gradient is the same and the result is stored in out_matrix. + * + **/ +void +_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient, + double max_value, + cairo_matrix_t *out_matrix, + cairo_circle_double_t out_circle[2]) +{ + double dim; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + + out_circle[0].center = linear->pd1; + out_circle[0].radius = 0; + out_circle[1].center = linear->pd2; + out_circle[1].radius = 0; + + dim = fabs (linear->pd1.x); + dim = MAX (dim, fabs (linear->pd1.y)); + dim = MAX (dim, fabs (linear->pd2.x)); + dim = MAX (dim, fabs (linear->pd2.y)); + dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x)); + dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y)); + } else { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; + + out_circle[0] = radial->cd1; + out_circle[1] = radial->cd2; + + dim = fabs (radial->cd1.center.x); + dim = MAX (dim, fabs (radial->cd1.center.y)); + dim = MAX (dim, fabs (radial->cd1.radius)); + dim = MAX (dim, fabs (radial->cd2.center.x)); + dim = MAX (dim, fabs (radial->cd2.center.y)); + dim = MAX (dim, fabs (radial->cd2.radius)); + dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x)); + dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y)); + dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius)); + } + + if (unlikely (dim > max_value)) { + cairo_matrix_t scale; + + dim = max_value / dim; + + out_circle[0].center.x *= dim; + out_circle[0].center.y *= dim; + out_circle[0].radius *= dim; + out_circle[1].center.x *= dim; + out_circle[1].center.y *= dim; + out_circle[1].radius *= dim; + + cairo_matrix_init_scale (&scale, dim, dim); + cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale); + } else { + *out_matrix = gradient->base.matrix; + } } static cairo_bool_t @@ -1789,27 +2778,40 @@ _gradient_is_clear (const cairo_gradient_pattern_t *gradient, gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) return TRUE; - /* Check if the extents intersect the drawn part of the pattern. */ - if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - if (gradient->base.extend == CAIRO_EXTEND_NONE) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; - /* EXTEND_NONE degenerate linear gradients are clear */ - if (_linear_pattern_is_degenerate (linear)) - return TRUE; - - if (extents != NULL) { - double t[2]; - _extents_to_linear_parameter (linear, extents, t); - if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0)) - return TRUE; - } - } - } else { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; + if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) { /* degenerate radial gradients are clear */ - if (_radial_pattern_is_degenerate (radial) && FALSE) + if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient)) + return TRUE; + } else if (gradient->base.extend == CAIRO_EXTEND_NONE) { + /* EXTEND_NONE degenerate linear gradients are clear */ + if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient)) + return TRUE; + } + + /* Check if the extents intersect the drawn part of the pattern. */ + if (extents != NULL && + (gradient->base.extend == CAIRO_EXTEND_NONE || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL)) + { + double t[2]; + + _cairo_gradient_pattern_box_to_parameter (gradient, + extents->x, + extents->y, + extents->x + extents->width, + extents->y + extents->height, + DBL_EPSILON, + t); + + if (gradient->base.extend == CAIRO_EXTEND_NONE && + (t[0] >= gradient->stops[gradient->n_stops - 1].offset || + t[1] <= gradient->stops[0].offset)) + { + return TRUE; + } + + if (t[0] == t[1]) return TRUE; - /* TODO: check actual intersection */ } for (i = 0; i < gradient->n_stops; i++) @@ -1819,8 +2821,254 @@ _gradient_is_clear (const cairo_gradient_pattern_t *gradient, return TRUE; } +static void +_gradient_color_average (const cairo_gradient_pattern_t *gradient, + cairo_color_t *color) +{ + double delta0, delta1; + double r, g, b, a; + unsigned int i, start = 1, end; + + assert (gradient->n_stops > 0); + assert (gradient->base.extend != CAIRO_EXTEND_NONE); + + if (gradient->n_stops == 1) { + _cairo_color_init_rgba (color, + gradient->stops[0].color.red, + gradient->stops[0].color.green, + gradient->stops[0].color.blue, + gradient->stops[0].color.alpha); + return; + } + + end = gradient->n_stops - 1; + + switch (gradient->base.extend) { + case CAIRO_EXTEND_REPEAT: + /* + * Sa, Sb and Sy, Sz are the first two and last two stops respectively. + * The weight of the first and last stop can be computed as the area of + * the following triangles (taken with height 1, since the whole [0-1] + * will have total weight 1 this way): b*h/2 + * + * + + + * / |\ / | \ + * / | \ / | \ + * / | \ / | \ + * ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~ + * -1+Sz 0 Sa Sb Sy Sz 1 1+Sa + * + * For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2 + * For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2 + * Halving the result is done after summing up all the areas. + */ + delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset; + delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset; + break; + + case CAIRO_EXTEND_REFLECT: + /* + * Sa, Sb and Sy, Sz are the first two and last two stops respectively. + * The weight of the first and last stop can be computed as the area of + * the following trapezoids (taken with height 1, since the whole [0-1] + * will have total weight 1 this way): (b+B)*h/2 + * + * +-------+ +---+ + * | |\ / | | + * | | \ / | | + * | | \ / | | + * +-------+---+~~~~~~~+-------+---+ + * 0 Sa Sb Sy Sz 1 + * + * For the first stop: (Sa+Sb)/2 + * For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2 + * Halving the result is done after summing up all the areas. + */ + delta0 = gradient->stops[0].offset + gradient->stops[1].offset; + delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset; + break; + + case CAIRO_EXTEND_PAD: + /* PAD is computed as the average of the first and last stop: + * - take both of them with weight 1 (they will be halved + * after the whole sum has been computed). + * - avoid summing any of the inner stops. + */ + delta0 = delta1 = 1.0; + start = end; + break; + + case CAIRO_EXTEND_NONE: + default: + ASSERT_NOT_REACHED; + _cairo_color_init_rgba (color, 0, 0, 0, 0); + return; + } + + r = delta0 * gradient->stops[0].color.red; + g = delta0 * gradient->stops[0].color.green; + b = delta0 * gradient->stops[0].color.blue; + a = delta0 * gradient->stops[0].color.alpha; + + for (i = start; i < end; ++i) { + /* Inner stops weight is the same as the area of the triangle they influence + * (which goes from the stop before to the stop after), again with height 1 + * since the whole must sum up to 1: b*h/2 + * Halving is done after the whole sum has been computed. + */ + double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset; + r += delta * gradient->stops[i].color.red; + g += delta * gradient->stops[i].color.green; + b += delta * gradient->stops[i].color.blue; + a += delta * gradient->stops[i].color.alpha; + } + + r += delta1 * gradient->stops[end].color.red; + g += delta1 * gradient->stops[end].color.green; + b += delta1 * gradient->stops[end].color.blue; + a += delta1 * gradient->stops[end].color.alpha; + + _cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5); +} + /** - * _cairo_gradient_pattern_is_solid + * _cairo_pattern_alpha_range: + * + * Convenience function to determine the minimum and maximum alpha in + * the drawn part of a pattern (i.e. ignoring clear parts caused by + * extend modes and/or pattern shape). + * + * If not NULL, out_min and out_max will be set respectively to the + * minimum and maximum alpha value of the pattern. + **/ +void +_cairo_pattern_alpha_range (const cairo_pattern_t *pattern, + double *out_min, + double *out_max) +{ + double alpha_min, alpha_max; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: { + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + alpha_min = alpha_max = solid->color.alpha; + break; + } + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { + const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + unsigned int i; + + assert (gradient->n_stops >= 1); + + alpha_min = alpha_max = gradient->stops[0].color.alpha; + for (i = 1; i < gradient->n_stops; i++) { + if (alpha_min > gradient->stops[i].color.alpha) + alpha_min = gradient->stops[i].color.alpha; + else if (alpha_max < gradient->stops[i].color.alpha) + alpha_max = gradient->stops[i].color.alpha; + } + + break; + } + + case CAIRO_PATTERN_TYPE_MESH: { + const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern; + const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0); + unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches); + + assert (n >= 1); + + alpha_min = alpha_max = patch[0].colors[0].alpha; + for (i = 0; i < n; i++) { + for (j = 0; j < 4; j++) { + if (patch[i].colors[j].alpha < alpha_min) + alpha_min = patch[i].colors[j].alpha; + else if (patch[i].colors[j].alpha > alpha_max) + alpha_max = patch[i].colors[j].alpha; + } + } + + break; + } + + default: + ASSERT_NOT_REACHED; + /* fall through */ + + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + alpha_min = 0; + alpha_max = 1; + break; + } + + if (out_min) + *out_min = alpha_min; + if (out_max) + *out_max = alpha_max; +} + +/** + * _cairo_mesh_pattern_coord_box: + * + * Convenience function to determine the range of the coordinates of + * the points used to define the patches of the mesh. + * + * This is guaranteed to contain the pattern extents, but might not be + * tight, just like a Bezier curve is always inside the convex hull of + * the control points. + * + * This function cannot be used while the mesh is being constructed. + * + * The function returns TRUE and sets the output parameters to define + * the coordinate range if the mesh pattern contains at least one + * patch, otherwise it returns FALSE. + **/ +cairo_bool_t +_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh, + double *out_xmin, + double *out_ymin, + double *out_xmax, + double *out_ymax) +{ + const cairo_mesh_patch_t *patch; + unsigned int num_patches, i, j, k; + double x0, y0, x1, y1; + + assert (mesh->current_patch == NULL); + + num_patches = _cairo_array_num_elements (&mesh->patches); + + if (num_patches == 0) + return FALSE; + + patch = _cairo_array_index_const (&mesh->patches, 0); + x0 = x1 = patch->points[0][0].x; + y0 = y1 = patch->points[0][0].y; + + for (i = 0; i < num_patches; i++) { + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + x0 = MIN (x0, patch[i].points[j][k].x); + y0 = MIN (y0, patch[i].points[j][k].y); + x1 = MAX (x1, patch[i].points[j][k].x); + y1 = MAX (y1, patch[i].points[j][k].y); + } + } + } + + *out_xmin = x0; + *out_ymin = y0; + *out_xmax = x1; + *out_ymax = y1; + + return TRUE; +} + +/** + * _cairo_gradient_pattern_is_solid: * * Convenience function to determine whether a gradient pattern is * a solid color within the given extents. In this case the color @@ -1841,10 +3089,15 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); - /* TODO: radial, degenerate linear */ + /* TODO: radial */ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + if (_linear_pattern_is_degenerate (linear)) { + _gradient_color_average (gradient, color); + return TRUE; + } + if (gradient->base.extend == CAIRO_EXTEND_NONE) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; double t[2]; /* We already know that the pattern is not clear, thus if some @@ -1854,11 +3107,18 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, if (extents == NULL) return FALSE; - _extents_to_linear_parameter (linear, extents, t); + _cairo_linear_pattern_box_to_parameter (linear, + extents->x, + extents->y, + extents->x + extents->width, + extents->y + extents->height, + t); + if (t[0] < 0.0 || t[1] > 1.0) return FALSE; } - } + } else + return FALSE; for (i = 1; i < gradient->n_stops; i++) if (! _cairo_color_stop_equal (&gradient->stops[0].color, @@ -1875,7 +3135,76 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, } /** - * _cairo_pattern_is_opaque_solid + * _cairo_pattern_is_constant_alpha: + * + * Convenience function to determine whether a pattern has constant + * alpha within the given extents. In this case the alpha argument is + * initialized to the alpha within the extents. + * + * Return value: %TRUE if the pattern has constant alpha. + **/ +cairo_bool_t +_cairo_pattern_is_constant_alpha (const cairo_pattern_t *abstract_pattern, + const cairo_rectangle_int_t *extents, + double *alpha) +{ + const cairo_pattern_union_t *pattern; + cairo_color_t color; + + if (_cairo_pattern_is_clear (abstract_pattern)) { + *alpha = 0.0; + return TRUE; + } + + if (_cairo_pattern_is_opaque (abstract_pattern, extents)) { + *alpha = 1.0; + return TRUE; + } + + pattern = (cairo_pattern_union_t *) abstract_pattern; + switch (pattern->base.type) { + case CAIRO_PATTERN_TYPE_SOLID: + *alpha = pattern->solid.color.alpha; + return TRUE; + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + if (_cairo_gradient_pattern_is_solid (&pattern->gradient.base, extents, &color)) { + *alpha = color.alpha; + return TRUE; + } else { + return FALSE; + } + + /* TODO: need to test these as well */ + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + case CAIRO_PATTERN_TYPE_MESH: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +static cairo_bool_t +_mesh_is_clear (const cairo_mesh_pattern_t *mesh) +{ + double x1, y1, x2, y2; + cairo_bool_t is_valid; + + is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2); + if (!is_valid) + return TRUE; + + if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON) + return TRUE; + + return FALSE; +} + +/** + * _cairo_pattern_is_opaque_solid: * * Convenience function to determine whether a pattern is an opaque * (alpha==1.0) solid color pattern. This is done by testing whether @@ -1900,30 +3229,39 @@ _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) static cairo_bool_t _surface_is_opaque (const cairo_surface_pattern_t *pattern, - const cairo_rectangle_int_t *r) + const cairo_rectangle_int_t *sample) { + cairo_rectangle_int_t extents; + if (pattern->surface->content & CAIRO_CONTENT_ALPHA) return FALSE; if (pattern->base.extend != CAIRO_EXTEND_NONE) return TRUE; - if (r != NULL) { - cairo_rectangle_int_t extents; + if (! _cairo_surface_get_extents (pattern->surface, &extents)) + return TRUE; - if (! _cairo_surface_get_extents (pattern->surface, &extents)) - return TRUE; + if (sample == NULL) + return FALSE; - if (r->x >= extents.x && - r->y >= extents.y && - r->x + r->width <= extents.x + extents.width && - r->y + r->height <= extents.y + extents.height) - { - return TRUE; - } - } + return _cairo_rectangle_contains_rectangle (&extents, sample); +} - return FALSE; +static cairo_bool_t +_raster_source_is_opaque (const cairo_raster_source_pattern_t *pattern, + const cairo_rectangle_int_t *sample) +{ + if (pattern->content & CAIRO_CONTENT_ALPHA) + return FALSE; + + if (pattern->base.extend != CAIRO_EXTEND_NONE) + return TRUE; + + if (sample == NULL) + return FALSE; + + return _cairo_rectangle_contains_rectangle (&pattern->extents, sample); } static cairo_bool_t @@ -1939,9 +3277,15 @@ _surface_is_clear (const cairo_surface_pattern_t *pattern) pattern->surface->content & CAIRO_CONTENT_ALPHA; } +static cairo_bool_t +_raster_source_is_clear (const cairo_raster_source_pattern_t *pattern) +{ + return pattern->extents.width == 0 || pattern->extents.height == 0; +} + static cairo_bool_t _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *sample) { unsigned int i; @@ -1962,14 +3306,21 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, if (_linear_pattern_is_degenerate (linear)) return FALSE; - if (extents == NULL) + if (sample == NULL) return FALSE; - _extents_to_linear_parameter (linear, extents, t); + _cairo_linear_pattern_box_to_parameter (linear, + sample->x, + sample->y, + sample->x + sample->width, + sample->y + sample->height, + t); + if (t[0] < 0.0 || t[1] > 1.0) return FALSE; } - } + } else + return FALSE; /* TODO: check actual intersection */ for (i = 0; i < gradient->n_stops; i++) if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color)) @@ -1979,7 +3330,7 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, } /** - * _cairo_pattern_is_opaque + * _cairo_pattern_is_opaque: * * Convenience function to determine whether a pattern is an opaque * pattern (of any type). The same caveats that apply to @@ -1989,7 +3340,7 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, **/ cairo_bool_t _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, - const cairo_rectangle_int_t *extents) + const cairo_rectangle_int_t *sample) { const cairo_pattern_union_t *pattern; @@ -2001,10 +3352,14 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, case CAIRO_PATTERN_TYPE_SOLID: return _cairo_pattern_is_opaque_solid (abstract_pattern); case CAIRO_PATTERN_TYPE_SURFACE: - return _surface_is_opaque (&pattern->surface, extents); + return _surface_is_opaque (&pattern->surface, sample); + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _raster_source_is_opaque (&pattern->raster_source, sample); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: - return _gradient_is_opaque (&pattern->gradient.base, extents); + return _gradient_is_opaque (&pattern->gradient.base, sample); + case CAIRO_PATTERN_TYPE_MESH: + return FALSE; } ASSERT_NOT_REACHED; @@ -2020,508 +3375,198 @@ _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern) return FALSE; pattern = (cairo_pattern_union_t *) abstract_pattern; - switch (pattern->type) { + switch (abstract_pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color); case CAIRO_PATTERN_TYPE_SURFACE: return _surface_is_clear (&pattern->surface); + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _raster_source_is_clear (&pattern->raster_source); case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: return _gradient_is_clear (&pattern->gradient.base, NULL); + case CAIRO_PATTERN_TYPE_MESH: + return _mesh_is_clear (&pattern->mesh); } ASSERT_NOT_REACHED; return FALSE; } +/* + * Will given row of back-translation matrix work with bilinear scale? + * This is true for scales larger than 1. Also it was judged acceptable + * for scales larger than .75. And if there is integer translation + * then a scale of exactly .5 works. + */ +static int +use_bilinear(double x, double y, double t) +{ + /* This is the inverse matrix! */ + double h = x*x + y*y; + if (h < 1.0 / (0.75 * 0.75)) + return TRUE; /* scale > .75 */ + if ((h > 3.99 && h < 4.01) /* scale is 1/2 */ + && !_cairo_fixed_from_double(x*y) /* parallel to an axis */ + && _cairo_fixed_is_integer (_cairo_fixed_from_double (t))) + return TRUE; + return FALSE; +} + /** * _cairo_pattern_analyze_filter: * @pattern: surface pattern - * @pad_out: location to store necessary padding in the source image, or %NULL * Returns: the optimized #cairo_filter_t to use with @pattern. * - * Analyze the filter to determine how much extra needs to be sampled - * from the source image to account for the filter radius and whether - * we can optimize the filter to a simpler value. - * - * XXX: We don't actually have any way of querying the backend for - * the filter radius, so we just guess base on what we know that - * backends do currently (see bug #10508) - */ + * Possibly optimize the filter to a simpler value depending on transformation + **/ cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, - double *pad_out) +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern) { - double pad; - cairo_filter_t optimized_filter; - switch (pattern->filter) { case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_FAST: /* If source pixels map 1:1 onto destination pixels, we do * not need to filter (and do not want to filter, since it * will cause blurriness) */ if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { - pad = 0.; - optimized_filter = CAIRO_FILTER_NEAREST; + return CAIRO_FILTER_NEAREST; } else { - /* 0.5 is enough for a bilinear filter. It's possible we - * should defensively use more for CAIRO_FILTER_BEST, but - * without a single example, it's hard to know how much - * more would be defensive... + /* Use BILINEAR for any scale greater than .75 instead + * of GOOD. For scales of 1 and larger this is identical, + * for the smaller sizes it was judged that the artifacts + * were not worse than the artifacts from a box filer. + * BILINEAR can also be used if the scale is exactly .5 + * and the translation in that direction is an integer. */ - pad = 0.5; - optimized_filter = pattern->filter; + if (pattern->filter == CAIRO_FILTER_GOOD && + use_bilinear (pattern->matrix.xx, pattern->matrix.xy, + pattern->matrix.x0) && + use_bilinear (pattern->matrix.yx, pattern->matrix.yy, + pattern->matrix.y0)) + return CAIRO_FILTER_BILINEAR; } break; - case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN: default: - pad = 0.; - optimized_filter = pattern->filter; break; } - if (pad_out) - *pad_out = pad; - - return optimized_filter; -} - - -static double -_pixman_nearest_sample (double d) -{ - return ceil (d - .5); -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **out, - cairo_surface_attributes_t *attr) -{ - cairo_surface_t *surface; - cairo_rectangle_int_t extents; - cairo_rectangle_int_t sampled_area; - double x1, y1, x2, y2; - int tx, ty; - double pad; - cairo_bool_t is_identity; - cairo_bool_t is_empty; - cairo_bool_t is_bounded; - cairo_int_status_t status; - - surface = cairo_surface_reference (pattern->surface); - - is_identity = FALSE; - attr->matrix = pattern->base.matrix; - attr->extend = pattern->base.extend; - attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); - attr->has_component_alpha = pattern->base.has_component_alpha; - - attr->x_offset = attr->y_offset = tx = ty = 0; - if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { - cairo_matrix_init_identity (&attr->matrix); - attr->x_offset = tx; - attr->y_offset = ty; - is_identity = TRUE; - } else if (attr->filter == CAIRO_FILTER_NEAREST) { - /* - * For NEAREST, we can remove the fractional translation component - * from the transformation - this ensures that the pattern will always - * hit fast-paths in the backends for simple transformations that - * become (almost) identity, without loss of quality. - */ - attr->matrix.x0 = 0; - attr->matrix.y0 = 0; - if (_cairo_matrix_is_pixel_exact (&attr->matrix)) { - /* The rounding here is rather peculiar as it needs to match the - * rounding performed on the sample coordinate used by pixman. - */ - attr->matrix.x0 = _pixman_nearest_sample (pattern->base.matrix.x0); - attr->matrix.y0 = _pixman_nearest_sample (pattern->base.matrix.y0); - } else { - attr->matrix.x0 = pattern->base.matrix.x0; - attr->matrix.y0 = pattern->base.matrix.y0; - } - - if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { - cairo_matrix_init_identity (&attr->matrix); - attr->x_offset = tx; - attr->y_offset = ty; - is_identity = TRUE; - } - } - - /* XXX: Hack: - * - * The way we currently support CAIRO_EXTEND_REFLECT is to create - * an image twice bigger on each side, and create a pattern of four - * images such that the new image, when repeated, has the same effect - * of reflecting the original pattern. - */ - if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT && - attr->extend == CAIRO_EXTEND_REFLECT) - { - cairo_t *cr; - cairo_surface_t *src; - int w, h; - - is_bounded = _cairo_surface_get_extents (surface, &extents); - assert (is_bounded); - - status = _cairo_surface_clone_similar (dst, surface, - extents.x, extents.y, - extents.width, extents.height, - &extents.x, &extents.y, &src); - if (unlikely (status)) - goto BAIL; - - w = 2 * extents.width; - h = 2 * extents.height; - - if (is_identity) { - attr->x_offset = -x; - x += tx; - while (x <= -w) - x += w; - while (x >= w) - x -= w; - extents.x += x; - tx = x = 0; - - attr->y_offset = -y; - y += ty; - while (y <= -h) - y += h; - while (y >= h) - y -= h; - extents.y += y; - ty = y = 0; - } - - cairo_surface_destroy (surface); - surface = _cairo_surface_create_similar_solid (dst, - dst->content, w, h, - CAIRO_COLOR_TRANSPARENT, - FALSE); - if (surface == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (surface->status)) { - cairo_surface_destroy (src); - return surface->status; - } - - surface->device_transform = pattern->surface->device_transform; - surface->device_transform_inverse = pattern->surface->device_transform_inverse; - - cr = cairo_create (surface); - - cairo_set_source_surface (cr, src, -extents.x, -extents.y); - cairo_paint (cr); - - cairo_scale (cr, -1, +1); - cairo_set_source_surface (cr, src, extents.x-w, -extents.y); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x, -extents.y); - cairo_paint (cr); - - cairo_scale (cr, +1, -1); - cairo_set_source_surface (cr, src, extents.x-w, extents.y-h); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x, extents.y-h); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x-w, extents.y); - cairo_paint (cr); - cairo_set_source_surface (cr, src, extents.x, extents.y); - cairo_paint (cr); - - cairo_scale (cr, -1, +1); - cairo_set_source_surface (cr, src, -extents.x, extents.y-h); - cairo_paint (cr); - cairo_set_source_surface (cr, src, -extents.x, extents.y); - cairo_paint (cr); - - status = cairo_status (cr); - cairo_destroy (cr); - - cairo_surface_destroy (src); - - if (unlikely (status)) - goto BAIL; - - attr->extend = CAIRO_EXTEND_REPEAT; - } - - /* We first transform the rectangle to the coordinate space of the - * source surface so that we only need to clone that portion of the - * surface that will be read. - */ - x1 = x; - y1 = y; - x2 = x + (int) width; - y2 = y + (int) height; - if (! is_identity) { - _cairo_matrix_transform_bounding_box (&attr->matrix, - &x1, &y1, &x2, &y2, - NULL); - } - - sampled_area.x = floor (x1 - pad); - sampled_area.y = floor (y1 - pad); - sampled_area.width = ceil (x2 + pad) - sampled_area.x; - sampled_area.height = ceil (y2 + pad) - sampled_area.y; - - sampled_area.x += tx; - sampled_area.y += ty; - - if ( _cairo_surface_get_extents (surface, &extents)) { - if (attr->extend == CAIRO_EXTEND_NONE) { - /* Never acquire a larger area than the source itself */ - is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); - } else { - int trim = 0; - - if (sampled_area.x >= extents.x && - sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) - { - /* source is horizontally contained within extents, trim */ - extents.x = sampled_area.x; - extents.width = sampled_area.width; - trim |= 0x1; - } - - if (sampled_area.y >= extents.y && - sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) - { - /* source is vertically contained within extents, trim */ - extents.y = sampled_area.y; - extents.height = sampled_area.height; - trim |= 0x2; - } - - if (trim == 0x3) { - /* source is wholly contained within extents, drop the REPEAT */ - attr->extend = CAIRO_EXTEND_NONE; - } - - is_empty = extents.width == 0 || extents.height == 0; - } - } - - /* XXX can we use is_empty? */ - - status = _cairo_surface_clone_similar (dst, surface, - extents.x, extents.y, - extents.width, extents.height, - &x, &y, out); - if (unlikely (status)) - goto BAIL; - - if (x != 0 || y != 0) { - if (is_identity) { - attr->x_offset -= x; - attr->y_offset -= y; - } else { - cairo_matrix_t m; - - x -= attr->x_offset; - y -= attr->y_offset; - attr->x_offset = 0; - attr->y_offset = 0; - - cairo_matrix_init_translate (&m, -x, -y); - cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m); - } - } - - /* reduce likelihood of range overflow with large downscaling */ - if (! is_identity) { - cairo_matrix_t m; - cairo_status_t invert_status; - - m = attr->matrix; - invert_status = cairo_matrix_invert (&m); - assert (invert_status == CAIRO_STATUS_SUCCESS); - - if (m.x0 != 0. || m.y0 != 0.) { - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - x = floor (m.x0 / 2); - y = floor (m.y0 / 2); - attr->x_offset -= x; - attr->y_offset -= y; - cairo_matrix_init_translate (&m, x, y); - cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix); - } - } - - BAIL: - cairo_surface_destroy (surface); - return status; + return pattern->filter; } /** - * _cairo_pattern_acquire_surface: - * @pattern: a #cairo_pattern_t - * @dst: destination surface - * @x: X coordinate in source corresponding to left side of destination area - * @y: Y coordinate in source corresponding to top side of destination area - * @width: width of destination area - * @height: height of destination area - * @surface_out: location to store a pointer to a surface - * @attributes: surface attributes that destination backend should apply to - * the returned surface + * _cairo_hypot: + * Returns: value similar to hypot(@x,@y) * - * A convenience function to obtain a surface to use as the source for - * drawing on @dst. - * - * Note that this function is only suitable for use when the destination - * surface is pixel based and 1 device unit maps to one pixel. - * - * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. + * May want to replace this with Manhattan distance (abs(x)+abs(y)) if + * hypot is too slow, as there is no need for accuracy here. **/ -cairo_int_status_t -_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **surface_out, - cairo_surface_attributes_t *attributes) +static inline double +_cairo_hypot(double x, double y) { - if (unlikely (pattern->status)) { - *surface_out = NULL; - return pattern->status; - } - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_pattern_acquire_surface_for_solid ((cairo_solid_pattern_t *) pattern, - dst, x, y, width, height, - surface_out, - attributes); - - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - return _cairo_pattern_acquire_surface_for_gradient ((cairo_gradient_pattern_t *) pattern, - dst, x, y, width, height, - surface_out, - attributes); - - case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_pattern_acquire_surface_for_surface ((cairo_surface_pattern_t *) pattern, - dst, x, y, width, height, - flags, - surface_out, - attributes); - - default: - ASSERT_NOT_REACHED; - return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - } + return hypot(x, y); } /** - * _cairo_pattern_release_surface: - * @pattern: a #cairo_pattern_t - * @surface: a surface obtained by _cairo_pattern_acquire_surface - * @attributes: attributes obtained by _cairo_pattern_acquire_surface + * _cairo_pattern_sampled_area: * - * Releases resources obtained by _cairo_pattern_acquire_surface. + * Return region of @pattern that will be sampled to fill @extents, + * based on the transformation and filter. + * + * This does not include pixels that are mulitiplied by values very + * close to zero by the ends of filters. This is so that transforms + * that should be the identity or 90 degree rotations do not expand + * the source unexpectedly. + * + * XXX: We don't actually have any way of querying the backend for + * the filter radius, so we just guess base on what we know that + * backends do currently (see bug #10508) **/ void -_cairo_pattern_release_surface (const cairo_pattern_t *pattern, - cairo_surface_t *surface, - cairo_surface_attributes_t *attributes) +_cairo_pattern_sampled_area (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *sample) { - cairo_surface_destroy (surface); -} + double x1, x2, y1, y2; + double padx, pady; -cairo_int_status_t -_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **src_out, - cairo_surface_t **mask_out, - cairo_surface_attributes_t *src_attributes, - cairo_surface_attributes_t *mask_attributes) -{ - cairo_int_status_t status; - cairo_pattern_union_t src_tmp; - - if (unlikely (src->status)) - return src->status; - if (unlikely (mask != NULL && mask->status)) - return mask->status; - - /* If src and mask are both solid, then the mask alpha can be - * combined into src and mask can be ignored. */ - - if (src->type == CAIRO_PATTERN_TYPE_SOLID && - mask && - ! mask->has_component_alpha && - mask->type == CAIRO_PATTERN_TYPE_SOLID) - { - cairo_color_t combined; - cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src; - cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask; - - combined = src_solid->color; - _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha); - - _cairo_pattern_init_solid (&src_tmp.solid, &combined); - - src = &src_tmp.base; - mask = NULL; + /* Assume filters are interpolating, which means identity + cannot change the image */ + if (_cairo_matrix_is_identity (&pattern->matrix)) { + *sample = *extents; + return; } - status = _cairo_pattern_acquire_surface (src, dst, - src_x, src_y, - width, height, - flags, - src_out, src_attributes); - if (unlikely (status)) - goto BAIL; + /* Transform the centers of the corner pixels */ + x1 = extents->x + 0.5; + y1 = extents->y + 0.5; + x2 = x1 + (extents->width - 1); + y2 = y1 + (extents->height - 1); + _cairo_matrix_transform_bounding_box (&pattern->matrix, + &x1, &y1, &x2, &y2, + NULL); - if (mask == NULL) { - *mask_out = NULL; - goto BAIL; + /* How far away from center will it actually sample? + * This is the distance from a transformed pixel center to the + * furthest sample of reasonable size. + */ + switch (pattern->filter) { + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_FAST: + /* Correct value is zero, but when the sample is on an integer + * it is unknown if the backend will sample the pixel to the + * left or right. This value makes it include both possible pixels. + */ + padx = pady = 0.004; + break; + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + default: + /* Correct value is .5 */ + padx = pady = 0.495; + break; + case CAIRO_FILTER_GOOD: + /* Correct value is max(width,1)*.5 */ + padx = _cairo_hypot (pattern->matrix.xx, pattern->matrix.xy); + if (padx <= 1.0) padx = 0.495; + else if (padx >= 16.0) padx = 7.92; + else padx *= 0.495; + pady = _cairo_hypot (pattern->matrix.yx, pattern->matrix.yy); + if (pady <= 1.0) pady = 0.495; + else if (pady >= 16.0) pady = 7.92; + else pady *= 0.495; + break; + case CAIRO_FILTER_BEST: + /* Correct value is width*2 */ + padx = _cairo_hypot (pattern->matrix.xx, pattern->matrix.xy) * 1.98; + if (padx > 7.92) padx = 7.92; + pady = _cairo_hypot (pattern->matrix.yx, pattern->matrix.yy) * 1.98; + if (pady > 7.92) pady = 7.92; + break; } - status = _cairo_pattern_acquire_surface (mask, dst, - mask_x, mask_y, - width, height, - flags, - mask_out, mask_attributes); - if (unlikely (status)) - _cairo_pattern_release_surface (src, *src_out, src_attributes); + /* round furthest samples to edge of pixels */ + x1 = floor (x1 - padx); + if (x1 < CAIRO_RECT_INT_MIN) x1 = CAIRO_RECT_INT_MIN; + sample->x = x1; - BAIL: - if (src == &src_tmp.base) - _cairo_pattern_fini (&src_tmp.base); + y1 = floor (y1 - pady); + if (y1 < CAIRO_RECT_INT_MIN) y1 = CAIRO_RECT_INT_MIN; + sample->y = y1; - return status; + x2 = floor (x2 + padx) + 1.0; + if (x2 > CAIRO_RECT_INT_MAX) x2 = CAIRO_RECT_INT_MAX; + sample->width = x2 - x1; + + y2 = floor (y2 + pady) + 1.0; + if (y2 > CAIRO_RECT_INT_MAX) y2 = CAIRO_RECT_INT_MAX; + sample->height = y2 - y1; } /** @@ -2532,16 +3577,22 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, * For unbounded patterns, the @extents will be initialized with * "infinite" extents, (minimum and maximum fixed-point values). * + * When is_vector is TRUE, avoid rounding to zero widths or heights that + * are less than 1 unit. + * * XXX: Currently, bounded gradient patterns will also return * "infinite" extents, though it would be possible to optimize these * with a little more work. **/ void _cairo_pattern_get_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents) + cairo_rectangle_int_t *extents, + cairo_bool_t is_vector) { double x1, y1, x2, y2; - cairo_status_t status; + int ix1, ix2, iy1, iy2; + cairo_bool_t round_x = FALSE; + cairo_bool_t round_y = FALSE; switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: @@ -2553,7 +3604,6 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; - double pad; if (! _cairo_surface_get_extents (surface, &surface_extents)) goto UNBOUNDED; @@ -2564,14 +3614,61 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; - /* The filter can effectively enlarge the extents of the - * pattern, so extend as necessary. - */ - _cairo_pattern_analyze_filter (&surface_pattern->base, &pad); - x1 = surface_extents.x - pad; - y1 = surface_extents.y - pad; - x2 = surface_extents.x + (int) surface_extents.width + pad; - y2 = surface_extents.y + (int) surface_extents.height + pad; + x1 = surface_extents.x; + y1 = surface_extents.y; + x2 = surface_extents.x + (int) surface_extents.width; + y2 = surface_extents.y + (int) surface_extents.height; + + goto HANDLE_FILTER; + } + break; + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + { + const cairo_raster_source_pattern_t *raster = + (const cairo_raster_source_pattern_t *) pattern; + + if (raster->extents.width == 0 || raster->extents.height == 0) + goto EMPTY; + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + x1 = raster->extents.x; + y1 = raster->extents.y; + x2 = raster->extents.x + (int) raster->extents.width; + y2 = raster->extents.y + (int) raster->extents.height; + } + HANDLE_FILTER: + switch (pattern->filter) { + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_FAST: + round_x = round_y = TRUE; + /* We don't know which way .5 will go, so fudge it slightly. */ + x1 -= 0.004; + y1 -= 0.004; + x2 += 0.004; + y2 += 0.004; + break; + case CAIRO_FILTER_BEST: + /* Assume best filter will produce nice antialiased edges */ + break; + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + case CAIRO_FILTER_GOOD: + default: + /* These filters can blur the edge out 1/2 pixel when scaling up */ + if (_cairo_hypot (pattern->matrix.xx, pattern->matrix.yx) < 1.0) { + x1 -= 0.5; + x2 += 0.5; + round_x = TRUE; + } + if (_cairo_hypot (pattern->matrix.xy, pattern->matrix.yy) < 1.0) { + y1 -= 0.5; + y2 += 0.5; + round_y = TRUE; + } + break; } break; @@ -2581,40 +3678,32 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, (const cairo_radial_pattern_t *) pattern; double cx1, cy1; double cx2, cy2; - double r, D; + double r1, r2; - if (radial->r1 == 0 && radial->r2 == 0) + if (_radial_pattern_is_degenerate (radial)) { + /* cairo-gstate should have optimised degenerate + * patterns to solid clear patterns, so we can ignore + * them here. */ goto EMPTY; + } - cx1 = _cairo_fixed_to_double (radial->c1.x); - cy1 = _cairo_fixed_to_double (radial->c1.y); - r = _cairo_fixed_to_double (radial->r1); - x1 = cx1 - r; x2 = cx1 + r; - y1 = cy1 - r; y2 = cy1 + r; - - cx2 = _cairo_fixed_to_double (radial->c2.x); - cy2 = _cairo_fixed_to_double (radial->c2.y); - r = fabs (_cairo_fixed_to_double (radial->r2)); - + /* TODO: in some cases (focus outside/on the circle) it is + * half-bounded. */ if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; - /* We need to be careful, as if the circles are not - * self-contained, then the solution is actually unbounded. - */ - D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2); - if (D > r*r - 1e-5) - goto UNBOUNDED; + cx1 = radial->cd1.center.x; + cy1 = radial->cd1.center.y; + r1 = radial->cd1.radius; - if (cx2 - r < x1) - x1 = cx2 - r; - if (cx2 + r > x2) - x2 = cx2 + r; + cx2 = radial->cd2.center.x; + cy2 = radial->cd2.center.y; + r2 = radial->cd2.radius; - if (cy2 - r < y1) - y1 = cy2 - r; - if (cy2 + r > y2) - y2 = cy2 + r; + x1 = MIN (cx1 - r1, cx2 - r2); + y1 = MIN (cy1 - r1, cy2 - r2); + x2 = MAX (cx1 + r1, cx2 + r2); + y2 = MAX (cy1 + r1, cy2 + r2); } break; @@ -2626,25 +3715,44 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED; - if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y) + if (_linear_pattern_is_degenerate (linear)) { + /* cairo-gstate should have optimised degenerate + * patterns to solid ones, so we can again ignore + * them here. */ goto EMPTY; + } + /* TODO: to get tight extents, use the matrix to transform + * the pattern instead of transforming the extents later. */ if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) goto UNBOUNDED; - if (linear->p1.x == linear->p2.x) { + if (linear->pd1.x == linear->pd2.x) { x1 = -HUGE_VAL; x2 = HUGE_VAL; - y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y)); - y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y)); - } else if (linear->p1.y == linear->p2.y) { - x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x)); - x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x)); + y1 = MIN (linear->pd1.y, linear->pd2.y); + y2 = MAX (linear->pd1.y, linear->pd2.y); + } else if (linear->pd1.y == linear->pd2.y) { + x1 = MIN (linear->pd1.x, linear->pd2.x); + x2 = MAX (linear->pd1.x, linear->pd2.x); y1 = -HUGE_VAL; y2 = HUGE_VAL; } else { goto UNBOUNDED; } + + /* The current linear renderer just point-samples in the middle + of the pixels, similar to the NEAREST filter: */ + round_x = round_y = TRUE; + } + break; + + case CAIRO_PATTERN_TYPE_MESH: + { + const cairo_mesh_pattern_t *mesh = + (const cairo_mesh_pattern_t *) pattern; + if (! _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2)) + goto EMPTY; } break; @@ -2657,6 +3765,7 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0; } else { cairo_matrix_t imatrix; + cairo_status_t status; imatrix = pattern->matrix; status = cairo_matrix_invert (&imatrix); @@ -2668,22 +3777,38 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, NULL); } - x1 = floor (x1); + if (!round_x) { + x1 -= 0.5; + x2 += 0.5; + } if (x1 < CAIRO_RECT_INT_MIN) - x1 = CAIRO_RECT_INT_MIN; - y1 = floor (y1); - if (y1 < CAIRO_RECT_INT_MIN) - y1 = CAIRO_RECT_INT_MIN; - - x2 = ceil (x2); + ix1 = CAIRO_RECT_INT_MIN; + else + ix1 = _cairo_lround (x1); if (x2 > CAIRO_RECT_INT_MAX) - x2 = CAIRO_RECT_INT_MAX; - y2 = ceil (y2); - if (y2 > CAIRO_RECT_INT_MAX) - y2 = CAIRO_RECT_INT_MAX; + ix2 = CAIRO_RECT_INT_MAX; + else + ix2 = _cairo_lround (x2); + extents->x = ix1; extents->width = ix2 - ix1; + if (is_vector && extents->width == 0 && x1 != x2) + extents->width += 1; + + if (!round_y) { + y1 -= 0.5; + y2 += 0.5; + } + if (y1 < CAIRO_RECT_INT_MIN) + iy1 = CAIRO_RECT_INT_MIN; + else + iy1 = _cairo_lround (y1); + if (y2 > CAIRO_RECT_INT_MAX) + iy2 = CAIRO_RECT_INT_MAX; + else + iy2 = _cairo_lround (y2); + extents->y = iy1; extents->height = iy2 - iy1; + if (is_vector && extents->height == 0 && y1 != y2) + extents->height += 1; - extents->x = x1; extents->width = x2 - x1; - extents->y = y1; extents->height = y2 - y1; return; UNBOUNDED: @@ -2697,13 +3822,51 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, return; } +/** + * _cairo_pattern_get_ink_extents: + * + * Return the "target-space" inked extents of @pattern in @extents. + **/ +cairo_int_status_t +_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && + pattern->extend == CAIRO_EXTEND_NONE) + { + const cairo_surface_pattern_t *surface_pattern = + (const cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + + surface = _cairo_surface_get_source (surface, NULL); + if (_cairo_surface_is_recording (surface)) { + cairo_matrix_t imatrix; + cairo_box_t box; + cairo_status_t status; + + imatrix = pattern->matrix; + status = cairo_matrix_invert (&imatrix); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)surface, + &box, &imatrix); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&box, extents); + return CAIRO_STATUS_SUCCESS; + } + } + + _cairo_pattern_get_extents (pattern, extents, TRUE); + return CAIRO_STATUS_SUCCESS; +} static unsigned long _cairo_solid_pattern_hash (unsigned long hash, - const cairo_pattern_t *pattern) + const cairo_solid_pattern_t *solid) { - const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; - hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); return hash; @@ -2725,7 +3888,7 @@ _cairo_gradient_color_stops_hash (unsigned long hash, sizeof (double)); hash = _cairo_hash_bytes (hash, &gradient->stops[n].color, - sizeof (cairo_color_t)); + sizeof (cairo_color_stop_t)); } return hash; @@ -2735,8 +3898,8 @@ unsigned long _cairo_linear_pattern_hash (unsigned long hash, const cairo_linear_pattern_t *linear) { - hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1)); - hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2)); + hash = _cairo_hash_bytes (hash, &linear->pd1, sizeof (linear->pd1)); + hash = _cairo_hash_bytes (hash, &linear->pd2, sizeof (linear->pd2)); return _cairo_gradient_color_stops_hash (hash, &linear->base); } @@ -2745,25 +3908,44 @@ unsigned long _cairo_radial_pattern_hash (unsigned long hash, const cairo_radial_pattern_t *radial) { - hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1)); - hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1)); - hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2)); - hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2)); + hash = _cairo_hash_bytes (hash, &radial->cd1.center, sizeof (radial->cd1.center)); + hash = _cairo_hash_bytes (hash, &radial->cd1.radius, sizeof (radial->cd1.radius)); + hash = _cairo_hash_bytes (hash, &radial->cd2.center, sizeof (radial->cd2.center)); + hash = _cairo_hash_bytes (hash, &radial->cd2.radius, sizeof (radial->cd2.radius)); return _cairo_gradient_color_stops_hash (hash, &radial->base); } static unsigned long -_cairo_surface_pattern_hash (unsigned long hash, - const cairo_pattern_t *pattern) +_cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh) { - const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; + const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0); + unsigned int i, n = _cairo_array_num_elements (&mesh->patches); + for (i = 0; i < n; i++) + hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t)); + + return hash; +} + +static unsigned long +_cairo_surface_pattern_hash (unsigned long hash, + const cairo_surface_pattern_t *surface) +{ hash ^= surface->surface->unique_id; return hash; } +static unsigned long +_cairo_raster_source_pattern_hash (unsigned long hash, + const cairo_raster_source_pattern_t *raster) +{ + hash ^= (uintptr_t)raster->user_data; + + return hash; +} + unsigned long _cairo_pattern_hash (const cairo_pattern_t *pattern) { @@ -2787,62 +3969,27 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern) switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_solid_pattern_hash (hash, pattern); + return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_MESH: + return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern); case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_surface_pattern_hash (hash, pattern); + return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _cairo_raster_source_pattern_hash (hash, (cairo_raster_source_pattern_t *) pattern); default: ASSERT_NOT_REACHED; return FALSE; } } -static unsigned long -_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern) -{ - cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; - - return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t)); -} - -unsigned long -_cairo_pattern_size (const cairo_pattern_t *pattern) -{ - if (pattern->status) - return 0; - - /* XXX */ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return sizeof (cairo_solid_pattern_t); - break; - case CAIRO_PATTERN_TYPE_SURFACE: - return sizeof (cairo_surface_pattern_t); - break; - case CAIRO_PATTERN_TYPE_LINEAR: - return sizeof (cairo_linear_pattern_t) + - _cairo_gradient_pattern_color_stops_size (pattern); - break; - case CAIRO_PATTERN_TYPE_RADIAL: - return sizeof (cairo_radial_pattern_t) + - _cairo_gradient_pattern_color_stops_size (pattern); - default: - ASSERT_NOT_REACHED; - return 0; - } -} - - static cairo_bool_t -_cairo_solid_pattern_equal (const cairo_pattern_t *A, - const cairo_pattern_t *B) +_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a, + const cairo_solid_pattern_t *b) { - const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A; - const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B; - return _cairo_color_equal (&a->color, &b->color); } @@ -2869,16 +4016,16 @@ cairo_bool_t _cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, const cairo_linear_pattern_t *b) { - if (a->p1.x != b->p1.x) + if (a->pd1.x != b->pd1.x) return FALSE; - if (a->p1.y != b->p1.y) + if (a->pd1.y != b->pd1.y) return FALSE; - if (a->p2.x != b->p2.x) + if (a->pd2.x != b->pd2.x) return FALSE; - if (a->p2.y != b->p2.y) + if (a->pd2.y != b->pd2.y) return FALSE; return _cairo_gradient_color_stops_equal (&a->base, &b->base); @@ -2888,37 +4035,64 @@ cairo_bool_t _cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, const cairo_radial_pattern_t *b) { - if (a->c1.x != b->c1.x) + if (a->cd1.center.x != b->cd1.center.x) return FALSE; - if (a->c1.y != b->c1.y) + if (a->cd1.center.y != b->cd1.center.y) return FALSE; - if (a->r1 != b->r1) + if (a->cd1.radius != b->cd1.radius) return FALSE; - if (a->c2.x != b->c2.x) + if (a->cd2.center.x != b->cd2.center.x) return FALSE; - if (a->c2.y != b->c2.y) + if (a->cd2.center.y != b->cd2.center.y) return FALSE; - if (a->r2 != b->r2) + if (a->cd2.radius != b->cd2.radius) return FALSE; return _cairo_gradient_color_stops_equal (&a->base, &b->base); } static cairo_bool_t -_cairo_surface_pattern_equal (const cairo_pattern_t *A, - const cairo_pattern_t *B) +_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a, + const cairo_mesh_pattern_t *b) { - const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A; - const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B; + const cairo_mesh_patch_t *patch_a, *patch_b; + unsigned int i, num_patches_a, num_patches_b; + num_patches_a = _cairo_array_num_elements (&a->patches); + num_patches_b = _cairo_array_num_elements (&b->patches); + + if (num_patches_a != num_patches_b) + return FALSE; + + for (i = 0; i < num_patches_a; i++) { + patch_a = _cairo_array_index_const (&a->patches, i); + patch_b = _cairo_array_index_const (&b->patches, i); + if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0) + return FALSE; + } + + return TRUE; +} + +static cairo_bool_t +_cairo_surface_pattern_equal (const cairo_surface_pattern_t *a, + const cairo_surface_pattern_t *b) +{ return a->surface->unique_id == b->surface->unique_id; } +static cairo_bool_t +_cairo_raster_source_pattern_equal (const cairo_raster_source_pattern_t *a, + const cairo_raster_source_pattern_t *b) +{ + return a->user_data == b->user_data; +} + cairo_bool_t _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) { @@ -2947,15 +4121,23 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) switch (a->type) { case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_solid_pattern_equal (a, b); + return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a, + (cairo_solid_pattern_t *) b); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a, (cairo_linear_pattern_t *) b); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a, (cairo_radial_pattern_t *) b); + case CAIRO_PATTERN_TYPE_MESH: + return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a, + (cairo_mesh_pattern_t *) b); case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_surface_pattern_equal (a, b); + return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a, + (cairo_surface_pattern_t *) b); + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _cairo_raster_source_pattern_equal ((cairo_raster_source_pattern_t *) a, + (cairo_raster_source_pattern_t *) b); default: ASSERT_NOT_REACHED; return FALSE; @@ -2963,7 +4145,7 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) } /** - * cairo_pattern_get_rgba + * cairo_pattern_get_rgba: * @pattern: a #cairo_pattern_t * @red: return value for red component of color, or %NULL * @green: return value for green component of color, or %NULL @@ -3007,10 +4189,10 @@ cairo_pattern_get_rgba (cairo_pattern_t *pattern, } /** - * cairo_pattern_get_surface + * cairo_pattern_get_surface: * @pattern: a #cairo_pattern_t * @surface: return value for surface of pattern, or %NULL - * + * * Gets the surface of a surface pattern. The reference returned in * @surface is owned by the pattern; the caller should call * cairo_surface_reference() if the surface is to be retained. @@ -3031,7 +4213,7 @@ cairo_pattern_get_surface (cairo_pattern_t *pattern, return pattern->status; if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_STATUS_PATTERN_TYPE_MISMATCH; + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (surface) *surface = spat->surface; @@ -3040,7 +4222,7 @@ cairo_pattern_get_surface (cairo_pattern_t *pattern, } /** - * cairo_pattern_get_color_stop_rgba + * cairo_pattern_get_color_stop_rgba: * @pattern: a #cairo_pattern_t * @index: index of the stop to return data for * @offset: return value for the offset of the stop, or %NULL @@ -3050,8 +4232,9 @@ cairo_pattern_get_surface (cairo_pattern_t *pattern, * @alpha: return value for alpha component of color, or %NULL * * Gets the color and offset information at the given @index for a - * gradient pattern. Values of @index are 0 to 1 less than the number - * returned by cairo_pattern_get_color_stop_count(). + * gradient pattern. Values of @index range from 0 to n-1 + * where n is the number returned + * by cairo_pattern_get_color_stop_count(). * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @index is not valid for the given pattern. If the pattern is @@ -3093,7 +4276,7 @@ cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, } /** - * cairo_pattern_get_color_stop_count + * cairo_pattern_get_color_stop_count: * @pattern: a #cairo_pattern_t * @count: return value for the number of color stops, or %NULL * @@ -3105,7 +4288,7 @@ cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, * pattern. * * Since: 1.4 - */ + **/ cairo_status_t cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, int *count) @@ -3126,7 +4309,7 @@ cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, } /** - * cairo_pattern_get_linear_points + * cairo_pattern_get_linear_points: * @pattern: a #cairo_pattern_t * @x0: return value for the x coordinate of the first point, or %NULL * @y0: return value for the y coordinate of the first point, or %NULL @@ -3155,19 +4338,19 @@ cairo_pattern_get_linear_points (cairo_pattern_t *pattern, return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (x0) - *x0 = _cairo_fixed_to_double (linear->p1.x); + *x0 = linear->pd1.x; if (y0) - *y0 = _cairo_fixed_to_double (linear->p1.y); + *y0 = linear->pd1.y; if (x1) - *x1 = _cairo_fixed_to_double (linear->p2.x); + *x1 = linear->pd2.x; if (y1) - *y1 = _cairo_fixed_to_double (linear->p2.y); + *y1 = linear->pd2.y; return CAIRO_STATUS_SUCCESS; } /** - * cairo_pattern_get_radial_circles + * cairo_pattern_get_radial_circles: * @pattern: a #cairo_pattern_t * @x0: return value for the x coordinate of the center of the first circle, or %NULL * @y0: return value for the y coordinate of the center of the first circle, or %NULL @@ -3199,30 +4382,410 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); if (x0) - *x0 = _cairo_fixed_to_double (radial->c1.x); + *x0 = radial->cd1.center.x; if (y0) - *y0 = _cairo_fixed_to_double (radial->c1.y); + *y0 = radial->cd1.center.y; if (r0) - *r0 = _cairo_fixed_to_double (radial->r1); + *r0 = radial->cd1.radius; if (x1) - *x1 = _cairo_fixed_to_double (radial->c2.x); + *x1 = radial->cd2.center.x; if (y1) - *y1 = _cairo_fixed_to_double (radial->c2.y); + *y1 = radial->cd2.center.y; if (r1) - *r1 = _cairo_fixed_to_double (radial->r2); + *r1 = radial->cd2.radius; return CAIRO_STATUS_SUCCESS; } +/** + * cairo_mesh_pattern_get_patch_count: + * @pattern: a #cairo_pattern_t + * @count: return value for the number patches, or %NULL + * + * Gets the number of patches specified in the given mesh pattern. + * + * The number only includes patches which have been finished by + * calling cairo_mesh_pattern_end_patch(). For example it will be 0 + * during the definition of the first patch. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh + * pattern. + * + * Since: 1.12 + **/ +cairo_status_t +cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern, + unsigned int *count) +{ + cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; + + if (unlikely (pattern->status)) + return pattern->status; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (count) { + *count = _cairo_array_num_elements (&mesh->patches); + if (mesh->current_patch) + *count -= 1; + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_mesh_pattern_get_patch_count); + +/** + * cairo_mesh_pattern_get_path: + * @pattern: a #cairo_pattern_t + * @patch_num: the patch number to return data for + * + * Gets path defining the patch @patch_num for a mesh + * pattern. + * + * @patch_num can range from 0 to n-1 where n is the number returned by + * cairo_mesh_pattern_get_patch_count(). + * + * Return value: the path defining the patch, or a path with status + * %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not + * valid for @pattern. If @pattern is not a mesh pattern, a path with + * status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned. + * + * Since: 1.12 + **/ +cairo_path_t * +cairo_mesh_pattern_get_path (cairo_pattern_t *pattern, + unsigned int patch_num) +{ + cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; + const cairo_mesh_patch_t *patch; + cairo_path_t *path; + cairo_path_data_t *data; + unsigned int patch_count; + int l, current_point; + + if (unlikely (pattern->status)) + return _cairo_path_create_in_error (pattern->status); + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) + return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH)); + + patch_count = _cairo_array_num_elements (&mesh->patches); + if (mesh->current_patch) + patch_count--; + + if (unlikely (patch_num >= patch_count)) + return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); + + patch = _cairo_array_index_const (&mesh->patches, patch_num); + + path = _cairo_malloc (sizeof (cairo_path_t)); + if (path == NULL) + return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + path->num_data = 18; + path->data = _cairo_malloc_ab (path->num_data, + sizeof (cairo_path_data_t)); + if (path->data == NULL) { + free (path); + return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + data = path->data; + data[0].header.type = CAIRO_PATH_MOVE_TO; + data[0].header.length = 2; + data[1].point.x = patch->points[0][0].x; + data[1].point.y = patch->points[0][0].y; + data += data[0].header.length; + + current_point = 0; + + for (l = 0; l < 4; l++) { + int i, j, k; + + data[0].header.type = CAIRO_PATH_CURVE_TO; + data[0].header.length = 4; + + for (k = 1; k < 4; k++) { + current_point = (current_point + 1) % 12; + i = mesh_path_point_i[current_point]; + j = mesh_path_point_j[current_point]; + data[k].point.x = patch->points[i][j].x; + data[k].point.y = patch->points[i][j].y; + } + + data += data[0].header.length; + } + + path->status = CAIRO_STATUS_SUCCESS; + + return path; +} +slim_hidden_def (cairo_mesh_pattern_get_path); + +/** + * cairo_mesh_pattern_get_corner_color_rgba: + * @pattern: a #cairo_pattern_t + * @patch_num: the patch number to return data for + * @corner_num: the corner number to return data for + * @red: return value for red component of color, or %NULL + * @green: return value for green component of color, or %NULL + * @blue: return value for blue component of color, or %NULL + * @alpha: return value for alpha component of color, or %NULL + * + * Gets the color information in corner @corner_num of patch + * @patch_num for a mesh pattern. + * + * @patch_num can range from 0 to n-1 where n is the number returned by + * cairo_mesh_pattern_get_patch_count(). + * + * Valid values for @corner_num are from 0 to 3 and identify the + * corners as explained in cairo_pattern_create_mesh(). + * + * 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 + * is returned. + * + * Since: 1.12 + **/ +cairo_status_t +cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern, + unsigned int patch_num, + unsigned int corner_num, + double *red, double *green, + double *blue, double *alpha) +{ + cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; + unsigned int patch_count; + const cairo_mesh_patch_t *patch; + + if (unlikely (pattern->status)) + return pattern->status; + + if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (unlikely (corner_num > 3)) + return _cairo_error (CAIRO_STATUS_INVALID_INDEX); + + patch_count = _cairo_array_num_elements (&mesh->patches); + if (mesh->current_patch) + patch_count--; + + if (unlikely (patch_num >= patch_count)) + return _cairo_error (CAIRO_STATUS_INVALID_INDEX); + + patch = _cairo_array_index_const (&mesh->patches, patch_num); + + if (red) + *red = patch->colors[corner_num].red; + if (green) + *green = patch->colors[corner_num].green; + if (blue) + *blue = patch->colors[corner_num].blue; + if (alpha) + *alpha = patch->colors[corner_num].alpha; + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_mesh_pattern_get_corner_color_rgba); + +/** + * cairo_mesh_pattern_get_control_point: + * @pattern: a #cairo_pattern_t + * @patch_num: the patch number to return data for + * @point_num: the control point number to return data for + * @x: return value for the x coordinate of the control point, or %NULL + * @y: return value for the y coordinate of the control point, or %NULL + * + * Gets the control point @point_num of patch @patch_num for a mesh + * pattern. + * + * @patch_num can range from 0 to n-1 where n is the number returned by + * cairo_mesh_pattern_get_patch_count(). + * + * Valid values for @point_num are from 0 to 3 and identify the + * control points as explained in cairo_pattern_create_mesh(). + * + * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX + * if @patch_num or @point_num is not valid for @pattern. If @pattern + * is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is + * returned. + * + * Since: 1.12 + **/ +cairo_status_t +cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern, + unsigned int patch_num, + unsigned int point_num, + double *x, double *y) +{ + cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; + const cairo_mesh_patch_t *patch; + unsigned int patch_count; + int i, j; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_MESH) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (point_num > 3) + return _cairo_error (CAIRO_STATUS_INVALID_INDEX); + + patch_count = _cairo_array_num_elements (&mesh->patches); + if (mesh->current_patch) + patch_count--; + + if (unlikely (patch_num >= patch_count)) + return _cairo_error (CAIRO_STATUS_INVALID_INDEX); + + patch = _cairo_array_index_const (&mesh->patches, patch_num); + + i = mesh_control_point_i[point_num]; + j = mesh_control_point_j[point_num]; + + if (x) + *x = patch->points[i][j].x; + if (y) + *y = patch->points[i][j].y; + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_mesh_pattern_get_control_point); + void _cairo_pattern_reset_static_data (void) { -#if HAS_FREED_POOL int i; for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) _freed_pool_reset (&freed_pattern_pool[i]); -#endif - - _cairo_pattern_reset_solid_surface_cache (); +} + +static void +_cairo_debug_print_surface_pattern (FILE *file, + const cairo_surface_pattern_t *pattern) +{ + const char *s; + switch (pattern->surface->type) { + case CAIRO_SURFACE_TYPE_IMAGE: s = "image"; break; + case CAIRO_SURFACE_TYPE_PDF: s = "pdf"; break; + case CAIRO_SURFACE_TYPE_PS: s = "ps"; break; + case CAIRO_SURFACE_TYPE_XLIB: s = "xlib"; break; + case CAIRO_SURFACE_TYPE_XCB: s = "xcb"; break; + case CAIRO_SURFACE_TYPE_GLITZ: s = "glitz"; break; + case CAIRO_SURFACE_TYPE_QUARTZ: s = "quartz"; break; + case CAIRO_SURFACE_TYPE_WIN32: s = "win32"; break; + case CAIRO_SURFACE_TYPE_BEOS: s = "beos"; break; + case CAIRO_SURFACE_TYPE_DIRECTFB: s = "directfb"; break; + case CAIRO_SURFACE_TYPE_SVG: s = "svg"; break; + case CAIRO_SURFACE_TYPE_OS2: s = "os2"; break; + case CAIRO_SURFACE_TYPE_WIN32_PRINTING: s = "win32_printing"; break; + case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: s = "quartz_image"; break; + case CAIRO_SURFACE_TYPE_SCRIPT: s = "script"; break; + case CAIRO_SURFACE_TYPE_QT: s = "qt"; break; + case CAIRO_SURFACE_TYPE_RECORDING: s = "recording"; break; + case CAIRO_SURFACE_TYPE_VG: s = "vg"; break; + case CAIRO_SURFACE_TYPE_GL: s = "gl"; break; + case CAIRO_SURFACE_TYPE_DRM: s = "drm"; break; + case CAIRO_SURFACE_TYPE_TEE: s = "tee"; break; + case CAIRO_SURFACE_TYPE_XML: s = "xml"; break; + case CAIRO_SURFACE_TYPE_SKIA: s = "skia"; break; /* Deprecated */ + case CAIRO_SURFACE_TYPE_SUBSURFACE: s = "subsurface"; break; + case CAIRO_SURFACE_TYPE_COGL: s = "cogl"; break; + default: s = "invalid"; ASSERT_NOT_REACHED; break; + } + fprintf (file, " surface type: %s\n", s); +} + +static void +_cairo_debug_print_raster_source_pattern (FILE *file, + const cairo_raster_source_pattern_t *raster) +{ + fprintf (file, " content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height); +} + +static void +_cairo_debug_print_linear_pattern (FILE *file, + const cairo_linear_pattern_t *pattern) +{ +} + +static void +_cairo_debug_print_radial_pattern (FILE *file, + const cairo_radial_pattern_t *pattern) +{ +} + +static void +_cairo_debug_print_mesh_pattern (FILE *file, + const cairo_mesh_pattern_t *pattern) +{ +} + +void +_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern) +{ + const char *s; + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: s = "solid"; break; + case CAIRO_PATTERN_TYPE_SURFACE: s = "surface"; break; + case CAIRO_PATTERN_TYPE_LINEAR: s = "linear"; break; + case CAIRO_PATTERN_TYPE_RADIAL: s = "radial"; break; + case CAIRO_PATTERN_TYPE_MESH: s = "mesh"; break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: s = "raster"; break; + default: s = "invalid"; ASSERT_NOT_REACHED; break; + } + + fprintf (file, "pattern: %s\n", s); + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return; + + switch (pattern->extend) { + case CAIRO_EXTEND_NONE: s = "none"; break; + case CAIRO_EXTEND_REPEAT: s = "repeat"; break; + case CAIRO_EXTEND_REFLECT: s = "reflect"; break; + case CAIRO_EXTEND_PAD: s = "pad"; break; + default: s = "invalid"; ASSERT_NOT_REACHED; break; + } + fprintf (file, " extend: %s\n", s); + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: s = "fast"; break; + case CAIRO_FILTER_GOOD: s = "good"; break; + case CAIRO_FILTER_BEST: s = "best"; break; + case CAIRO_FILTER_NEAREST: s = "nearest"; break; + case CAIRO_FILTER_BILINEAR: s = "bilinear"; break; + case CAIRO_FILTER_GAUSSIAN: s = "gaussian"; break; + default: s = "invalid"; ASSERT_NOT_REACHED; break; + } + fprintf (file, " filter: %s\n", s); + fprintf (file, " matrix: [%g %g %g %g %g %g]\n", + pattern->matrix.xx, pattern->matrix.yx, + pattern->matrix.xy, pattern->matrix.yy, + pattern->matrix.x0, pattern->matrix.y0); + switch (pattern->type) { + default: + case CAIRO_PATTERN_TYPE_SOLID: + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + _cairo_debug_print_raster_source_pattern (file, (cairo_raster_source_pattern_t *)pattern); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + _cairo_debug_print_surface_pattern (file, (cairo_surface_pattern_t *)pattern); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + _cairo_debug_print_linear_pattern (file, (cairo_linear_pattern_t *)pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + _cairo_debug_print_radial_pattern (file, (cairo_radial_pattern_t *)pattern); + break; + case CAIRO_PATTERN_TYPE_MESH: + _cairo_debug_print_mesh_pattern (file, (cairo_mesh_pattern_t *)pattern); + break; + } } diff --git a/gfx/cairo/cairo/src/cairo-pdf-interchange.c b/gfx/cairo/cairo/src/cairo-pdf-interchange.c new file mode 100644 index 000000000000..434486cc9f15 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-interchange.c @@ -0,0 +1,1736 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + */ + + +/* PDF Document Interchange features: + * - metadata + * - document outline + * - tagged pdf + * - hyperlinks + * - page labels + */ + +#define _DEFAULT_SOURCE /* for localtime_r(), gmtime_r(), snprintf(), strdup() */ +#include "cairoint.h" + +#include "cairo-pdf.h" +#include "cairo-pdf-surface-private.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +#include + +#ifndef HAVE_LOCALTIME_R +#define localtime_r(T, BUF) (*(BUF) = *localtime (T)) +#endif +#ifndef HAVE_GMTIME_R +#define gmtime_r(T, BUF) (*(BUF) = *gmtime (T)) +#endif + +static void +write_rect_to_pdf_quad_points (cairo_output_stream_t *stream, + const cairo_rectangle_t *rect, + double surface_height) +{ + _cairo_output_stream_printf (stream, + "%f %f %f %f %f %f %f %f", + rect->x, + surface_height - rect->y, + rect->x + rect->width, + surface_height - rect->y, + rect->x + rect->width, + surface_height - (rect->y + rect->height), + rect->x, + surface_height - (rect->y + rect->height)); +} + +static void +write_rect_int_to_pdf_bbox (cairo_output_stream_t *stream, + const cairo_rectangle_int_t *rect, + double surface_height) +{ + _cairo_output_stream_printf (stream, + "%d %f %d %f", + rect->x, + surface_height - (rect->y + rect->height), + rect->x + rect->width, + surface_height - rect->y); +} + +static cairo_int_status_t +add_tree_node (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *parent, + const char *name, + cairo_pdf_struct_tree_node_t **new_node) +{ + cairo_pdf_struct_tree_node_t *node; + + node = _cairo_malloc (sizeof(cairo_pdf_struct_tree_node_t)); + if (unlikely (node == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + node->name = strdup (name); + node->res = _cairo_pdf_surface_new_object (surface); + if (node->res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + node->parent = parent; + cairo_list_init (&node->children); + _cairo_array_init (&node->mcid, sizeof(struct page_mcid)); + node->annot_res.id = 0; + node->extents.valid = FALSE; + cairo_list_init (&node->extents.link); + + cairo_list_add_tail (&node->link, &parent->children); + + *new_node = node; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +is_leaf_node (cairo_pdf_struct_tree_node_t *node) +{ + return node->parent && cairo_list_is_empty (&node->children) ; +} + +static void +free_node (cairo_pdf_struct_tree_node_t *node) +{ + cairo_pdf_struct_tree_node_t *child, *next; + + if (!node) + return; + + cairo_list_foreach_entry_safe (child, next, cairo_pdf_struct_tree_node_t, + &node->children, link) + { + cairo_list_del (&child->link); + free_node (child); + } + free (node->name); + _cairo_array_fini (&node->mcid); + free (node); +} + +static cairo_status_t +add_mcid_to_node (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + int page, + int *mcid) +{ + struct page_mcid mcid_elem; + cairo_int_status_t status; + cairo_pdf_interchange_t *ic = &surface->interchange; + + status = _cairo_array_append (&ic->mcid_to_tree, &node); + if (unlikely (status)) + return status; + + mcid_elem.page = page; + mcid_elem.mcid = _cairo_array_num_elements (&ic->mcid_to_tree) - 1; + *mcid = mcid_elem.mcid; + return _cairo_array_append (&node->mcid, &mcid_elem); +} + +static cairo_int_status_t +add_annotation (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + const char *name, + const char *attributes) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_annotation_t *annot; + + annot = malloc (sizeof(cairo_pdf_annotation_t)); + if (unlikely (annot == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_tag_parse_link_attributes (attributes, &annot->link_attrs); + if (unlikely (status)) { + free (annot); + return status; + } + + annot->node = node; + + status = _cairo_array_append (&ic->annots, &annot); + + return status; +} + +static void +free_annotation (cairo_pdf_annotation_t *annot) +{ + _cairo_array_fini (&annot->link_attrs.rects); + free (annot->link_attrs.dest); + free (annot->link_attrs.uri); + free (annot->link_attrs.file); + free (annot); +} + +static void +cairo_pdf_interchange_clear_annotations (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + int num_elems, i; + + num_elems = _cairo_array_num_elements (&ic->annots); + for (i = 0; i < num_elems; i++) { + cairo_pdf_annotation_t * annot; + + _cairo_array_copy_element (&ic->annots, i, &annot); + free_annotation (annot); + } + _cairo_array_truncate (&ic->annots, 0); +} + +static cairo_int_status_t +cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node) +{ + struct page_mcid *mcid_elem; + int i, num_mcid, first_page; + cairo_pdf_resource_t *page_res; + cairo_pdf_struct_tree_node_t *child; + + _cairo_pdf_surface_update_object (surface, node->res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /StructElem\n" + " /S /%s\n" + " /P %d 0 R\n", + node->res.id, + node->name, + node->parent->res.id); + + if (! cairo_list_is_empty (&node->children)) { + if (cairo_list_is_singular (&node->children) && node->annot_res.id == 0) { + child = cairo_list_first_entry (&node->children, cairo_pdf_struct_tree_node_t, link); + _cairo_output_stream_printf (surface->output, " /K %d 0 R\n", child->res.id); + } else { + _cairo_output_stream_printf (surface->output, " /K [ "); + if (node->annot_res.id != 0) { + _cairo_output_stream_printf (surface->output, + "<< /Type /OBJR /Obj %d 0 R >> ", + node->annot_res.id); + } + cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, + &node->children, link) + { + _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id); + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + } else { + num_mcid = _cairo_array_num_elements (&node->mcid); + if (num_mcid > 0 ) { + mcid_elem = _cairo_array_index (&node->mcid, 0); + first_page = mcid_elem->page; + page_res = _cairo_array_index (&surface->pages, first_page - 1); + _cairo_output_stream_printf (surface->output, " /Pg %d 0 R\n", page_res->id); + + if (num_mcid == 1 && node->annot_res.id == 0) { + _cairo_output_stream_printf (surface->output, " /K %d\n", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->output, " /K [ "); + if (node->annot_res.id != 0) { + _cairo_output_stream_printf (surface->output, + "<< /Type /OBJR /Obj %d 0 R >> ", + node->annot_res.id); + } + for (i = 0; i < num_mcid; i++) { + mcid_elem = _cairo_array_index (&node->mcid, i); + page_res = _cairo_array_index (&surface->pages, mcid_elem->page - 1); + if (mcid_elem->page == first_page) { + _cairo_output_stream_printf (surface->output, "%d ", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->output, + "\n << /Type /MCR /Pg %d 0 R /MCID %d >> ", + page_res->id, + mcid_elem->mcid); + } + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + } + } + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + return _cairo_output_stream_get_status (surface->output); +} + +static void +init_named_dest_key (cairo_pdf_named_dest_t *dest) +{ + dest->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + dest->attrs.name, + strlen (dest->attrs.name)); +} + +static cairo_bool_t +_named_dest_equal (const void *key_a, const void *key_b) +{ + const cairo_pdf_named_dest_t *a = key_a; + const cairo_pdf_named_dest_t *b = key_b; + + return strcmp (a->attrs.name, b->attrs.name) == 0; +} + +static void +_named_dest_pluck (void *entry, void *closure) +{ + cairo_pdf_named_dest_t *dest = entry; + cairo_hash_table_t *table = closure; + + _cairo_hash_table_remove (table, &dest->base); + free (dest->attrs.name); + free (dest); +} + +static cairo_int_status_t +cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface, + int page, + cairo_bool_t has_pos, + double x, + double y) +{ + cairo_pdf_resource_t res; + double height; + + if (page < 1 || page > (int)_cairo_array_num_elements (&surface->pages)) + return CAIRO_INT_STATUS_TAG_ERROR; + + _cairo_array_copy_element (&surface->page_heights, page - 1, &height); + _cairo_array_copy_element (&surface->pages, page - 1, &res); + if (has_pos) { + _cairo_output_stream_printf (surface->output, + " /Dest [%d 0 R /XYZ %f %f 0]\n", + res.id, + x, + height - y); + } else { + _cairo_output_stream_printf (surface->output, + " /Dest [%d 0 R /XYZ null null 0]\n", + res.id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface, + cairo_link_attrs_t *link_attrs) +{ + cairo_int_status_t status; + cairo_pdf_interchange_t *ic = &surface->interchange; + char *dest = NULL; + + if (link_attrs->dest) { + cairo_pdf_named_dest_t key; + cairo_pdf_named_dest_t *named_dest; + + /* check if this is a link to an internal named dest */ + key.attrs.name = link_attrs->dest; + init_named_dest_key (&key); + named_dest = _cairo_hash_table_lookup (ic->named_dests, &key.base); + if (named_dest && named_dest->attrs.internal) { + /* if dests exists and has internal attribute, use a direct + * reference instead of the name */ + double x = 0; + double y = 0; + + if (named_dest->extents.valid) { + x = named_dest->extents.extents.x; + y = named_dest->extents.extents.y; + } + + if (named_dest->attrs.x_valid) + x = named_dest->attrs.x; + + if (named_dest->attrs.y_valid) + y = named_dest->attrs.y; + + status = cairo_pdf_interchange_write_explicit_dest (surface, + named_dest->page, + TRUE, + x, y); + return status; + } + } + + if (link_attrs->dest) { + status = _cairo_utf8_to_pdf_string (link_attrs->dest, &dest); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + " /Dest %s\n", + dest); + free (dest); + } else { + status = cairo_pdf_interchange_write_explicit_dest (surface, + link_attrs->page, + link_attrs->has_pos, + link_attrs->pos.x, + link_attrs->pos.y); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface, + cairo_link_attrs_t *link_attrs) +{ + cairo_int_status_t status; + char *dest = NULL; + + if (link_attrs->link_type == TAG_LINK_DEST) { + status = cairo_pdf_interchange_write_dest (surface, link_attrs); + if (unlikely (status)) + return status; + + } else if (link_attrs->link_type == TAG_LINK_URI) { + _cairo_output_stream_printf (surface->output, + " /A <<\n" + " /Type /Action\n" + " /S /URI\n" + " /URI (%s)\n" + " >>\n", + link_attrs->uri); + } else if (link_attrs->link_type == TAG_LINK_FILE) { + _cairo_output_stream_printf (surface->output, + " /A <<\n" + " /Type /Action\n" + " /S /GoToR\n" + " /F (%s)\n", + link_attrs->file); + if (link_attrs->dest) { + status = _cairo_utf8_to_pdf_string (link_attrs->dest, &dest); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + " /D %s\n", + dest); + free (dest); + } else { + if (link_attrs->has_pos) { + _cairo_output_stream_printf (surface->output, + " /D [%d %f %f 0]\n", + link_attrs->page, + link_attrs->pos.x, + link_attrs->pos.y); + } else { + _cairo_output_stream_printf (surface->output, + " /D [%d null null 0]\n", + link_attrs->page); + } + } + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface, + cairo_pdf_annotation_t *annot) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_struct_tree_node_t *node = annot->node; + int sp; + int i, num_rects; + double height; + + num_rects = _cairo_array_num_elements (&annot->link_attrs.rects); + if (strcmp (node->name, CAIRO_TAG_LINK) == 0 && + annot->link_attrs.link_type != TAG_LINK_EMPTY && + (node->extents.valid || num_rects > 0)) + { + status = _cairo_array_append (&ic->parent_tree, &node->res); + if (unlikely (status)) + return status; + + sp = _cairo_array_num_elements (&ic->parent_tree) - 1; + + node->annot_res = _cairo_pdf_surface_new_object (surface); + + status = _cairo_array_append (&surface->page_annots, &node->annot_res); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_update_object (surface, node->annot_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Annot\n" + " /Subtype /Link\n" + " /StructParent %d\n", + node->annot_res.id, + sp); + + height = surface->height; + if (num_rects > 0) { + cairo_rectangle_int_t bbox_rect; + + _cairo_output_stream_printf (surface->output, + " /QuadPoints [ "); + for (i = 0; i < num_rects; i++) { + cairo_rectangle_t rectf; + cairo_rectangle_int_t recti; + + _cairo_array_copy_element (&annot->link_attrs.rects, i, &rectf); + _cairo_rectangle_int_from_double (&recti, &rectf); + if (i == 0) + bbox_rect = recti; + else + _cairo_rectangle_union (&bbox_rect, &recti); + + write_rect_to_pdf_quad_points (surface->output, &rectf, height); + _cairo_output_stream_printf (surface->output, " "); + } + _cairo_output_stream_printf (surface->output, + "]\n" + " /Rect [ "); + write_rect_int_to_pdf_bbox (surface->output, &bbox_rect, height); + _cairo_output_stream_printf (surface->output, " ]\n"); + } else { + _cairo_output_stream_printf (surface->output, + " /Rect [ "); + write_rect_int_to_pdf_bbox (surface->output, &node->extents.extents, height); + _cairo_output_stream_printf (surface->output, " ]\n"); + } + + status = cairo_pdf_interchange_write_link_action (surface, &annot->link_attrs); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + " /BS << /W 0 >>" + ">>\n" + "endobj\n"); + + status = _cairo_output_stream_get_status (surface->output); + } + + return status; +} + +static cairo_int_status_t +cairo_pdf_interchange_walk_struct_tree (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node, + cairo_int_status_t (*func) (cairo_pdf_surface_t *surface, + cairo_pdf_struct_tree_node_t *node)) +{ + cairo_int_status_t status; + cairo_pdf_struct_tree_node_t *child; + + if (node->parent) { + status = func (surface, node); + if (unlikely (status)) + return status; + } + + cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, + &node->children, link) + { + status = cairo_pdf_interchange_walk_struct_tree (surface, child, func); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_struct_tree_node_t *child; + + if (cairo_list_is_empty (&ic->struct_root->children)) + return CAIRO_STATUS_SUCCESS; + + surface->struct_tree_root = _cairo_pdf_surface_new_object (surface); + ic->struct_root->res = surface->struct_tree_root; + + cairo_pdf_interchange_walk_struct_tree (surface, ic->struct_root, cairo_pdf_interchange_write_node_object); + + child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link); + _cairo_pdf_surface_update_object (surface, surface->struct_tree_root); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /StructTreeRoot\n" + " /ParentTree %d 0 R\n", + surface->struct_tree_root.id, + ic->parent_tree_res.id); + + if (cairo_list_is_singular (&ic->struct_root->children)) { + child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link); + _cairo_output_stream_printf (surface->output, " /K [ %d 0 R ]\n", child->res.id); + } else { + _cairo_output_stream_printf (surface->output, " /K [ "); + + cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, + &ic->struct_root->children, link) + { + _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id); + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_page_annots (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + int num_elems, i; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + num_elems = _cairo_array_num_elements (&ic->annots); + for (i = 0; i < num_elems; i++) { + cairo_pdf_annotation_t * annot; + + _cairo_array_copy_element (&ic->annots, i, &annot); + status = cairo_pdf_interchange_write_annot (surface, annot); + if (unlikely (status)) + return status; + } + + return status; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_page_parent_elems (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_struct_tree_node_t *node; + cairo_pdf_resource_t res; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + surface->page_parent_tree = -1; + num_elems = _cairo_array_num_elements (&ic->mcid_to_tree); + if (num_elems > 0) { + res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "[\n", + res.id); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&ic->mcid_to_tree, i, &node); + _cairo_output_stream_printf (surface->output, " %d 0 R\n", node->res.id); + } + _cairo_output_stream_printf (surface->output, + "]\n" + "endobj\n"); + status = _cairo_array_append (&ic->parent_tree, &res); + surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1; + } + + return status; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_resource_t *res; + cairo_pdf_interchange_t *ic = &surface->interchange; + + num_elems = _cairo_array_num_elements (&ic->parent_tree); + if (num_elems > 0) { + ic->parent_tree_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Nums [\n", + ic->parent_tree_res.id); + for (i = 0; i < num_elems; i++) { + res = _cairo_array_index (&ic->parent_tree, i); + if (res->id) { + _cairo_output_stream_printf (surface->output, + " %d %d 0 R\n", + i, + res->id); + } + } + _cairo_output_stream_printf (surface->output, + " ]\n" + ">>\n" + "endobj\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_outline_entry_t *outline; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status; + char *name = NULL; + + num_elems = _cairo_array_num_elements (&ic->outline); + if (num_elems < 2) + return CAIRO_INT_STATUS_SUCCESS; + + _cairo_array_copy_element (&ic->outline, 0, &outline); + outline->res = _cairo_pdf_surface_new_object (surface); + surface->outlines_dict_res = outline->res; + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Outlines\n" + " /First %d 0 R\n" + " /Last %d 0 R\n" + " /Count %d\n" + ">>\n" + "endobj\n", + outline->res.id, + outline->first_child->res.id, + outline->last_child->res.id, + outline->count); + + for (i = 1; i < num_elems; i++) { + _cairo_array_copy_element (&ic->outline, i, &outline); + _cairo_pdf_surface_update_object (surface, outline->res); + + status = _cairo_utf8_to_pdf_string (outline->name, &name); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Title %s\n" + " /Parent %d 0 R\n", + outline->res.id, + name, + outline->parent->res.id); + free (name); + + if (outline->prev) { + _cairo_output_stream_printf (surface->output, + " /Prev %d 0 R\n", + outline->prev->res.id); + } + + if (outline->next) { + _cairo_output_stream_printf (surface->output, + " /Next %d 0 R\n", + outline->next->res.id); + } + + if (outline->first_child) { + _cairo_output_stream_printf (surface->output, + " /First %d 0 R\n" + " /Last %d 0 R\n" + " /Count %d\n", + outline->first_child->res.id, + outline->last_child->res.id, + outline->count); + } + + if (outline->flags) { + int flags = 0; + if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_ITALIC) + flags |= 1; + if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_BOLD) + flags |= 2; + _cairo_output_stream_printf (surface->output, + " /F %d\n", + flags); + } + + status = cairo_pdf_interchange_write_link_action (surface, &outline->link_attrs); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } + + return status; +} + +/* + * Split a page label into a text prefix and numeric suffix. Leading '0's are + * included in the prefix. eg + * "3" => NULL, 3 + * "cover" => "cover", 0 + * "A-2" => "A-", 2 + * "A-002" => "A-00", 2 + */ +static char * +split_label (const char* label, int *num) +{ + int len, i; + + *num = 0; + len = strlen (label); + if (len == 0) + return NULL; + + i = len; + while (i > 0 && _cairo_isdigit (label[i-1])) + i--; + + while (i < len && label[i] == '0') + i++; + + if (i < len) + sscanf (label + i, "%d", num); + + if (i > 0) { + char *s; + s = _cairo_malloc (i + 1); + if (!s) + return NULL; + + memcpy (s, label, i); + s[i] = 0; + return s; + } + + return NULL; +} + +/* strcmp that handles NULL arguments */ +static cairo_bool_t +strcmp_null (const char *s1, const char *s2) +{ + if (s1 && s2) + return strcmp (s1, s2) == 0; + + if (!s1 && !s2) + return TRUE; + + return FALSE; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + char *label; + char *prefix; + char *prev_prefix; + int num, prev_num; + cairo_int_status_t status; + cairo_bool_t has_labels; + + /* Check if any labels defined */ + num_elems = _cairo_array_num_elements (&surface->page_labels); + has_labels = FALSE; + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->page_labels, i, &label); + if (label) { + has_labels = TRUE; + break; + } + } + + if (!has_labels) + return CAIRO_STATUS_SUCCESS; + + surface->page_labels_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Nums [\n", + surface->page_labels_res.id); + prefix = NULL; + prev_prefix = NULL; + num = 0; + prev_num = 0; + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->page_labels, i, &label); + if (label) { + prefix = split_label (label, &num); + } else { + prefix = NULL; + num = i + 1; + } + + if (!strcmp_null (prefix, prev_prefix) || num != prev_num + 1) { + _cairo_output_stream_printf (surface->output, " %d << ", i); + + if (num) + _cairo_output_stream_printf (surface->output, "/S /D /St %d ", num); + + if (prefix) { + char *s; + status = _cairo_utf8_to_pdf_string (prefix, &s); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "/P %s ", s); + free (s); + } + + _cairo_output_stream_printf (surface->output, ">>\n"); + } + free (prev_prefix); + prev_prefix = prefix; + prefix = NULL; + prev_num = num; + } + free (prefix); + free (prev_prefix); + _cairo_output_stream_printf (surface->output, + " ]\n" + ">>\n" + "endobj\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_collect_dest (void *entry, void *closure) +{ + cairo_pdf_named_dest_t *dest = entry; + cairo_pdf_surface_t *surface = closure; + cairo_pdf_interchange_t *ic = &surface->interchange; + + ic->sorted_dests[ic->num_dests++] = dest; +} + +static int +_dest_compare (const void *a, const void *b) +{ + const cairo_pdf_named_dest_t * const *dest_a = a; + const cairo_pdf_named_dest_t * const *dest_b = b; + + return strcmp ((*dest_a)->attrs.name, (*dest_b)->attrs.name); +} + +static cairo_int_status_t +_cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface) +{ + int i; + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (ic->num_dests == 0) { + ic->dests_res.id = 0; + return CAIRO_STATUS_SUCCESS; + } + + ic->sorted_dests = calloc (ic->num_dests, sizeof (cairo_pdf_named_dest_t *)); + if (unlikely (ic->sorted_dests == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ic->num_dests = 0; + _cairo_hash_table_foreach (ic->named_dests, _collect_dest, surface); + qsort (ic->sorted_dests, ic->num_dests, sizeof (cairo_pdf_named_dest_t *), _dest_compare); + + ic->dests_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Names [\n", + ic->dests_res.id); + for (i = 0; i < ic->num_dests; i++) { + cairo_pdf_named_dest_t *dest = ic->sorted_dests[i]; + cairo_pdf_resource_t page_res; + double x = 0; + double y = 0; + double height; + + if (dest->attrs.internal) + continue; + + if (dest->extents.valid) { + x = dest->extents.extents.x; + y = dest->extents.extents.y; + } + + if (dest->attrs.x_valid) + x = dest->attrs.x; + + if (dest->attrs.y_valid) + y = dest->attrs.y; + + _cairo_array_copy_element (&surface->pages, dest->page - 1, &page_res); + _cairo_array_copy_element (&surface->page_heights, dest->page - 1, &height); + _cairo_output_stream_printf (surface->output, + " (%s) [%d 0 R /XYZ %f %f 0]\n", + dest->attrs.name, + page_res.id, + x, + height - y); + } + _cairo_output_stream_printf (surface->output, + " ]\n" + ">>\n" + "endobj\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_names_dict (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status; + + status = _cairo_pdf_interchange_write_document_dests (surface); + if (unlikely (status)) + return status; + + surface->names_dict_res.id = 0; + if (ic->dests_res.id != 0) { + surface->names_dict_res = _cairo_pdf_surface_new_object (surface); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Dests %d 0 R >>\n" + "endobj\n", + surface->names_dict_res.id, + ic->dests_res.id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + surface->docinfo_res = _cairo_pdf_surface_new_object (surface); + if (surface->docinfo_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Producer (cairo %s (https://cairographics.org))\n", + surface->docinfo_res.id, + cairo_version_string ()); + + if (ic->docinfo.title) + _cairo_output_stream_printf (surface->output, " /Title %s\n", ic->docinfo.title); + + if (ic->docinfo.author) + _cairo_output_stream_printf (surface->output, " /Author %s\n", ic->docinfo.author); + + if (ic->docinfo.subject) + _cairo_output_stream_printf (surface->output, " /Subject %s\n", ic->docinfo.subject); + + if (ic->docinfo.keywords) + _cairo_output_stream_printf (surface->output, " /Keywords %s\n", ic->docinfo.keywords); + + if (ic->docinfo.creator) + _cairo_output_stream_printf (surface->output, " /Creator %s\n", ic->docinfo.creator); + + if (ic->docinfo.create_date) + _cairo_output_stream_printf (surface->output, " /CreationDate %s\n", ic->docinfo.create_date); + + if (ic->docinfo.mod_date) + _cairo_output_stream_printf (surface->output, " /ModDate %s\n", ic->docinfo.mod_date); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_interchange_begin_structure_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + const char *name, + const char *attributes) +{ + int page_num, mcid; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = add_tree_node (surface, ic->current_node, name, &ic->current_node); + if (unlikely (status)) + return status; + + _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, ic->current_node); + + if (tag_type & TAG_TYPE_LINK) { + status = add_annotation (surface, ic->current_node, name, attributes); + if (unlikely (status)) + return status; + + cairo_list_add_tail (&ic->current_node->extents.link, &ic->extents_list); + } + + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + ic->current_node = _cairo_tag_stack_top_elem (&ic->render_tag_stack)->data; + assert (ic->current_node != NULL); + if (is_leaf_node (ic->current_node)) { + page_num = _cairo_array_num_elements (&surface->pages); + add_mcid_to_node (surface, ic->current_node, page_num, &mcid); + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, name, mcid); + } + } + + return status; +} + +static cairo_int_status_t +_cairo_pdf_interchange_begin_dest_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + const char *name, + const char *attributes) +{ + cairo_pdf_named_dest_t *dest; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + dest = calloc (1, sizeof (cairo_pdf_named_dest_t)); + if (unlikely (dest == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_tag_parse_dest_attributes (attributes, &dest->attrs); + if (unlikely (status)) + { + free (dest); + return status; + } + + dest->page = _cairo_array_num_elements (&surface->pages); + init_named_dest_key (dest); + status = _cairo_hash_table_insert (ic->named_dests, &dest->base); + if (unlikely (status)) + { + free (dest->attrs.name); + free (dest); + return status; + } + + _cairo_tag_stack_set_top_data (&ic->analysis_tag_stack, dest); + cairo_list_add_tail (&dest->extents.link, &ic->extents_list); + ic->num_dests++; + } + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface, + const char *name, + const char *attributes) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_tag_type_t tag_type; + cairo_pdf_interchange_t *ic = &surface->interchange; + void *ptr; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_tag_stack_push (&ic->analysis_tag_stack, name, attributes); + + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + status = _cairo_tag_stack_push (&ic->render_tag_stack, name, attributes); + _cairo_array_copy_element (&ic->push_data, ic->push_data_index++, &ptr); + _cairo_tag_stack_set_top_data (&ic->render_tag_stack, ptr); + } + + if (unlikely (status)) + return status; + + tag_type = _cairo_tag_get_type (name); + if (tag_type & TAG_TYPE_STRUCTURE) { + status = _cairo_pdf_interchange_begin_structure_tag (surface, tag_type, name, attributes); + if (unlikely (status)) + return status; + } + + if (tag_type & TAG_TYPE_DEST) { + status = _cairo_pdf_interchange_begin_dest_tag (surface, tag_type, name, attributes); + if (unlikely (status)) + return status; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + ptr = _cairo_tag_stack_top_elem (&ic->analysis_tag_stack)->data; + status = _cairo_array_append (&ic->push_data, &ptr); + } + + return status; +} + +static cairo_int_status_t +_cairo_pdf_interchange_end_structure_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + cairo_tag_stack_elem_t *elem) +{ + const cairo_pdf_struct_tree_node_t *node; + struct tag_extents *tag, *next; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + assert (elem->data != NULL); + node = elem->data; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + if (tag_type & TAG_TYPE_LINK) { + cairo_list_foreach_entry_safe (tag, next, struct tag_extents, + &ic->extents_list, link) { + if (tag == &node->extents) { + cairo_list_del (&tag->link); + break; + } + } + } + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + if (is_leaf_node (ic->current_node)) { + status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + if (unlikely (status)) + return status; + } + } + + ic->current_node = ic->current_node->parent; + assert (ic->current_node != NULL); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_interchange_end_dest_tag (cairo_pdf_surface_t *surface, + cairo_tag_type_t tag_type, + cairo_tag_stack_elem_t *elem) +{ + struct tag_extents *tag, *next; + cairo_pdf_named_dest_t *dest; + cairo_pdf_interchange_t *ic = &surface->interchange; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + assert (elem->data != NULL); + dest = (cairo_pdf_named_dest_t *) elem->data; + cairo_list_foreach_entry_safe (tag, next, struct tag_extents, + &ic->extents_list, link) { + if (tag == &dest->extents) { + cairo_list_del (&tag->link); + break; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface, + const char *name) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_tag_type_t tag_type; + cairo_tag_stack_elem_t *elem; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + status = _cairo_tag_stack_pop (&ic->analysis_tag_stack, name, &elem); + else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) + status = _cairo_tag_stack_pop (&ic->render_tag_stack, name, &elem); + + if (unlikely (status)) + return status; + + tag_type = _cairo_tag_get_type (name); + if (tag_type & TAG_TYPE_STRUCTURE) { + status = _cairo_pdf_interchange_end_structure_tag (surface, tag_type, elem); + if (unlikely (status)) + goto cleanup; + } + + if (tag_type & TAG_TYPE_DEST) { + status = _cairo_pdf_interchange_end_dest_tag (surface, tag_type, elem); + if (unlikely (status)) + goto cleanup; + } + + cleanup: + _cairo_tag_stack_free_elem (elem); + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + struct tag_extents *tag; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + cairo_list_foreach_entry (tag, struct tag_extents, &ic->extents_list, link) { + if (tag->valid) { + _cairo_rectangle_union (&tag->extents, extents); + } else { + tag->extents = *extents; + tag->valid = TRUE; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + int page_num, mcid; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + _cairo_array_truncate (&ic->mcid_to_tree, 0); + _cairo_array_truncate (&ic->push_data, 0); + ic->begin_page_node = ic->current_node; + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + ic->push_data_index = 0; + ic->current_node = ic->begin_page_node; + if (ic->end_page_node && is_leaf_node (ic->end_page_node)) { + page_num = _cairo_array_num_elements (&surface->pages); + add_mcid_to_node (surface, ic->end_page_node, page_num, &mcid); + status = _cairo_pdf_operators_tag_begin (&surface->pdf_operators, + ic->end_page_node->name, + mcid); + } + } + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + ic->end_page_node = ic->current_node; + if (is_leaf_node (ic->current_node)) + status = _cairo_pdf_operators_tag_end (&surface->pdf_operators); + } + + return status; +} + +cairo_int_status_t +_cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + + status = cairo_pdf_interchange_write_page_annots (surface); + if (unlikely (status)) + return status; + + cairo_pdf_interchange_clear_annotations (surface); + + return cairo_pdf_interchange_write_page_parent_elems (surface); +} + +cairo_int_status_t +_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_tag_stack_structure_type_t tag_type; + + tag_type = _cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack); + if (tag_type == TAG_TREE_TYPE_TAGGED || tag_type == TAG_TREE_TYPE_STRUCTURE) { + + status = cairo_pdf_interchange_write_parent_tree (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_struct_tree (surface); + if (unlikely (status)) + return status; + + if (tag_type == TAG_TREE_TYPE_TAGGED) + surface->tagged = TRUE; + } + + status = cairo_pdf_interchange_write_outline (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_page_labels (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_names_dict (surface); + if (unlikely (status)) + return status; + + status = cairo_pdf_interchange_write_docinfo (surface); + + return status; +} + +static void +_cairo_pdf_interchange_set_create_date (cairo_pdf_surface_t *surface) +{ + time_t utc, local, offset; + struct tm tm_local, tm_utc; + char buf[50]; + int buf_size; + char *p; + cairo_pdf_interchange_t *ic = &surface->interchange; + + utc = time (NULL); + localtime_r (&utc, &tm_local); + strftime (buf, sizeof(buf), "(D:%Y%m%d%H%M%S", &tm_local); + + /* strftime "%z" is non standard and does not work on windows (it prints zone name, not offset). + * Calculate time zone offset by comparing local and utc time_t values for the same time. + */ + gmtime_r (&utc, &tm_utc); + tm_utc.tm_isdst = tm_local.tm_isdst; + local = mktime (&tm_utc); + offset = difftime (utc, local); + + if (offset == 0) { + strcat (buf, "Z"); + } else { + if (offset > 0) { + strcat (buf, "+"); + } else { + strcat (buf, "-"); + offset = -offset; + } + p = buf + strlen (buf); + buf_size = sizeof (buf) - strlen (buf); + snprintf (p, buf_size, "%02d'%02d", (int)(offset/3600), (int)(offset%3600)/60); + } + strcat (buf, ")"); + ic->docinfo.create_date = strdup (buf); +} + +cairo_int_status_t +_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_outline_entry_t *outline_root; + cairo_int_status_t status; + + _cairo_tag_stack_init (&ic->analysis_tag_stack); + _cairo_tag_stack_init (&ic->render_tag_stack); + _cairo_array_init (&ic->push_data, sizeof(void *)); + ic->struct_root = calloc (1, sizeof(cairo_pdf_struct_tree_node_t)); + if (unlikely (ic->struct_root == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_init (&ic->struct_root->children); + _cairo_array_init (&ic->struct_root->mcid, sizeof(struct page_mcid)); + ic->current_node = ic->struct_root; + ic->begin_page_node = NULL; + ic->end_page_node = NULL; + _cairo_array_init (&ic->parent_tree, sizeof(cairo_pdf_resource_t)); + _cairo_array_init (&ic->mcid_to_tree, sizeof(cairo_pdf_struct_tree_node_t *)); + _cairo_array_init (&ic->annots, sizeof(cairo_pdf_annotation_t *)); + ic->parent_tree_res.id = 0; + cairo_list_init (&ic->extents_list); + ic->named_dests = _cairo_hash_table_create (_named_dest_equal); + if (unlikely (ic->named_dests == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ic->num_dests = 0; + ic->sorted_dests = NULL; + ic->dests_res.id = 0; + + _cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *)); + outline_root = calloc (1, sizeof(cairo_pdf_outline_entry_t)); + if (unlikely (outline_root == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memset (&ic->docinfo, 0, sizeof (ic->docinfo)); + _cairo_pdf_interchange_set_create_date (surface); + status = _cairo_array_append (&ic->outline, &outline_root); + + return status; +} + +static void +_cairo_pdf_interchange_free_outlines (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + int num_elems, i; + + num_elems = _cairo_array_num_elements (&ic->outline); + for (i = 0; i < num_elems; i++) { + cairo_pdf_outline_entry_t *outline; + + _cairo_array_copy_element (&ic->outline, i, &outline); + free (outline->name); + free (outline->link_attrs.dest); + free (outline->link_attrs.uri); + free (outline->link_attrs.file); + free (outline); + } + _cairo_array_fini (&ic->outline); +} + +cairo_int_status_t +_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + + _cairo_tag_stack_fini (&ic->analysis_tag_stack); + _cairo_tag_stack_fini (&ic->render_tag_stack); + _cairo_array_fini (&ic->push_data); + free_node (ic->struct_root); + _cairo_array_fini (&ic->mcid_to_tree); + cairo_pdf_interchange_clear_annotations (surface); + _cairo_array_fini (&ic->annots); + _cairo_array_fini (&ic->parent_tree); + _cairo_hash_table_foreach (ic->named_dests, _named_dest_pluck, ic->named_dests); + _cairo_hash_table_destroy (ic->named_dests); + free (ic->sorted_dests); + _cairo_pdf_interchange_free_outlines (surface); + free (ic->docinfo.title); + free (ic->docinfo.author); + free (ic->docinfo.subject); + free (ic->docinfo.keywords); + free (ic->docinfo.creator); + free (ic->docinfo.create_date); + free (ic->docinfo.mod_date); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface, + int parent_id, + const char *name, + const char *link_attribs, + cairo_pdf_outline_flags_t flags, + int *id) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_pdf_outline_entry_t *outline; + cairo_pdf_outline_entry_t *parent; + cairo_int_status_t status; + + if (parent_id < 0 || parent_id >= (int)_cairo_array_num_elements (&ic->outline)) + return CAIRO_STATUS_SUCCESS; + + outline = _cairo_malloc (sizeof(cairo_pdf_outline_entry_t)); + if (unlikely (outline == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_tag_parse_link_attributes (link_attribs, &outline->link_attrs); + if (unlikely (status)) { + free (outline); + return status; + } + + outline->res = _cairo_pdf_surface_new_object (surface); + if (outline->res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + outline->name = strdup (name); + outline->flags = flags; + outline->count = 0; + + _cairo_array_copy_element (&ic->outline, parent_id, &parent); + + outline->parent = parent; + outline->first_child = NULL; + outline->last_child = NULL; + outline->next = NULL; + if (parent->last_child) { + parent->last_child->next = outline; + outline->prev = parent->last_child; + parent->last_child = outline; + } else { + parent->first_child = outline; + parent->last_child = outline; + outline->prev = NULL; + } + + *id = _cairo_array_num_elements (&ic->outline); + status = _cairo_array_append (&ic->outline, &outline); + if (unlikely (status)) + return status; + + /* Update Count */ + outline = outline->parent; + while (outline) { + if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_OPEN) { + outline->count++; + } else { + outline->count--; + break; + } + outline = outline->parent; + } + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Date must be in the following format: + * + * YYYY-MM-DDThh:mm:ss[Z+-]hh:mm + * + * Only the year is required. If a field is included all preceding + * fields must be included. + */ +static char * +iso8601_to_pdf_date_string (const char *iso) +{ + char buf[40]; + const char *p; + int i; + + /* Check that utf8 contains only the characters "0123456789-T:Z+" */ + p = iso; + while (*p) { + if (!_cairo_isdigit (*p) && *p != '-' && *p != 'T' && + *p != ':' && *p != 'Z' && *p != '+') + return NULL; + p++; + } + + p = iso; + strcpy (buf, "("); + + /* YYYY (required) */ + if (strlen (p) < 4) + return NULL; + + strncat (buf, p, 4); + p += 4; + + /* -MM, -DD, Thh, :mm, :ss */ + for (i = 0; i < 5; i++) { + if (strlen (p) < 3) + goto finish; + + strncat (buf, p + 1, 2); + p += 3; + } + + /* Z, +, - */ + if (strlen (p) < 1) + goto finish; + strncat (buf, p, 1); + p += 1; + + /* hh */ + if (strlen (p) < 2) + goto finish; + + strncat (buf, p, 2); + strcat (buf, "'"); + p += 2; + + /* :mm */ + if (strlen (p) < 3) + goto finish; + + strncat (buf, p + 1, 2); + strcat (buf, "'"); + + finish: + strcat (buf, ")"); + return strdup (buf); +} + +cairo_int_status_t +_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + cairo_status_t status; + char *s = NULL; + + if (utf8) { + if (metadata == CAIRO_PDF_METADATA_CREATE_DATE || + metadata == CAIRO_PDF_METADATA_MOD_DATE) { + s = iso8601_to_pdf_date_string (utf8); + } else { + status = _cairo_utf8_to_pdf_string (utf8, &s); + if (unlikely (status)) + return status; + } + } + + switch (metadata) { + case CAIRO_PDF_METADATA_TITLE: + free (ic->docinfo.title); + ic->docinfo.title = s; + break; + case CAIRO_PDF_METADATA_AUTHOR: + free (ic->docinfo.author); + ic->docinfo.author = s; + break; + case CAIRO_PDF_METADATA_SUBJECT: + free (ic->docinfo.subject); + ic->docinfo.subject = s; + break; + case CAIRO_PDF_METADATA_KEYWORDS: + free (ic->docinfo.keywords); + ic->docinfo.keywords = s; + break; + case CAIRO_PDF_METADATA_CREATOR: + free (ic->docinfo.creator); + ic->docinfo.creator = s; + break; + case CAIRO_PDF_METADATA_CREATE_DATE: + free (ic->docinfo.create_date); + ic->docinfo.create_date = s; + break; + case CAIRO_PDF_METADATA_MOD_DATE: + free (ic->docinfo.mod_date); + ic->docinfo.mod_date = s; + break; + } + + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/cairo-pdf-operators-private.h b/gfx/cairo/cairo/src/cairo-pdf-operators-private.h index 67d1cc2331b9..ed8be7f96e0f 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-operators-private.h +++ b/gfx/cairo/cairo/src/cairo-pdf-operators-private.h @@ -43,6 +43,7 @@ #define CAIRO_PDF_OPERATORS_H #include "cairo-compiler-private.h" +#include "cairo-error-private.h" #include "cairo-types-private.h" /* The glyph buffer size is based on the expected maximum glyphs in a @@ -52,9 +53,10 @@ */ #define PDF_GLYPH_BUFFER_SIZE 200 -typedef cairo_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id, - unsigned int subset_id, - void *closure); +typedef cairo_int_status_t +(*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id, + unsigned int subset_id, + void *closure); typedef struct _cairo_pdf_glyph { unsigned int glyph_index; @@ -68,6 +70,7 @@ typedef struct _cairo_pdf_operators { cairo_scaled_font_subsets_t *font_subsets; cairo_pdf_operators_use_font_subset_t use_font_subset; void *use_font_subset_closure; + cairo_bool_t ps_output; /* output is for PostScript */ cairo_bool_t use_actual_text; cairo_bool_t in_text_object; /* inside BT/ET pair */ @@ -81,6 +84,7 @@ typedef struct _cairo_pdf_operators { double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */ double cur_y; int hex_width; + cairo_bool_t is_latin; int num_glyphs; double glyph_buf_x_pos; cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE]; @@ -98,7 +102,8 @@ cairo_private void _cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream, cairo_matrix_t *cairo_to_pdf, - cairo_scaled_font_subsets_t *font_subsets); + cairo_scaled_font_subsets_t *font_subsets, + cairo_bool_t ps); cairo_private cairo_status_t _cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators); @@ -128,8 +133,8 @@ cairo_private void _cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators); cairo_private cairo_int_status_t -_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, +_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule); cairo_private cairo_int_status_t @@ -139,19 +144,19 @@ _cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, cairo_private cairo_int_status_t _cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse); cairo_private cairo_int_status_t -_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule); +_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule); cairo_private cairo_int_status_t _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, @@ -168,4 +173,12 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font); +cairo_private cairo_int_status_t +_cairo_pdf_operators_tag_begin (cairo_pdf_operators_t *pdf_operators, + const char *tag_name, + int mcid); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_tag_end (cairo_pdf_operators_t *pdf_operators); + #endif /* CAIRO_PDF_OPERATORS_H */ diff --git a/gfx/cairo/cairo/src/cairo-pdf-operators.c b/gfx/cairo/cairo/src/cairo-pdf-operators.c index d70756b3a8ff..328e893d7140 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-operators.c +++ b/gfx/cairo/cairo/src/cairo-pdf-operators.c @@ -57,11 +57,13 @@ void _cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators, cairo_output_stream_t *stream, cairo_matrix_t *cairo_to_pdf, - cairo_scaled_font_subsets_t *font_subsets) + cairo_scaled_font_subsets_t *font_subsets, + cairo_bool_t ps) { pdf_operators->stream = stream; pdf_operators->cairo_to_pdf = *cairo_to_pdf; pdf_operators->font_subsets = font_subsets; + pdf_operators->ps_output = ps; pdf_operators->use_font_subset = NULL; pdf_operators->use_font_subset_closure = NULL; pdf_operators->in_text_object = FALSE; @@ -138,7 +140,7 @@ _cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators) * assumptions will be made about the state. The next time a * particular graphics state is required (eg line width) the state * operator is always emitted and then remembered for subsequent - * operatations. + * operations. * * This should be called when starting a new stream or after emitting * the 'Q' operator (where pdf-operators functions were called inside @@ -163,54 +165,133 @@ _cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators) * exceed max_column. In particular, if a single word is larger than * max_column it will not be broken up. */ + +typedef enum _cairo_word_wrap_state { + WRAP_STATE_DELIMITER, + WRAP_STATE_WORD, + WRAP_STATE_STRING, + WRAP_STATE_HEXSTRING +} cairo_word_wrap_state_t; + + typedef struct _word_wrap_stream { cairo_output_stream_t base; cairo_output_stream_t *output; int max_column; + cairo_bool_t ps_output; int column; - cairo_bool_t last_write_was_space; - cairo_bool_t in_hexstring; - cairo_bool_t empty_hexstring; + cairo_word_wrap_state_t state; + cairo_bool_t in_escape; + int escape_digits; } word_wrap_stream_t; + + +/* Emit word bytes up to the next delimiter character */ static int -_count_word_up_to (const unsigned char *s, int length) +_word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream, + const unsigned char *data, int length) { - int word = 0; + const unsigned char *s = data; + int count = 0; while (length--) { - if (! (_cairo_isspace (*s) || *s == '<')) { - s++; - word++; - } else { - return word; + if (_cairo_isspace (*s) || *s == '<' || *s == '(') { + stream->state = WRAP_STATE_DELIMITER; + break; } + + count++; + stream->column++; + s++; } - return word; + if (count) + _cairo_output_stream_write (stream->output, data, count); + + return count; } -/* Count up to either the end of the ASCII hexstring or the number +/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number * of columns remaining. */ static int -_count_hexstring_up_to (const unsigned char *s, int length, int columns) +_word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream, + const unsigned char *data, int length) { - int word = 0; + const unsigned char *s = data; + int count = 0; + cairo_bool_t newline = FALSE; while (length--) { - if (*s++ != '>') - word++; - else - return word; + count++; + stream->column++; + if (*s == '>') { + stream->state = WRAP_STATE_DELIMITER; + break; + } - columns--; - if (columns < 0 && word > 1) - return word; + if (stream->column > stream->max_column) { + newline = TRUE; + break; + } + s++; } - return word; + if (count) + _cairo_output_stream_write (stream->output, data, count); + + if (newline) { + _cairo_output_stream_printf (stream->output, "\n"); + stream->column = 0; + } + + return count; +} + +/* Count up to either the end of the string or the number of columns + * remaining. + */ +static int +_word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream, + const unsigned char *data, int length) +{ + const unsigned char *s = data; + int count = 0; + cairo_bool_t newline = FALSE; + + while (length--) { + count++; + stream->column++; + if (!stream->in_escape) { + if (*s == ')') { + stream->state = WRAP_STATE_DELIMITER; + break; + } + if (*s == '\\') { + stream->in_escape = TRUE; + stream->escape_digits = 0; + } else if (stream->ps_output && stream->column > stream->max_column) { + newline = TRUE; + break; + } + } else { + if (!_cairo_isdigit(*s) || ++stream->escape_digits == 3) + stream->in_escape = FALSE; + } + s++; + } + + if (count) + _cairo_output_stream_write (stream->output, data, count); + + if (newline) { + _cairo_output_stream_printf (stream->output, "\\\n"); + stream->column = 0; + } + + return count; } static cairo_status_t @@ -219,65 +300,44 @@ _word_wrap_stream_write (cairo_output_stream_t *base, unsigned int length) { word_wrap_stream_t *stream = (word_wrap_stream_t *) base; - cairo_bool_t newline; - int word; + int count; while (length) { - if (*data == '<') { - stream->in_hexstring = TRUE; - stream->empty_hexstring = TRUE; - stream->last_write_was_space = FALSE; - data++; - length--; - _cairo_output_stream_printf (stream->output, "<"); + switch (stream->state) { + case WRAP_STATE_WORD: + count = _word_wrap_stream_count_word_up_to (stream, data, length); + break; + case WRAP_STATE_HEXSTRING: + count = _word_wrap_stream_count_hexstring_up_to (stream, data, length); + break; + case WRAP_STATE_STRING: + count = _word_wrap_stream_count_string_up_to (stream, data, length); + break; + case WRAP_STATE_DELIMITER: + count = 1; stream->column++; - } else if (*data == '>') { - stream->in_hexstring = FALSE; - stream->last_write_was_space = FALSE; - data++; - length--; - _cairo_output_stream_printf (stream->output, ">"); - stream->column++; - } else if (_cairo_isspace (*data)) { - newline = (*data == '\n' || *data == '\r'); - if (! newline && stream->column >= stream->max_column) { + if (*data == '\n' || stream->column >= stream->max_column) { _cairo_output_stream_printf (stream->output, "\n"); stream->column = 0; } - _cairo_output_stream_write (stream->output, data, 1); - data++; - length--; - if (newline) { - stream->column = 0; + if (*data == '<') { + stream->state = WRAP_STATE_HEXSTRING; + } else if (*data == '(') { + stream->state = WRAP_STATE_STRING; + } else if (!_cairo_isspace (*data)) { + stream->state = WRAP_STATE_WORD; } - else - stream->column++; - stream->last_write_was_space = TRUE; - } else { - if (stream->in_hexstring) { - word = _count_hexstring_up_to (data, length, - MAX (stream->max_column - stream->column, 0)); - } else { - word = _count_word_up_to (data, length); - } - /* Don't wrap if this word is a continuation of a non hex - * string word from a previous call to write. */ - if (stream->column + word >= stream->max_column) { - if (stream->last_write_was_space || - (stream->in_hexstring && !stream->empty_hexstring)) - { - _cairo_output_stream_printf (stream->output, "\n"); - stream->column = 0; - } - } - _cairo_output_stream_write (stream->output, data, word); - data += word; - length -= word; - stream->column += word; - stream->last_write_was_space = FALSE; - if (stream->in_hexstring) - stream->empty_hexstring = FALSE; + if (*data != '\n') + _cairo_output_stream_write (stream->output, data, 1); + break; + + default: + ASSERT_NOT_REACHED; + count = length; + break; } + data += count; + length -= count; } return _cairo_output_stream_get_status (stream->output); @@ -292,14 +352,14 @@ _word_wrap_stream_close (cairo_output_stream_t *base) } static cairo_output_stream_t * -_word_wrap_stream_create (cairo_output_stream_t *output, int max_column) +_word_wrap_stream_create (cairo_output_stream_t *output, cairo_bool_t ps, int max_column) { word_wrap_stream_t *stream; if (output->status) return _cairo_output_stream_create_in_error (output->status); - stream = malloc (sizeof (word_wrap_stream_t)); + stream = _cairo_malloc (sizeof (word_wrap_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; @@ -311,10 +371,11 @@ _word_wrap_stream_create (cairo_output_stream_t *output, int max_column) _word_wrap_stream_close); stream->output = output; stream->max_column = max_column; + stream->ps_output = ps; stream->column = 0; - stream->last_write_was_space = FALSE; - stream->in_hexstring = FALSE; - stream->empty_hexstring = TRUE; + stream->state = WRAP_STATE_DELIMITER; + stream->in_escape = FALSE; + stream->escape_digits = 0; return &stream->base; } @@ -437,7 +498,7 @@ _cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box) */ static cairo_status_t _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_matrix_t *path_transform, cairo_line_cap_t line_cap) { @@ -446,7 +507,7 @@ _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, pdf_path_info_t info; cairo_box_t box; - word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72); + word_wrap = _word_wrap_stream_create (pdf_operators->stream, pdf_operators->ps_output, 72); status = _cairo_output_stream_get_status (word_wrap); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap); @@ -454,11 +515,12 @@ _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, info.output = word_wrap; info.path_transform = path_transform; info.line_cap = line_cap; - if (_cairo_path_fixed_is_rectangle (path, &box)) { + if (_cairo_path_fixed_is_rectangle (path, &box) && + ((path_transform->xx == 0 && path_transform->yy == 0) || + (path_transform->xy == 0 && path_transform->yx == 0))) { status = _cairo_pdf_path_rectangle (&info, &box); } else { status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_pdf_path_move_to, _cairo_pdf_path_line_to, _cairo_pdf_path_curve_to, @@ -475,7 +537,7 @@ _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, cairo_int_status_t _cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule) { const char *pdf_operator; @@ -708,13 +770,13 @@ _cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) static cairo_int_status_t _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, const char *pdf_operator) { - cairo_status_t status; + cairo_int_status_t status; cairo_matrix_t m, path_transform; cairo_bool_t has_ctm = TRUE; double scale = 1.0; @@ -773,10 +835,9 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, return status; if (has_ctm) { - _cairo_output_stream_printf (pdf_operators->stream, - "q %f %f %f %f %f %f cm\n", - m.xx, m.yx, m.xy, m.yy, - m.x0, m.y0); + _cairo_output_stream_printf (pdf_operators->stream, "q "); + _cairo_output_stream_print_matrix (pdf_operators->stream, &m); + _cairo_output_stream_printf (pdf_operators->stream, " cm\n"); } else { path_transform = pdf_operators->cairo_to_pdf; } @@ -799,7 +860,7 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, cairo_int_status_t _cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse) @@ -814,7 +875,7 @@ _cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, cairo_int_status_t _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule) { const char *pdf_operator; @@ -853,7 +914,7 @@ _cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, cairo_int_status_t _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, @@ -880,6 +941,26 @@ _cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, operator); } +static void +_cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream, + unsigned int glyph) +{ + if (pdf_operators->is_latin) { + if (glyph == '(' || glyph == ')' || glyph == '\\') + _cairo_output_stream_printf (stream, "\\%c", glyph); + else if (glyph >= 0x20 && glyph <= 0x7e) + _cairo_output_stream_printf (stream, "%c", glyph); + else + _cairo_output_stream_printf (stream, "\\%03o", glyph); + } else { + _cairo_output_stream_printf (stream, + "%0*x", + pdf_operators->hex_width, + glyph); + } +} + #define GLYPH_POSITION_TOLERANCE 0.001 /* Emit the string of glyphs using the 'Tj' operator. This requires @@ -890,15 +971,14 @@ _cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators, { int i; - _cairo_output_stream_printf (stream, "<"); + _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { - _cairo_output_stream_printf (stream, - "%0*x", - pdf_operators->hex_width, - pdf_operators->glyphs[i].glyph_index); + _cairo_pdf_operators_emit_glyph_index (pdf_operators, + stream, + pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } - _cairo_output_stream_printf (stream, ">Tj\n"); + _cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); } @@ -918,7 +998,7 @@ _cairo_pdf_operators_emit_glyph_string_with_positioning ( { int i; - _cairo_output_stream_printf (stream, "[<"); + _cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<"); for (i = 0; i < pdf_operators->num_glyphs; i++) { if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x) { @@ -934,10 +1014,18 @@ _cairo_pdf_operators_emit_glyph_string_with_positioning ( * calculating subsequent deltas. */ rounded_delta = _cairo_lround (delta); + if (abs(rounded_delta) < 3) + rounded_delta = 0; if (rounded_delta != 0) { - _cairo_output_stream_printf (stream, - ">%d<", - rounded_delta); + if (pdf_operators->is_latin) { + _cairo_output_stream_printf (stream, + ")%d(", + rounded_delta); + } else { + _cairo_output_stream_printf (stream, + ">%d<", + rounded_delta); + } } /* Convert the rounded delta back to text @@ -947,13 +1035,12 @@ _cairo_pdf_operators_emit_glyph_string_with_positioning ( pdf_operators->cur_x += delta; } - _cairo_output_stream_printf (stream, - "%0*x", - pdf_operators->hex_width, - pdf_operators->glyphs[i].glyph_index); + _cairo_pdf_operators_emit_glyph_index (pdf_operators, + stream, + pdf_operators->glyphs[i].glyph_index); pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance; } - _cairo_output_stream_printf (stream, ">]TJ\n"); + _cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">"); return _cairo_output_stream_get_status (stream); } @@ -969,7 +1056,7 @@ _cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) if (pdf_operators->num_glyphs == 0) return CAIRO_STATUS_SUCCESS; - word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72); + word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, pdf_operators->ps_output, 72); status = _cairo_output_stream_get_status (word_wrap_stream); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap_stream); @@ -1039,14 +1126,8 @@ _cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, pdf_operators->cur_x = 0; pdf_operators->cur_y = 0; pdf_operators->glyph_buf_x_pos = 0; - _cairo_output_stream_printf (pdf_operators->stream, - "%f %f %f %f %f %f Tm\n", - pdf_operators->text_matrix.xx, - pdf_operators->text_matrix.yx, - pdf_operators->text_matrix.xy, - pdf_operators->text_matrix.yy, - pdf_operators->text_matrix.x0, - pdf_operators->text_matrix.y0); + _cairo_output_stream_print_matrix (pdf_operators->stream, &pdf_operators->text_matrix); + _cairo_output_stream_printf (pdf_operators->stream, " Tm\n"); pdf_operators->cairo_to_pdftext = *matrix; status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); @@ -1128,6 +1209,7 @@ _cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_ope } pdf_operators->font_id = subset_glyph->font_id; pdf_operators->subset_id = subset_glyph->subset_id; + pdf_operators->is_latin = subset_glyph->is_latin; if (subset_glyph->is_composite) pdf_operators->hex_width = 4; @@ -1244,7 +1326,7 @@ _cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operator * current position to the next glyph. We also use the Td * operator to move the current position if the horizontal * position changes by more than 10 (in text space - * units). This is becauses the horizontal glyph positioning + * units). This is because the horizontal glyph positioning * in the TJ operator is intended for kerning and there may be * PDF consumers that do not handle very large position * adjustments in TJ. @@ -1287,7 +1369,7 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, { cairo_scaled_font_subsets_glyph_t subset_glyph; cairo_glyph_t *cur_glyph; - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; int i; /* If the cluster maps 1 glyph to 1 or more unicode characters, we @@ -1322,17 +1404,23 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, } } - /* Fallback to using ActualText to map zero or more glyphs to a - * unicode string. */ - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; + if (pdf_operators->use_actual_text) { + /* Fallback to using ActualText to map zero or more glyphs to a + * unicode string. */ + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; - status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); - if (unlikely (status)) - return status; + status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len); + if (unlikely (status)) + return status; + } + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; - cur_glyph = glyphs; /* XXX * If no glyphs, we should put *something* here for the text to be selectable. */ for (i = 0; i < num_glyphs; i++) { @@ -1355,11 +1443,14 @@ _cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, else cur_glyph++; } - status = _cairo_pdf_operators_flush_glyphs (pdf_operators); - if (unlikely (status)) - return status; - status = _cairo_pdf_operators_end_actualtext (pdf_operators); + if (pdf_operators->use_actual_text) { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_end_actualtext (pdf_operators); + } return status; } @@ -1402,9 +1493,6 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, cairo_matrix_init_scale (&invert_y_axis, 1, -1); text_matrix = scaled_font->scale; - /* Invert y axis in font space */ - cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis); - /* Invert y axis in device space */ cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); @@ -1467,4 +1555,41 @@ _cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, return _cairo_output_stream_get_status (pdf_operators->stream); } +cairo_int_status_t +_cairo_pdf_operators_tag_begin (cairo_pdf_operators_t *pdf_operators, + const char *tag_name, + int mcid) +{ + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "/%s << /MCID %d >> BDC\n", + tag_name, + mcid); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +cairo_int_status_t +_cairo_pdf_operators_tag_end (cairo_pdf_operators_t *pdf_operators) +{ + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + #endif /* CAIRO_HAS_PDF_OPERATORS */ diff --git a/gfx/cairo/cairo/src/cairo-pdf-shading-private.h b/gfx/cairo/cairo/src/cairo-pdf-shading-private.h new file mode 100644 index 000000000000..0ca8cb7d518f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-shading-private.h @@ -0,0 +1,100 @@ +/* -*- 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 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 + */ + +#ifndef CAIRO_PDF_SHADING_H +#define CAIRO_PDF_SHADING_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" +#include "cairo-pattern-private.h" + + +typedef struct _cairo_pdf_shading { + int shading_type; + int bits_per_coordinate; + int bits_per_component; + int bits_per_flag; + double *decode_array; + int decode_array_length; + unsigned char *data; + unsigned long data_length; +} cairo_pdf_shading_t; + + +/** + * _cairo_pdf_shading_init_color: + * @shading: a #cairo_pdf_shading_t to initialize + * @pattern: the #cairo_mesh_pattern_t to initialize from + * + * Generate the PDF shading dictionary data for the a PDF type 7 + * shading from RGB part of the specified mesh pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *pattern); + + +/** + * _cairo_pdf_shading_init_alpha: + * @shading: a #cairo_pdf_shading_t to initialize + * @pattern: the #cairo_mesh_pattern_t to initialize from + * + * Generate the PDF shading dictionary data for a PDF type 7 + * shading from alpha part of the specified mesh pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *pattern); + +/** + * _cairo_pdf_shading_fini: + * @shading: a #cairo_pdf_shading_t + * + * Free all resources associated with @shading. After this call, + * @shading should not be used again without a subsequent call to + * _cairo_pdf_shading_init() again first. + **/ +cairo_private void +_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading); + + +#endif /* CAIRO_PDF_SHADING_H */ diff --git a/gfx/cairo/cairo/src/cairo-pdf-shading.c b/gfx/cairo/cairo/src/cairo-pdf-shading.c new file mode 100644 index 000000000000..b114b3104ba3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-shading.c @@ -0,0 +1,279 @@ +/* -*- 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 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 + */ + +#include "cairoint.h" + +#if CAIRO_HAS_PDF_OPERATORS + +#include "cairo-pdf-shading-private.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" +#include + +static unsigned char * +encode_coordinate (unsigned char *p, double c) +{ + uint32_t f; + + f = c; + *p++ = f >> 24; + *p++ = (f >> 16) & 0xff; + *p++ = (f >> 8) & 0xff; + *p++ = f & 0xff; + + return p; +} + +static unsigned char * +encode_point (unsigned char *p, const cairo_point_double_t *point) +{ + p = encode_coordinate (p, point->x); + p = encode_coordinate (p, point->y); + + return p; +} + +static unsigned char * +encode_color_component (unsigned char *p, double color) +{ + uint16_t c; + + c = _cairo_color_double_to_short (color); + *p++ = c >> 8; + *p++ = c & 0xff; + + return p; +} + +static unsigned char * +encode_color (unsigned char *p, const cairo_color_t *color) +{ + p = encode_color_component (p, color->red); + p = encode_color_component (p, color->green); + p = encode_color_component (p, color->blue); + + return p; +} + +static unsigned char * +encode_alpha (unsigned char *p, const cairo_color_t *color) +{ + p = encode_color_component (p, color->alpha); + + return p; +} + +static cairo_status_t +_cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *mesh, + cairo_bool_t is_alpha) +{ + unsigned int num_color_components, i; + cairo_bool_t is_valid; + + if (is_alpha) + num_color_components = 1; + else + num_color_components = 3; + + shading->decode_array_length = 4 + num_color_components * 2; + shading->decode_array = _cairo_malloc_ab (shading->decode_array_length, + sizeof (double)); + if (unlikely (shading->decode_array == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + is_valid = _cairo_mesh_pattern_coord_box (mesh, + &shading->decode_array[0], + &shading->decode_array[2], + &shading->decode_array[1], + &shading->decode_array[3]); + + assert (is_valid); + assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON); + assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON); + + for (i = 0; i < num_color_components; i++) { + shading->decode_array[4 + 2*i] = 0; + shading->decode_array[5 + 2*i] = 1; + } + + return CAIRO_STATUS_SUCCESS; +} + +/* The ISO32000 specification mandates this order for the points which + * define the patch. */ +static const int pdf_points_order_i[16] = { + 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 }; +static const int pdf_points_order_j[16] = { + 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 }; + +static cairo_status_t +_cairo_pdf_shading_generate_data (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *mesh, + cairo_bool_t is_alpha) +{ + const cairo_mesh_patch_t *patch; + double x_off, y_off, x_scale, y_scale; + unsigned int num_patches; + unsigned int num_color_components; + unsigned char *p; + unsigned int i, j; + + if (is_alpha) + num_color_components = 1; + else + num_color_components = 3; + + num_patches = _cairo_array_num_elements (&mesh->patches); + patch = _cairo_array_index_const (&mesh->patches, 0); + + /* Each patch requires: + * + * 1 flag - 1 byte + * 16 points. Each point is 2 coordinates. Each coordinate is + * stored in 4 bytes. + * + * 4 colors. Each color is stored in 2 bytes * num_color_components. + */ + shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components); + shading->data = _cairo_malloc (shading->data_length); + if (unlikely (shading->data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + x_off = shading->decode_array[0]; + y_off = shading->decode_array[2]; + x_scale = UINT32_MAX / (shading->decode_array[1] - x_off); + y_scale = UINT32_MAX / (shading->decode_array[3] - y_off); + + p = shading->data; + for (i = 0; i < num_patches; i++) { + /* edge flag */ + *p++ = 0; + + /* 16 points */ + for (j = 0; j < 16; j++) { + cairo_point_double_t point; + int pi, pj; + + pi = pdf_points_order_i[j]; + pj = pdf_points_order_j[j]; + point = patch[i].points[pi][pj]; + + /* Transform the point as specified in the decode array */ + point.x -= x_off; + point.y -= y_off; + point.x *= x_scale; + point.y *= y_scale; + + /* Make sure that rounding errors don't cause + * wraparounds */ + point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX); + point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX); + + p = encode_point (p, &point); + } + + /* 4 colors */ + for (j = 0; j < 4; j++) { + if (is_alpha) + p = encode_alpha (p, &patch[i].colors[j]); + else + p = encode_color (p, &patch[i].colors[j]); + } + } + + assert (p == shading->data + shading->data_length); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_shading_init (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *mesh, + cairo_bool_t is_alpha) +{ + cairo_status_t status; + + assert (mesh->base.status == CAIRO_STATUS_SUCCESS); + assert (mesh->current_patch == NULL); + + shading->shading_type = 7; + + /* + * Coordinates from the minimum to the maximum value of the mesh + * map to the [0..UINT32_MAX] range and are represented as + * uint32_t values. + * + * Color components are represented as uint16_t (in a 0.16 fixed + * point format, as in the rest of cairo). + */ + shading->bits_per_coordinate = 32; + shading->bits_per_component = 16; + shading->bits_per_flag = 8; + + shading->decode_array = NULL; + shading->data = NULL; + + status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha); + if (unlikely (status)) + return status; + + return _cairo_pdf_shading_generate_data (shading, mesh, is_alpha); +} + +cairo_status_t +_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *pattern) +{ + return _cairo_pdf_shading_init (shading, pattern, FALSE); +} + +cairo_status_t +_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading, + const cairo_mesh_pattern_t *pattern) +{ + return _cairo_pdf_shading_init (shading, pattern, TRUE); +} + +void +_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading) +{ + free (shading->data); + free (shading->decode_array); +} + +#endif /* CAIRO_HAS_PDF_OPERATORS */ diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface-private.h b/gfx/cairo/cairo/src/cairo-pdf-surface-private.h index 221418ec9bab..3793332ce8c8 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-pdf-surface-private.h @@ -48,6 +48,8 @@ #include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" #include "cairo-path-fixed-private.h" +#include "cairo-tag-attributes-private.h" +#include "cairo-tag-stack-private.h" typedef struct _cairo_pdf_resource { unsigned int id; @@ -60,6 +62,7 @@ typedef struct _cairo_pdf_group_resources { cairo_array_t alphas; cairo_array_t smasks; cairo_array_t patterns; + cairo_array_t shadings; cairo_array_t xobjects; cairo_array_t fonts; } cairo_pdf_group_resources_t; @@ -67,14 +70,31 @@ typedef struct _cairo_pdf_group_resources { typedef struct _cairo_pdf_source_surface_entry { cairo_hash_entry_t base; unsigned int id; + unsigned char *unique_id; + unsigned long unique_id_length; + cairo_operator_t operator; cairo_bool_t interpolate; + cairo_bool_t stencil_mask; + cairo_bool_t smask; + cairo_bool_t need_transp_group; cairo_pdf_resource_t surface_res; - int width; - int height; + cairo_pdf_resource_t smask_res; + + /* True if surface will be emitted as an Image XObject. */ + cairo_bool_t emit_image; + + /* Extents of the source surface. */ + cairo_bool_t bounded; + cairo_rectangle_int_t extents; + + /* Union of source extents required for all operations using this source */ + cairo_rectangle_int_t required_extents; } cairo_pdf_source_surface_entry_t; typedef struct _cairo_pdf_source_surface { + cairo_pattern_type_t type; cairo_surface_t *surface; + cairo_pattern_t *raster_pattern; cairo_pdf_source_surface_entry_t *hash_entry; } cairo_pdf_source_surface_t; @@ -85,6 +105,19 @@ typedef struct _cairo_pdf_pattern { cairo_pattern_t *pattern; cairo_pdf_resource_t pattern_res; cairo_pdf_resource_t gstate_res; + cairo_operator_t operator; + cairo_bool_t is_shading; + + /* PDF pattern space is the pattern matrix concatenated with the + * initial space of the parent object. If the parent object is the + * page, the initial space does not include the Y-axis flipping + * matrix emitted at the start of the page content stream. If the + * parent object is not the page content stream, the initial space + * will have a flipped Y-axis. The inverted_y_axis flag is true + * when the initial space of the parent object that is drawing + * this pattern has a flipped Y-axis. + */ + cairo_bool_t inverted_y_axis; } cairo_pdf_pattern_t; typedef enum _cairo_pdf_operation { @@ -98,6 +131,7 @@ typedef enum _cairo_pdf_operation { typedef struct _cairo_pdf_smask_group { double width; double height; + cairo_rectangle_int_t extents; cairo_pdf_resource_t group_res; cairo_pdf_operation_t operation; cairo_pattern_t *source; @@ -118,6 +152,98 @@ typedef struct _cairo_pdf_smask_group { cairo_scaled_font_t *scaled_font; } cairo_pdf_smask_group_t; +typedef struct _cairo_pdf_jbig2_global { + unsigned char *id; + unsigned long id_length; + cairo_pdf_resource_t res; + cairo_bool_t emitted; +} cairo_pdf_jbig2_global_t; + +/* cairo-pdf-interchange.c types */ + +struct page_mcid { + int page; + int mcid; +}; + +struct tag_extents { + cairo_rectangle_int_t extents; + cairo_bool_t valid; + cairo_list_t link; +}; + +typedef struct _cairo_pdf_struct_tree_node { + char *name; + cairo_pdf_resource_t res; + struct _cairo_pdf_struct_tree_node *parent; + cairo_list_t children; + cairo_array_t mcid; /* array of struct page_mcid */ + cairo_pdf_resource_t annot_res; /* 0 if no annot */ + struct tag_extents extents; + cairo_list_t link; +} cairo_pdf_struct_tree_node_t; + +typedef struct _cairo_pdf_annotation { + cairo_pdf_struct_tree_node_t *node; /* node containing the annotation */ + cairo_link_attrs_t link_attrs; +} cairo_pdf_annotation_t; + +typedef struct _cairo_pdf_named_dest { + cairo_hash_entry_t base; + struct tag_extents extents; + cairo_dest_attrs_t attrs; + int page; +} cairo_pdf_named_dest_t; + +typedef struct _cairo_pdf_outline_entry { + char *name; + cairo_link_attrs_t link_attrs; + cairo_pdf_outline_flags_t flags; + cairo_pdf_resource_t res; + struct _cairo_pdf_outline_entry *parent; + struct _cairo_pdf_outline_entry *first_child; + struct _cairo_pdf_outline_entry *last_child; + struct _cairo_pdf_outline_entry *next; + struct _cairo_pdf_outline_entry *prev; + int count; +} cairo_pdf_outline_entry_t; + +struct docinfo { + char *title; + char *author; + char *subject; + char *keywords; + char *creator; + char *create_date; + char *mod_date; +}; + +typedef struct _cairo_pdf_interchange { + cairo_tag_stack_t analysis_tag_stack; + cairo_tag_stack_t render_tag_stack; + cairo_array_t push_data; /* records analysis_tag_stack data field for each push */ + int push_data_index; + cairo_pdf_struct_tree_node_t *struct_root; + cairo_pdf_struct_tree_node_t *current_node; + cairo_pdf_struct_tree_node_t *begin_page_node; + cairo_pdf_struct_tree_node_t *end_page_node; + cairo_array_t parent_tree; /* parent tree resources */ + cairo_array_t mcid_to_tree; /* mcid to tree node mapping for current page */ + cairo_array_t annots; /* array of pointers to cairo_pdf_annotation_t */ + cairo_pdf_resource_t parent_tree_res; + cairo_list_t extents_list; + cairo_hash_table_t *named_dests; + int num_dests; + cairo_pdf_named_dest_t **sorted_dests; + cairo_pdf_resource_t dests_res; + int annot_page; + cairo_array_t outline; /* array of pointers to cairo_pdf_outline_entry_t; */ + struct docinfo docinfo; + +} cairo_pdf_interchange_t; + +/* pdf surface data */ + typedef struct _cairo_pdf_surface cairo_pdf_surface_t; struct _cairo_pdf_surface { @@ -129,7 +255,10 @@ struct _cairo_pdf_surface { double width; double height; + cairo_rectangle_int_t surface_extents; + cairo_bool_t surface_bounded; cairo_matrix_t cairo_to_pdf; + cairo_bool_t in_xobject; cairo_array_t objects; cairo_array_t pages; @@ -137,15 +266,19 @@ struct _cairo_pdf_surface { cairo_array_t alpha_linear_functions; cairo_array_t page_patterns; cairo_array_t page_surfaces; + cairo_array_t doc_surfaces; 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_scaled_font_subsets_t *font_subsets; cairo_array_t fonts; cairo_pdf_resource_t next_available_resource; cairo_pdf_resource_t pages_resource; + cairo_pdf_resource_t struct_tree_root; cairo_pdf_version_t pdf_version; cairo_bool_t compress_content; @@ -171,6 +304,7 @@ struct _cairo_pdf_surface { cairo_output_stream_t *mem_stream; cairo_output_stream_t *old_output; cairo_pdf_resource_t resource; + cairo_box_double_t bbox; cairo_bool_t is_knockout; } group_stream; @@ -190,7 +324,76 @@ struct _cairo_pdf_surface { double current_color_blue; double current_color_alpha; + cairo_pdf_interchange_t interchange; + int page_parent_tree; /* -1 if not used */ + cairo_array_t page_annots; + cairo_bool_t tagged; + char *current_page_label; + cairo_array_t page_labels; + cairo_pdf_resource_t outlines_dict_res; + cairo_pdf_resource_t names_dict_res; + cairo_pdf_resource_t docinfo_res; + cairo_pdf_resource_t page_labels_res; + + int thumbnail_width; + int thumbnail_height; + cairo_image_surface_t *thumbnail_image; + cairo_surface_t *paginated_surface; }; +cairo_private cairo_pdf_resource_t +_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); + +cairo_private void +_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource); + +cairo_private cairo_int_status_t +_cairo_utf8_to_pdf_string (const char *utf8, char **str_out); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_begin_page_content (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_end_page_content (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface, + const char *name, + const char *attributes); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface, + const char *name); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_add_operation_extents (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface, + int parent_id, + const char *name, + const char *dest, + cairo_pdf_outline_flags_t flags, + int *id); + +cairo_private cairo_int_status_t +_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8); + #endif /* CAIRO_PDF_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface.c b/gfx/cairo/cairo/src/cairo-pdf-surface.c index 09bd42ea0d71..52c49b6d2ee4 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-surface.c +++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c @@ -39,35 +39,32 @@ * Adrian Johnson */ -#define _BSD_SOURCE /* for snprintf() */ +#define _DEFAULT_SOURCE /* for snprintf() */ #include "cairoint.h" + #include "cairo-pdf.h" #include "cairo-pdf-surface-private.h" #include "cairo-pdf-operators-private.h" +#include "cairo-pdf-shading-private.h" + +#include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" #include "cairo-image-info-private.h" #include "cairo-recording-surface-private.h" #include "cairo-output-stream-private.h" #include "cairo-paginated-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-type3-glyph-surface-private.h" -#include #include -/* Issues: - * - * - We embed an image in the stream each time it's composited. We - * could add generation counters to surfaces and remember the stream - * ID for a particular generation for a particular surface. - * - * - Backend specific meta data. - */ - /* * Page Structure of the Generated PDF: * @@ -123,14 +120,80 @@ * * 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, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. + * + * # JBIG2 Images # + * JBIG2 data in PDF must be in the embedded format as described in + * ISO/IEC 11544. Image specific JBIG2 data must be in + * %CAIRO_MIME_TYPE_JBIG2. Any global segments in the JBIG2 data + * (segments with page association field set to 0) must be in + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data may be shared by + * multiple images. All images sharing the same global data must set + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifier. At least + * one of the images must provide the global data using + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data will only be + * embedded once and shared by all JBIG2 images with the same + * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID. + * + * # CCITT Fax Images # {#ccitt} + * The %CAIRO_MIME_TYPE_CCITT_FAX mime data requires a number of decoding + * parameters These parameters are specified using %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. + * + * %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime data must contain a string of the form + * "param1=value1 param2=value2 ...". + * + * @Columns: [required] An integer specifying the width of the image in pixels. + * + * @Rows: [required] An integer specifying the height of the image in scan lines. + * + * @K: [optional] An integer identifying the encoding scheme used. < 0 + * is 2 dimensional Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 + * and 2 dimensional encoding. Default is 0. + * + * @EndOfLine: [optional] If true end-of-line bit patterns are present. Default is false. + * + * @EncodedByteAlign: [optional] If true the end of line is padded + * with 0 bits so the next line begins on a byte boundary. Default is false. + * + * @EndOfBlock: [optional] If true the data contains an end-of-block pattern. Default is true. + * + * @BlackIs1: [optional] If true 1 bits are black pixels. Default is false. + * + * @DamagedRowsBeforeError: [optional] An integer specifying the + * number of damages rows tolerated before an error occurs. Default is 0. + * + * Boolean values may be "true" or "false", or 1 or 0. + * + * These parameters are the same as the CCITTFaxDecode parameters in the + * [PostScript Language Reference](https://www.adobe.com/products/postscript/pdfs/PLRM.pdf) + * and [Portable Document Format (PDF)](https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf). + * Refer to these documents for further details. + * + * An example %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS string is: + * + * + * "Columns=10230 Rows=40000 K=1 EndOfLine=true EncodedByteAlign=1 BlackIs1=false" + * + * + **/ + +static cairo_bool_t +_cairo_pdf_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); /** * CAIRO_HAS_PDF_SURFACE: * * Defined if the PDF surface backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.2 + **/ static const cairo_pdf_version_t _cairo_pdf_versions[] = { @@ -146,6 +209,19 @@ static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] = "PDF 1.5" }; +static const char *_cairo_pdf_supported_mime_types[] = +{ + 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, + NULL +}; + typedef struct _cairo_pdf_object { long offset; } cairo_pdf_object_t; @@ -168,16 +244,13 @@ typedef struct _cairo_pdf_alpha_linear_function { double alpha2; } cairo_pdf_alpha_linear_function_t; -static cairo_pdf_resource_t -_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface); - static void _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface); static void _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_font (unsigned int font_id, unsigned int subset_id, void *closure); @@ -185,34 +258,41 @@ _cairo_pdf_surface_add_font (unsigned int font_id, static void _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, cairo_bool_t compressed, const char *fmt, ...) CAIRO_PRINTF_FORMAT(4, 5); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface); -static cairo_status_t +static cairo_int_status_t +_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, + cairo_pdf_source_surface_t *source, + cairo_bool_t test, + cairo_bool_t *is_image); + +static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); static void _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); -static cairo_pdf_resource_t -_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface); - static cairo_pdf_resource_t _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); static long _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); -static cairo_status_t +static cairo_int_status_t +_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface, + cairo_bool_t finish); + +static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); static cairo_bool_t @@ -221,11 +301,11 @@ _cairo_pdf_source_surface_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; -static cairo_pdf_resource_t +cairo_pdf_resource_t _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t resource; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_object_t object; object.offset = _cairo_output_stream_get_position (surface->output); @@ -242,7 +322,7 @@ _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) return resource; } -static void +void _cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, cairo_pdf_resource_t resource) { @@ -259,9 +339,10 @@ _cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, { surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_pdf); + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); } static cairo_bool_t @@ -316,7 +397,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, cairo_pdf_surface_t *surface; cairo_status_t status, status_ignored; - surface = malloc (sizeof (cairo_pdf_surface_t)); + surface = _cairo_malloc (sizeof (cairo_pdf_surface_t)); if (unlikely (surface == NULL)) { /* destroy stream on behalf of caller */ status = _cairo_output_stream_destroy (output); @@ -326,12 +407,19 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_surface_init (&surface->base, &cairo_pdf_surface_backend, NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ surface->output = output; surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height); + cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, 1, 0, 0); + surface->in_xobject = FALSE; + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + surface->surface_bounded = TRUE; _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t)); _cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t)); @@ -343,6 +431,9 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t)); + _cairo_array_init (&surface->doc_surfaces, sizeof (cairo_pdf_source_surface_t)); + _cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_t)); + _cairo_array_init (&surface->page_heights, sizeof (double)); surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal); if (unlikely (surface->all_surfaces == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -357,6 +448,8 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL1; } + _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); + surface->next_available_resource.id = 1; surface->pages_resource = _cairo_pdf_surface_new_object (surface); if (surface->pages_resource.id == 0) { @@ -364,6 +457,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, goto BAIL2; } + surface->struct_tree_root.id = 0; surface->pdf_version = CAIRO_PDF_VERSION_1_5; surface->compress_content = TRUE; surface->pdf_stream.active = FALSE; @@ -386,12 +480,33 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, _cairo_pdf_operators_init (&surface->pdf_operators, surface->output, &surface->cairo_to_pdf, - surface->font_subsets); + surface->font_subsets, + FALSE); _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, _cairo_pdf_surface_add_font, surface); _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE); + status = _cairo_pdf_interchange_init (surface); + if (unlikely (status)) + goto BAIL2; + + surface->page_parent_tree = -1; + _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t)); + surface->tagged = FALSE; + surface->current_page_label = NULL; + _cairo_array_init (&surface->page_labels, sizeof (char *)); + surface->outlines_dict_res.id = 0; + surface->names_dict_res.id = 0; + surface->docinfo_res.id = 0; + surface->page_labels_res.id = 0; + surface->thumbnail_width = 0; + surface->thumbnail_height = 0; + surface->thumbnail_image = NULL; + + if (getenv ("CAIRO_DEBUG_PDF") != NULL) + surface->compress_content = FALSE; + surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, CAIRO_CONTENT_COLOR_ALPHA, @@ -440,7 +555,7 @@ BAIL0: * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 - */ + **/ cairo_surface_t * cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, void *closure, @@ -645,6 +760,7 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, double height_in_points) { cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + cairo_status_t status; if (! _extract_pdf_surface (surface, &pdf_surface)) return; @@ -652,6 +768,148 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, _cairo_pdf_surface_set_size_internal (pdf_surface, width_in_points, height_in_points); + status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface, + width_in_points, + height_in_points); + if (status) + status = _cairo_surface_set_error (surface, status); +} + +/** + * CAIRO_PDF_OUTLINE_ROOT: + * + * The root outline item in cairo_pdf_surface_add_outline(). + * + * Since: 1.16 + **/ + +/** + * cairo_pdf_surface_add_outline: + * @surface: a PDF #cairo_surface_t + * @parent_id: the id of the parent item or %CAIRO_PDF_OUTLINE_ROOT if this is a top level item. + * @utf8: the name of the outline + * @link_attribs: the link attributes specifying where this outline links to + * @flags: outline item flags + * + * Add an item to the document outline hierarchy with the name @utf8 + * that links to the location specified by @link_attribs. Link + * attributes have the same keys and values as the [Link Tag][link], + * excluding the "rect" attribute. The item will be a child of the + * item with id @parent_id. Use %CAIRO_PDF_OUTLINE_ROOT as the parent + * id of top level items. + * + * Return value: the id for the added item. + * + * Since: 1.16 + **/ +int +cairo_pdf_surface_add_outline (cairo_surface_t *surface, + int parent_id, + const char *utf8, + const char *link_attribs, + cairo_pdf_outline_flags_t flags) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + cairo_status_t status; + int id = 0; + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return 0; + + status = _cairo_pdf_interchange_add_outline (pdf_surface, + parent_id, + utf8, + link_attribs, + flags, + &id); + if (status) + status = _cairo_surface_set_error (surface, status); + + return id; +} + +/** + * cairo_pdf_surface_set_metadata: + * @surface: a PDF #cairo_surface_t + * @metadata: The metadata item to set. + * @utf8: metadata value + * + * Set document metadata. The %CAIRO_PDF_METADATA_CREATE_DATE and + * %CAIRO_PDF_METADATA_MOD_DATE values must be in ISO-8601 format: + * YYYY-MM-DDThh:mm:ss. An optional timezone of the form "[+/-]hh:mm" + * or "Z" for UTC time can be appended. All other metadata values can be any UTF-8 + * string. + * + * For example: + * + * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_TITLE, "My Document"); + * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2015-12-31T23:59+02:00"); + * + * + * Since: 1.16 + **/ +void +cairo_pdf_surface_set_metadata (cairo_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + cairo_status_t status; + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + status = _cairo_pdf_interchange_set_metadata (pdf_surface, metadata, utf8); + if (status) + status = _cairo_surface_set_error (surface, status); +} + +/** + * cairo_pdf_surface_set_page_label: + * @surface: a PDF #cairo_surface_t + * @utf8: The page label. + * + * Set page label for the current page. + * + * Since: 1.16 + **/ +void +cairo_pdf_surface_set_page_label (cairo_surface_t *surface, + const char *utf8) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + free (pdf_surface->current_page_label); + pdf_surface->current_page_label = utf8 ? strdup (utf8) : NULL; +} + +/** + * cairo_pdf_surface_set_thumbnail_size: + * @surface: a PDF #cairo_surface_t + * @width: Thumbnail width. + * @height: Thumbnail height + * + * Set the thumbnail image size for the current and all subsequent + * pages. Setting a width or height of 0 disables thumbnails for the + * current and subsequent pages. + * + * Since: 1.16 + **/ +void +cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface, + int width, + int height) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + pdf_surface->thumbnail_width = width; + pdf_surface->thumbnail_height = height; } static void @@ -683,6 +941,11 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) } _cairo_array_truncate (&surface->smask_groups, 0); _cairo_array_truncate (&surface->knockout_group, 0); + _cairo_array_truncate (&surface->page_annots, 0); + + if (surface->thumbnail_image) + cairo_surface_destroy (&surface->thumbnail_image->base); + surface->thumbnail_image = NULL; } static void @@ -696,6 +959,7 @@ _cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) _cairo_array_init (&res->alphas, sizeof (double)); _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t)); + _cairo_array_init (&res->shadings, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t)); } @@ -706,6 +970,7 @@ _cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res) _cairo_array_fini (&res->alphas); _cairo_array_fini (&res->smasks); _cairo_array_fini (&res->patterns); + _cairo_array_fini (&res->shadings); _cairo_array_fini (&res->xobjects); _cairo_array_fini (&res->fonts); } @@ -721,6 +986,7 @@ _cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) _cairo_array_truncate (&res->alphas, 0); _cairo_array_truncate (&res->smasks, 0); _cairo_array_truncate (&res->patterns, 0); + _cairo_array_truncate (&res->shadings, 0); _cairo_array_truncate (&res->xobjects, 0); _cairo_array_truncate (&res->fonts, 0); } @@ -734,14 +1000,14 @@ _cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, res->operators[op] = TRUE; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha, int *index) { int num_alphas, i; double other; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_group_resources_t *res = &surface->resources; num_alphas = _cairo_array_num_elements (&res->alphas); @@ -762,28 +1028,36 @@ _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface, cairo_pdf_resource_t smask) { return _cairo_array_append (&(surface->resources.smasks), &smask); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface, cairo_pdf_resource_t pattern) { return _cairo_array_append (&(surface->resources.patterns), &pattern); } -static cairo_status_t +static cairo_int_status_t +_cairo_pdf_surface_add_shading (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t shading) +{ + return _cairo_array_append (&(surface->resources.shadings), &shading); +} + + +static cairo_int_status_t _cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface, cairo_pdf_resource_t xobject) { return _cairo_array_append (&(surface->resources.xobjects), &xobject); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_font (unsigned int font_id, unsigned int subset_id, void *closure) @@ -791,7 +1065,7 @@ _cairo_pdf_surface_add_font (unsigned int font_id, cairo_pdf_surface_t *surface = closure; cairo_pdf_font_t font; int num_fonts, i; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_group_resources_t *res = &surface->resources; num_fonts = _cairo_array_num_elements (&res->fonts); @@ -889,7 +1163,7 @@ _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, { int num_alphas, num_smasks, num_resources, i; double alpha; - cairo_pdf_resource_t *smask, *pattern, *xobject; + cairo_pdf_resource_t *smask, *pattern, *shading, *xobject; cairo_pdf_font_t *font; _cairo_output_stream_printf (surface->output, "<<\n"); @@ -941,6 +1215,21 @@ _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, " >>\n"); } + num_resources = _cairo_array_num_elements (&res->shadings); + if (num_resources > 0) { + _cairo_output_stream_printf (surface->output, + " /Shading <<"); + for (i = 0; i < num_resources; i++) { + shading = _cairo_array_index (&res->shadings, i); + _cairo_output_stream_printf (surface->output, + " /sh%d %d 0 R", + shading->id, shading->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + num_resources = _cairo_array_num_elements (&res->xobjects); if (num_resources > 0) { _cairo_output_stream_printf (surface->output, @@ -976,7 +1265,8 @@ _cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, } static cairo_pdf_smask_group_t * -_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) +_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents) { cairo_pdf_smask_group_t *group; @@ -994,6 +1284,14 @@ _cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface) } group->width = surface->width; group->height = surface->height; + if (extents != NULL) { + group->extents = *extents; + } else { + group->extents.x = 0; + group->extents.y = 0; + group->extents.width = surface->width; + group->extents.height = surface->height; + } return group; } @@ -1007,18 +1305,15 @@ _cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group) cairo_pattern_destroy (group->source); if (group->mask) cairo_pattern_destroy (group->mask); - if (group->utf8) - free (group->utf8); - if (group->glyphs) - free (group->glyphs); - if (group->clusters) - free (group->clusters); + free (group->utf8); + free (group->glyphs); + free (group->clusters); if (group->scaled_font) cairo_scaled_font_destroy (group->scaled_font); free (group); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { @@ -1031,113 +1326,186 @@ _cairo_pdf_source_surface_equal (const void *key_a, const void *key_b) const cairo_pdf_source_surface_entry_t *a = key_a; const cairo_pdf_source_surface_entry_t *b = key_b; - return (a->id == b->id) && (a->interpolate == b->interpolate); + if (a->interpolate != b->interpolate) + return FALSE; + + if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length) + return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0); + + return (a->id == b->id); } static void _cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key) { - key->base.hash = key->id; + if (key->unique_id && key->unique_id_length > 0) { + key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + key->unique_id, key->unique_id_length); + } else { + key->base.hash = key->id; + } } static cairo_int_status_t -_get_jpx_image_info (cairo_surface_t *source, - cairo_image_info_t *info, - const unsigned char **mime_data, - unsigned long *mime_data_length) +_cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_image_surface_t **image, + void **image_extra) { - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, - mime_data, mime_data_length); - if (*mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; + return _cairo_surface_acquire_source_image (surf_pat->surface, image, image_extra); + } break; - return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length); -} + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { + cairo_surface_t *surf; + surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL); + if (!surf) + return CAIRO_INT_STATUS_UNSUPPORTED; + assert (_cairo_surface_is_image (surf)); + *image = (cairo_image_surface_t *) surf; + } break; -static cairo_int_status_t -_get_jpeg_image_info (cairo_surface_t *source, - cairo_image_info_t *info, - const unsigned char **mime_data, - unsigned long *mime_data_length) -{ - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - mime_data, mime_data_length); - if (*mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length); -} - -static cairo_status_t -_get_source_surface_size (cairo_surface_t *source, - int *width, - int *height) -{ - cairo_status_t status; - cairo_rectangle_int_t extents; - cairo_image_info_t info; - const unsigned char *mime_data; - unsigned long mime_data_length; - - if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; - - *width = sub->extents.width; - *height = sub->extents.height; - - } else { - cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source; - cairo_box_t bbox; - - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - - *width = extents.width; - *height = extents.height; - } - return CAIRO_STATUS_SUCCESS; + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + default: + ASSERT_NOT_REACHED; + break; } - status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; - return status; - } - - status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *width = info.width; - *height = info.height; - return status; - } - - if (! _cairo_surface_get_extents (source, &extents)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - *width = extents.width; - *height = extents.height; - return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_filter_t filter, - cairo_pdf_resource_t *surface_res, - int *width, - int *height) +static void +_cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_image_surface_t *image, + void *image_extra) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; + _cairo_surface_release_source_image (surf_pat->surface, image, image_extra); + } break; + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + _cairo_raster_source_pattern_release (pattern, &image->base); + break; + + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + default: + + ASSERT_NOT_REACHED; + break; + } +} + +static cairo_int_status_t +_get_source_surface_extents (cairo_surface_t *source, + cairo_rectangle_int_t *extents, + cairo_bool_t *bounded, + cairo_bool_t *subsurface) +{ + cairo_int_status_t status; + + *bounded = TRUE; + *subsurface = FALSE; + if (source->type == CAIRO_SURFACE_TYPE_RECORDING) { + cairo_surface_t *free_me = NULL; + + if (_cairo_surface_is_snapshot (source)) + free_me = source = _cairo_surface_snapshot_get_target (source); + + if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + + *extents = sub->extents; + *subsurface = TRUE; + } else { + cairo_box_t box; + + *bounded = _cairo_surface_get_extents (source, extents); + if (! *bounded) { + status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source, + &box, NULL); + if (unlikely (status)) { + cairo_surface_destroy (free_me); + return status; + } + _cairo_box_round_to_rectangle (&box, extents); + } + } + cairo_surface_destroy (free_me); + } else { + *bounded = _cairo_surface_get_extents (source, extents); + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_pdf_surface_add_source_surface: + * @surface: [in] the pdf surface + * @source_surface: [in] A #cairo_surface_t to use as the source surface + * @source_pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source + * @op: [in] the operator used to composite this source + * @filter: [in] filter type of the source pattern + * @stencil_mask: [in] if true, the surface will be written to the PDF as an /ImageMask + * @smask: [in] if true, only the alpha channel will be written (images only) + * @need_transp_group: [in] if true and an XObject is used, make it a Transparency group + * @extents: [in] extents of the operation that is using this source + * @smask_res: [in] if not NULL, the image written will specify this resource as the smask for + * the image (images only) + * @pdf_source: [out] return pdf_source_surface entry in hash table + * @x_offset: [out] if not NULL return x offset of surface + * @y_offset: [out] if not NULL return y offset of surface + * @source_extents: [out] if not NULL return operation extents in source space + * + * Add surface or raster_source pattern to list of surfaces to be + * written to the PDF file when the current page is finished. Returns + * a PDF resource to reference the surface. A hash table of all + * surfaces in the PDF file (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or + * surface unique_id) is used to ensure surfaces with the same id are + * only written once to the PDF file. + * + * Only one of @source_pattern or @source_surface is to be + * specified. Set the other to NULL. + **/ +static cairo_int_status_t +_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, + cairo_surface_t *source_surface, + const cairo_pattern_t *source_pattern, + cairo_operator_t op, + cairo_filter_t filter, + cairo_bool_t stencil_mask, + cairo_bool_t smask, + cairo_bool_t need_transp_group, + const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *smask_res, + cairo_pdf_source_surface_entry_t **pdf_source, + double *x_offset, + double *y_offset, + cairo_rectangle_int_t *source_extents) { cairo_pdf_source_surface_t src_surface; cairo_pdf_source_surface_entry_t surface_key; cairo_pdf_source_surface_entry_t *surface_entry; - cairo_status_t status; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_bool_t interpolate; + unsigned char *unique_id = NULL; + unsigned long unique_id_length = 0; + cairo_image_surface_t *image; + void *image_extra; + cairo_box_t box; + cairo_rectangle_int_t op_extents; + double x, y; + cairo_bool_t subsurface; switch (filter) { default: @@ -1153,70 +1521,201 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface, break; } - surface_key.id = source->unique_id; + x = 0; + y = 0; + if (source_pattern) { + if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern, + &image, &image_extra); + if (unlikely (status)) + return status; + source_surface = &image->base; + cairo_surface_get_device_offset (source_surface, &x, &y); + } else { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern; + source_surface = surface_pattern->surface; + } + } + if (x_offset) + *x_offset = x; + if (y_offset) + *y_offset = y; + + /* transform operation extents to pattern space */ + op_extents = *extents; + if (source_pattern) + { + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&source_pattern->matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &op_extents); + } + if (source_extents) + *source_extents = op_extents; + + surface_key.id = source_surface->unique_id; surface_key.interpolate = interpolate; + cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID, + (const unsigned char **) &surface_key.unique_id, + &surface_key.unique_id_length); _cairo_pdf_source_surface_init_key (&surface_key); surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base); if (surface_entry) { - *surface_res = surface_entry->surface_res; - *width = surface_entry->width; - *height = surface_entry->height; + if (pdf_source) + *pdf_source = surface_entry; - return CAIRO_STATUS_SUCCESS; + if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE) + _cairo_unbounded_rectangle_init (&op_extents); + + _cairo_rectangle_intersect (&op_extents, &surface_entry->extents); + _cairo_rectangle_union (&surface_entry->required_extents, &op_extents); + status = CAIRO_STATUS_SUCCESS; } - surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t)); - if (surface_entry == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (status || surface_entry) { + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); + return status; + } + if (surface_key.unique_id && surface_key.unique_id_length > 0) { + unique_id = _cairo_malloc (surface_key.unique_id_length); + if (unique_id == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + unique_id_length = surface_key.unique_id_length; + memcpy (unique_id, surface_key.unique_id, unique_id_length); + } else { + unique_id = NULL; + unique_id_length = 0; + } + + surface_entry = _cairo_malloc (sizeof (cairo_pdf_source_surface_entry_t)); + if (surface_entry == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + if (pdf_source) + *pdf_source = surface_entry; surface_entry->id = surface_key.id; + surface_entry->operator = op; surface_entry->interpolate = interpolate; + surface_entry->stencil_mask = stencil_mask; + surface_entry->smask = smask; + surface_entry->need_transp_group = need_transp_group; + surface_entry->unique_id_length = unique_id_length; + surface_entry->unique_id = unique_id; + if (smask_res) + surface_entry->smask_res = *smask_res; + else + surface_entry->smask_res.id = 0; + + status = _get_source_surface_extents (source_surface, + &surface_entry->extents, + &surface_entry->bounded, + &subsurface); + if (unlikely (status)) + goto fail2; + + if (subsurface) { + *x_offset = -surface_entry->extents.x; + *y_offset = -surface_entry->extents.y; + } + + if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE) + _cairo_unbounded_rectangle_init (&op_extents); + + _cairo_rectangle_intersect (&op_extents, &surface_entry->extents); + surface_entry->required_extents = op_extents; + _cairo_pdf_source_surface_init_key (surface_entry); src_surface.hash_entry = surface_entry; - src_surface.surface = cairo_surface_reference (source); - surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); - if (surface_entry->surface_res.id == 0) { - cairo_surface_destroy (source); - free (surface_entry); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE; + src_surface.surface = NULL; + status = _cairo_pattern_create_copy (&src_surface.raster_pattern, source_pattern); + if (unlikely (status)) + goto fail2; + + } else { + src_surface.type = CAIRO_PATTERN_TYPE_SURFACE; + src_surface.surface = cairo_surface_reference (source_surface); + src_surface.raster_pattern = NULL; } - status = _get_source_surface_size (source, &surface_entry->width, - &surface_entry->height); + surface_entry->surface_res = _cairo_pdf_surface_new_object (surface); + if (surface_entry->surface_res.id == 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } - status = _cairo_array_append (&surface->page_surfaces, &src_surface); - if (unlikely (status)) { - cairo_surface_destroy (source); - free (surface_entry); - return status; + /* Test if surface will be emitted as image or recording */ + status = _cairo_pdf_surface_emit_surface (surface, &src_surface, TRUE, &surface_entry->emit_image); + if (unlikely (status)) + goto fail3; + + if (surface_entry->bounded) { + status = _cairo_array_append (&surface->page_surfaces, &src_surface); + if (unlikely (status)) + goto fail3; + } else { + status = _cairo_array_append (&surface->doc_surfaces, &src_surface); + if (unlikely (status)) + goto fail3; } status = _cairo_hash_table_insert (surface->all_surfaces, &surface_entry->base); + if (unlikely(status)) + goto fail3; - *surface_res = surface_entry->surface_res; - *width = surface_entry->width; - *height = surface_entry->height; + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); + + return CAIRO_STATUS_SUCCESS; + +fail3: + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + cairo_pattern_destroy (src_surface.raster_pattern); + else + cairo_surface_destroy (src_surface.surface); + +fail2: + free (surface_entry); + +fail1: + if (unique_id) + free (unique_id); + + if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra); return status; } -static cairo_status_t -_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t *pattern_res, - cairo_pdf_resource_t *gstate_res) +static cairo_int_status_t +_cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_operator_t op, + const cairo_rectangle_int_t *extents, + cairo_bool_t is_shading, + cairo_pdf_resource_t *pattern_res, + cairo_pdf_resource_t *gstate_res) { cairo_pdf_pattern_t pdf_pattern; - cairo_status_t status; + cairo_int_status_t status; + + pdf_pattern.is_shading = is_shading; + pdf_pattern.operator = op; /* Solid colors are emitted into the content stream */ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { pattern_res->id = 0; gstate_res->id = 0; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); @@ -1233,9 +1732,13 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, /* gradient patterns require an smask object to implement transparency */ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + pattern->type == CAIRO_PATTERN_TYPE_RADIAL || + pattern->type == CAIRO_PATTERN_TYPE_MESH) { - if (_cairo_pattern_is_opaque (pattern, extents) == FALSE) { + double min_alpha; + + _cairo_pattern_alpha_range (pattern, &min_alpha, NULL); + if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) { pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface); if (pdf_pattern.gstate_res.id == 0) { cairo_pattern_destroy (pdf_pattern.pattern); @@ -1257,6 +1760,10 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, *pattern_res = pdf_pattern.pattern_res; *gstate_res = pdf_pattern.gstate_res; + /* If the pattern requires a gstate it will be drawn from within + * an XObject. The initial space of each XObject has an inverted + * Y-axis. */ + pdf_pattern.inverted_y_axis = pdf_pattern.gstate_res.id ? TRUE : surface->in_xobject; status = _cairo_array_append (&surface->page_patterns, &pdf_pattern); if (unlikely (status)) { @@ -1264,10 +1771,55 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, return status; } - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +/* Get BBox from extents */ +static void +_get_bbox_from_extents (const cairo_rectangle_int_t *extents, + cairo_box_double_t *bbox) +{ + bbox->p1.x = extents->x; + bbox->p1.y = extents->y; + bbox->p2.x = extents->x + extents->width; + bbox->p2.y = extents->y + extents->height; +} + +static cairo_int_status_t +_cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_operator_t op, + const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *shading_res, + cairo_pdf_resource_t *gstate_res) +{ + return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, + pattern, + op, + extents, + TRUE, + shading_res, + gstate_res); +} + +static cairo_int_status_t +_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_operator_t op, + const cairo_rectangle_int_t *extents, + cairo_pdf_resource_t *pattern_res, + cairo_pdf_resource_t *gstate_res) +{ + return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, + pattern, + op, + extents, + FALSE, + pattern_res, + gstate_res); +} + +static cairo_int_status_t _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *resource, cairo_bool_t compressed, @@ -1332,26 +1884,27 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, surface->output = output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); } + _cairo_pdf_operators_reset (&surface->pdf_operators); return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_int_status_t status; long length; if (! surface->pdf_stream.active) - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (surface->pdf_stream.compressed) { - cairo_status_t status2; + cairo_int_status_t status2; status2 = _cairo_output_stream_destroy (surface->output); - if (likely (status == CAIRO_STATUS_SUCCESS)) + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = status2; surface->output = surface->pdf_stream.old_output; @@ -1377,7 +1930,7 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) surface->pdf_stream.active = FALSE; - if (likely (status == CAIRO_STATUS_SUCCESS)) + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) status = _cairo_output_stream_get_status (surface->output); return status; @@ -1388,7 +1941,8 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, cairo_output_stream_t *mem_stream, cairo_pdf_resource_t resource, cairo_pdf_group_resources_t *resources, - cairo_bool_t is_knockout_group) + cairo_bool_t is_knockout_group, + const cairo_box_double_t *bbox) { _cairo_pdf_surface_update_object (surface, resource); @@ -1406,13 +1960,13 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, _cairo_output_stream_printf (surface->output, " /Subtype /Form\n" - " /BBox [ 0 0 %f %f ]\n" + " /BBox [ %f %f %f %f ]\n" " /Group <<\n" " /Type /Group\n" " /S /Transparency\n" + " /I true\n" " /CS /DeviceRGB\n", - surface->width, - surface->height); + bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y); if (is_knockout_group) _cairo_output_stream_printf (surface->output, @@ -1431,11 +1985,12 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, "endobj\n"); } -static cairo_status_t -_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource) +static cairo_int_status_t +_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, + const cairo_box_double_t *bbox, + cairo_pdf_resource_t *resource) { - cairo_status_t status; + cairo_int_status_t status; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); @@ -1468,29 +2023,31 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); } surface->group_stream.is_knockout = FALSE; + surface->group_stream.bbox = *bbox; return status; } -static cairo_status_t -_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface) +static cairo_int_status_t +_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, + const cairo_box_double_t *bbox) { - cairo_status_t status; + cairo_int_status_t status; - status = _cairo_pdf_surface_open_group (surface, NULL); + status = _cairo_pdf_surface_open_group (surface, bbox, NULL); if (unlikely (status)) return status; surface->group_stream.is_knockout = TRUE; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, cairo_pdf_resource_t *group) { - cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == TRUE); @@ -1513,12 +2070,13 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, surface->group_stream.mem_stream, surface->group_stream.resource, &surface->resources, - surface->group_stream.is_knockout); + surface->group_stream.is_knockout, + &surface->group_stream.bbox); if (group) *group = surface->group_stream.resource; status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; surface->group_stream.mem_stream = NULL; @@ -1527,12 +2085,14 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t -_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *resource, - cairo_bool_t is_form) +static cairo_int_status_t +_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, + const cairo_box_double_t *bbox, + cairo_pdf_resource_t *resource, + cairo_bool_t is_form, + cairo_bool_t is_group) { - cairo_status_t status; + cairo_int_status_t status; assert (surface->pdf_stream.active == FALSE); assert (surface->group_stream.active == FALSE); @@ -1542,28 +2102,52 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (is_form) { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ 0 0 %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - surface->width, - surface->height, - surface->content_resources.id); + assert (bbox != NULL); + + if (is_group) { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_content, + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /I true\n" + " /CS /DeviceRGB\n" + " >>\n" + " /Resources %d 0 R\n", + bbox->p1.x, + bbox->p1.y, + bbox->p2.x, + bbox->p2.y, + surface->content_resources.id); + } else { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_content, + " /Type /XObject\n" + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Resources %d 0 R\n", + bbox->p1.x, + bbox->p1.y, + bbox->p2.x, + bbox->p2.y, + surface->content_resources.id); + } } else { status = _cairo_pdf_surface_open_stream (surface, resource, surface->compress_content, NULL); + _cairo_output_stream_printf (surface->output, + "1 0 0 -1 0 %f cm\n", + surface->height); } if (unlikely (status)) return status; @@ -1571,14 +2155,15 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, surface->content = surface->pdf_stream.self; _cairo_output_stream_printf (surface->output, "q\n"); + _cairo_pdf_operators_reset (&surface->pdf_operators); return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_int_status_t status; assert (surface->pdf_stream.active == TRUE); assert (surface->group_stream.active == FALSE); @@ -1610,6 +2195,8 @@ _cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) cairo_hash_table_t *patterns = closure; _cairo_hash_table_remove (patterns, &surface_entry->base); + free (surface_entry->unique_id); + free (surface_entry); } @@ -1618,8 +2205,17 @@ _cairo_pdf_surface_finish (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; long offset; - cairo_pdf_resource_t info, catalog; + cairo_pdf_resource_t catalog; cairo_status_t status, status2; + int size, i; + cairo_pdf_source_surface_t doc_surface; + cairo_pdf_jbig2_global_t *global; + char *label; + + _cairo_pdf_surface_clear (surface); + + /* Emit unbounded surfaces */ + _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); status = surface->base.status; if (status == CAIRO_STATUS_SUCCESS) @@ -1627,9 +2223,9 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_pdf_surface_write_pages (surface); - info = _cairo_pdf_surface_write_info (surface); - if (info.id == 0 && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_pdf_interchange_write_document_objects (surface); + if (unlikely (status)) + return status; catalog = _cairo_pdf_surface_write_catalog (surface); if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) @@ -1645,7 +2241,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) ">>\n", surface->next_available_resource.id, catalog.id, - info.id); + surface->docinfo_res.id); _cairo_output_stream_printf (surface->output, "startxref\n" @@ -1685,7 +2281,6 @@ _cairo_pdf_surface_finish (void *abstract_surface) if (status == CAIRO_STATUS_SUCCESS) status = status2; - _cairo_pdf_surface_clear (surface); _cairo_pdf_group_resources_fini (&surface->resources); _cairo_array_fini (&surface->objects); @@ -1694,6 +2289,13 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->alpha_linear_functions); _cairo_array_fini (&surface->page_patterns); _cairo_array_fini (&surface->page_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); + 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, surface->all_surfaces); @@ -1701,21 +2303,41 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->smask_groups); _cairo_array_fini (&surface->fonts); _cairo_array_fini (&surface->knockout_group); + _cairo_array_fini (&surface->page_annots); if (surface->font_subsets) { _cairo_scaled_font_subsets_destroy (surface->font_subsets); surface->font_subsets = NULL; } + size = _cairo_array_num_elements (&surface->jbig2_global); + for (i = 0; i < size; i++) { + global = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i); + free(global->id); + if (!global->emitted) + return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING); + } + _cairo_array_fini (&surface->jbig2_global); + _cairo_array_fini (&surface->page_heights); + + size = _cairo_array_num_elements (&surface->page_labels); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->page_labels, i, &label); + free (label); + } + _cairo_array_fini (&surface->page_labels); + _cairo_surface_clipper_reset (&surface->clipper); - return status; + return _cairo_pdf_interchange_fini (surface); } static cairo_int_status_t _cairo_pdf_surface_start_page (void *abstract_surface) { cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_resource_t page; + cairo_int_status_t status; /* Document header */ if (! surface->header_emitted) { @@ -1739,6 +2361,15 @@ _cairo_pdf_surface_start_page (void *abstract_surface) } _cairo_pdf_group_resources_clear (&surface->resources); + surface->in_xobject = FALSE; + + page = _cairo_pdf_surface_new_object (surface); + if (page.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&surface->pages, &page); + if (unlikely (status)) + return status; return CAIRO_STATUS_SUCCESS; } @@ -1747,11 +2378,17 @@ static cairo_int_status_t _cairo_pdf_surface_has_fallback_images (void *abstract_surface, cairo_bool_t has_fallbacks) { - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_surface_t *surface = abstract_surface; + cairo_box_double_t bbox; surface->has_fallback_images = has_fallbacks; - status = _cairo_pdf_surface_open_content_stream (surface, NULL, has_fallbacks); + surface->in_xobject = has_fallbacks; + bbox.p1.x = 0; + bbox.p1.y = 0; + bbox.p2.x = surface->width; + bbox.p2.y = surface->height; + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks, has_fallbacks); if (unlikely (status)) return status; @@ -1764,34 +2401,160 @@ _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) return TRUE; } -/* Emit alpha channel from the image into the given data, providing - * an id that can be used to reference the resulting SMask object. - * - * In the case that the alpha channel happens to be all opaque, then - * no SMask object will be emitted and *id_ret will be set to 0. +static cairo_bool_t +_cairo_pdf_surface_requires_thumbnail_image (void *abstract_surface, + int *width, + int *height) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->thumbnail_width > 0 && surface->thumbnail_height > 0) { + *width = surface->thumbnail_width; + *height = surface->thumbnail_height; + return TRUE; + } + + return FALSE; +} + +static cairo_int_status_t +_cairo_pdf_surface_set_thumbnail_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + surface->thumbnail_image = (cairo_image_surface_t *)cairo_surface_reference(&image->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface, + const cairo_pattern_t *source, + const cairo_rectangle_int_t *extents, + cairo_pdf_source_surface_entry_t **pdf_source, + double *x_offset, + double *y_offset, + cairo_rectangle_int_t *source_extents) +{ + cairo_image_surface_t *image; + cairo_surface_t *pad_image; + void *image_extra; + cairo_int_status_t status; + int w, h; + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_pattern_t pad_pattern; + + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source, + &image, &image_extra); + if (unlikely (status)) + return status; + + pad_image = &image->base; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&source->matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + + /* Check if image needs padding to fill extents */ + w = image->width; + h = image->height; + if (_cairo_fixed_integer_ceil(box.p1.x) < 0 || + _cairo_fixed_integer_ceil(box.p1.y) < 0 || + _cairo_fixed_integer_floor(box.p2.x) > w || + _cairo_fixed_integer_floor(box.p2.y) > h) + { + pad_image = _cairo_image_surface_create_with_content (image->base.content, + rect.width, + rect.height); + if (pad_image->status) { + status = pad_image->status; + goto BAIL; + } + + _cairo_pattern_init_for_surface (&pad_pattern, &image->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_paint (pad_image, + CAIRO_OPERATOR_SOURCE, &pad_pattern.base, + NULL); + _cairo_pattern_fini (&pad_pattern.base); + if (unlikely (status)) + goto BAIL; + } + + status = _cairo_pdf_surface_add_source_surface (surface, + pad_image, + NULL, + CAIRO_OPERATOR_OVER, /* not used for images */ + source->filter, + FALSE, /* stencil mask */ + FALSE, /* smask */ + FALSE, /* need_transp_group */ + extents, + NULL, /* smask_res */ + pdf_source, + x_offset, + y_offset, + source_extents); + if (unlikely (status)) + goto BAIL; + + if (pad_image != &image->base) { + /* If using a padded image, replace _add_source_surface + * x/y_offset with padded image offset. Note: + * _add_source_surface only sets a non zero x/y_offset for + * RASTER_SOURCE patterns. _add_source_surface will always set + * x/y_offset to 0 for surfaces so we can ignore the returned + * offset and replace it with the offset required for the + * padded image */ + *x_offset = rect.x; + *y_offset = rect.y; + } + +BAIL: + if (pad_image != &image->base) + cairo_surface_destroy (pad_image); + + _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra); + + return status; +} + +/* Emit alpha channel from the image into stream_res. */ -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, cairo_image_surface_t *image, - cairo_pdf_resource_t *stream_ret) + cairo_bool_t stencil_mask, + cairo_bool_t interpolate, + cairo_pdf_resource_t *stream_res) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; char *alpha; unsigned long alpha_size; uint32_t *pixel32; uint8_t *pixel8; - int i, x, y; - cairo_bool_t opaque; - uint8_t a; + int i, x, y, bit, a; + cairo_image_transparency_t transparency; /* This is the only image format we support, which simplifies things. */ assert (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_RGB24 || image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1 ); - stream_ret->id = 0; + transparency = _cairo_image_analyze_transparency (image); + if (stencil_mask) { + assert (transparency == CAIRO_IMAGE_IS_OPAQUE || + transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA); + } else { + assert (transparency != CAIRO_IMAGE_IS_OPAQUE); + } - if (image->format == CAIRO_FORMAT_A1) { + if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA || transparency == CAIRO_IMAGE_IS_OPAQUE) { alpha_size = (image->width + 7) / 8 * image->height; alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); } else { @@ -1804,59 +2567,83 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, goto CLEANUP; } - opaque = TRUE; i = 0; for (y = 0; y < image->height; y++) { - if (image->format == CAIRO_FORMAT_ARGB32) { - pixel32 = (uint32_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel32++) { - a = (*pixel32 & 0xff000000) >> 24; - alpha[i++] = a; - if (a != 0xff) - opaque = FALSE; - } - } else if (image->format == CAIRO_FORMAT_A8){ - pixel8 = (uint8_t *) (image->data + y * image->stride); - - for (x = 0; x < image->width; x++, pixel8++) { - a = *pixel8; - alpha[i++] = a; - if (a != 0xff) - opaque = FALSE; - } - } else { /* image->format == CAIRO_FORMAT_A1 */ + if (transparency == CAIRO_IMAGE_IS_OPAQUE) { + for (x = 0; x < (image->width + 7) / 8; x++) + alpha[i++] = 0xff; + } else if (image->format == CAIRO_FORMAT_A1) { pixel8 = (uint8_t *) (image->data + y * image->stride); for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) { a = *pixel8; a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); alpha[i++] = a; - if (a != 0xff) - opaque = FALSE; } + } else { + pixel8 = (uint8_t *) (image->data + y * image->stride); + pixel32 = (uint32_t *) (image->data + y * image->stride); + bit = 7; + for (x = 0; x < image->width; x++) { + if (image->format == CAIRO_FORMAT_ARGB32) { + a = (*pixel32 & 0xff000000) >> 24; + pixel32++; + } else { + a = *pixel8; + pixel8++; + } + + if (transparency == CAIRO_IMAGE_HAS_ALPHA) { + alpha[i++] = a; + } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */ + if (bit == 7) + alpha[i] = 0; + if (a != 0) + alpha[i] |= (1 << bit); + bit--; + if (bit < 0) { + bit = 7; + i++; + } + } + } + if (bit != 7) + i++; } } - /* Bail out without emitting smask if it's all opaque. */ - if (opaque) - goto CLEANUP_ALPHA; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace /DeviceGray\n" - " /BitsPerComponent %d\n", - image->width, image->height, - image->format == CAIRO_FORMAT_A1 ? 1 : 8); + if (stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + stream_res, + TRUE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n", + image->width, image->height, + interpolate ? "true" : "false"); + } else { + status = _cairo_pdf_surface_open_stream (surface, + stream_res, + TRUE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceGray\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n", + image->width, image->height, + interpolate ? "true" : "false", + transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1); + } if (unlikely (status)) goto CLEANUP_ALPHA; - *stream_ret = surface->pdf_stream.self; _cairo_output_stream_write (surface->output, alpha, alpha_size); status = _cairo_pdf_surface_close_stream (surface); @@ -1866,36 +2653,88 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, return status; } -/* Emit image data into the given surface, providing a resource that - * can be used to reference the data in image_ret. */ -static cairo_status_t -_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image, - cairo_pdf_resource_t *image_res, - cairo_filter_t filter) +/** + * _cairo_pdf_surface_emit_image: + * @surface: the pdf surface + * @image_surf: The image to write + * @surface_entry: Contains image resource, smask resource, interpolate and stencil mask parameters. + * + * Emit an image stream using the @image_res resource and write out + * the image data from @image_surf. If @smask_res is not null, @smask_res will + * be specified as the smask for the image. Otherwise emit the an smask if + * the image is requires one. + **/ +static cairo_int_status_t +_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image_surf, + cairo_pdf_source_surface_entry_t *surface_entry) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - char *rgb; - unsigned long rgb_size; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + char *data; + unsigned long data_size; uint32_t *pixel; - int i, x, y; + int i, x, y, bit; cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ cairo_bool_t need_smask; - const char *interpolate = "true"; + cairo_image_color_t color; + cairo_image_surface_t *image; + cairo_image_transparency_t transparency; + char smask_buf[30]; - /* These are the only image formats we currently support, (which - * makes things a lot simpler here). This is enforced through - * _cairo_pdf_surface_analyze_operation which only accept source surfaces of - * CONTENT_COLOR or CONTENT_COLOR_ALPHA. - */ - assert (image->format == CAIRO_FORMAT_RGB24 || - image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1); + image = image_surf; + if (image->format != CAIRO_FORMAT_RGB24 && + image->format != CAIRO_FORMAT_ARGB32 && + image->format != CAIRO_FORMAT_A8 && + image->format != CAIRO_FORMAT_A1) + { + cairo_surface_t *surf; + cairo_surface_pattern_t pattern; - rgb_size = image->height * image->width * 3; - rgb = _cairo_malloc_abc (image->width, image->height, 3); - if (unlikely (rgb == NULL)) { + surf = _cairo_image_surface_create_with_content (image_surf->base.content, + image_surf->width, + image_surf->height); + image = (cairo_image_surface_t *) surf; + if (surf->status) { + status = surf->status; + goto CLEANUP; + } + + _cairo_pattern_init_for_surface (&pattern, &image_surf->base); + status = _cairo_surface_paint (surf, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto CLEANUP; + } + + if (surface_entry->smask || surface_entry->stencil_mask) { + return _cairo_pdf_surface_emit_smask (surface, image, + surface_entry->stencil_mask, + surface_entry->interpolate, + &surface_entry->surface_res); + } + + color = _cairo_image_analyze_color (image); + switch (color) { + default: + case CAIRO_IMAGE_UNKNOWN_COLOR: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_COLOR: + data_size = image->height * image->width * 3; + data = _cairo_malloc_abc (image->width, image->height, 3); + break; + + case CAIRO_IMAGE_IS_GRAYSCALE: + data_size = image->height * image->width; + data = _cairo_malloc_ab (image->width, image->height); + break; + case CAIRO_IMAGE_IS_MONOCHROME: + data_size = (image->width + 7) / 8 * image->height; + data = _cairo_malloc_ab ((image->width+7) / 8, image->height); + break; + } + if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } @@ -1904,7 +2743,10 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, for (y = 0; y < image->height; y++) { pixel = (uint32_t *) (image->data + y * image->stride); + bit = 7; for (x = 0; x < image->width; x++, pixel++) { + int r, g, b; + /* XXX: We're un-premultiplying alpha here. My reading of the PDF * specification suggests that we should be able to avoid having * to do this by filling in the SMask's Matte dictionary @@ -1914,98 +2756,279 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, uint8_t a; a = (*pixel & 0xff000000) >> 24; if (a == 0) { - rgb[i++] = 0; - rgb[i++] = 0; - rgb[i++] = 0; + r = g = b = 0; } else { - rgb[i++] = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; - rgb[i++] = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; - rgb[i++] = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; + r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; + g = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; + b = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; } } else if (image->format == CAIRO_FORMAT_RGB24) { - rgb[i++] = (*pixel & 0x00ff0000) >> 16; - rgb[i++] = (*pixel & 0x0000ff00) >> 8; - rgb[i++] = (*pixel & 0x000000ff) >> 0; + r = (*pixel & 0x00ff0000) >> 16; + g = (*pixel & 0x0000ff00) >> 8; + b = (*pixel & 0x000000ff) >> 0; } else { - rgb[i++] = 0; - rgb[i++] = 0; - rgb[i++] = 0; + r = g = b = 0; + } + + switch (color) { + case CAIRO_IMAGE_IS_COLOR: + case CAIRO_IMAGE_UNKNOWN_COLOR: + data[i++] = r; + data[i++] = g; + data[i++] = b; + break; + + case CAIRO_IMAGE_IS_GRAYSCALE: + data[i++] = r; + break; + + case CAIRO_IMAGE_IS_MONOCHROME: + if (bit == 7) + data[i] = 0; + if (r != 0) + data[i] |= (1 << bit); + bit--; + if (bit < 0) { + bit = 7; + i++; + } + break; + } + } + if (bit != 7) + i++; + } + + if (surface_entry->smask_res.id != 0) { + need_smask = TRUE; + smask = surface_entry->smask_res; + } else { + need_smask = FALSE; + if (image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_A8 || + image->format == CAIRO_FORMAT_A1) + { + transparency = _cairo_image_analyze_transparency (image); + if (transparency != CAIRO_IMAGE_IS_OPAQUE) { + need_smask = TRUE; + smask = _cairo_pdf_surface_new_object (surface); + if (smask.id == 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_RGB; + } + + status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, surface_entry->interpolate, &smask); + if (unlikely (status)) + goto CLEANUP_RGB; } } } - need_smask = FALSE; - if (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1) { - status = _cairo_pdf_surface_emit_smask (surface, image, &smask); - if (unlikely (status)) - goto CLEANUP_RGB; - - if (smask.id) - need_smask = TRUE; - } - - switch (filter) { - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = "true"; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = "false"; - break; - } - -#define IMAGE_DICTIONARY " /Type /XObject\n" \ - " /Subtype /Image\n" \ - " /Width %d\n" \ - " /Height %d\n" \ - " /ColorSpace /DeviceRGB\n" \ - " /Interpolate %s\n" \ - " /BitsPerComponent 8\n" - if (need_smask) - status = _cairo_pdf_surface_open_stream (surface, - image_res, - TRUE, - IMAGE_DICTIONARY - " /SMask %d 0 R\n", - image->width, image->height, - interpolate, - smask.id); + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", smask.id); else - status = _cairo_pdf_surface_open_stream (surface, - image_res, - TRUE, - IMAGE_DICTIONARY, - image->width, image->height, - interpolate); + smask_buf[0] = 0; + + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + TRUE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace %s\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + "%s", + image->width, + image->height, + color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", + surface_entry->interpolate ? "true" : "false", + color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8, + smask_buf); if (unlikely (status)) goto CLEANUP_RGB; #undef IMAGE_DICTIONARY - _cairo_output_stream_write (surface->output, rgb, rgb_size); + _cairo_output_stream_write (surface->output, data, data_size); status = _cairo_pdf_surface_close_stream (surface); CLEANUP_RGB: - free (rgb); + free (data); CLEANUP: + if (image != image_surf) + cairo_surface_destroy (&image->base); + return status; } static cairo_int_status_t -_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_resource_t res) +_cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t *surface, + const unsigned char *global_id, + unsigned long global_id_length, + cairo_pdf_jbig2_global_t **entry) { - cairo_status_t status; + cairo_pdf_jbig2_global_t global; + int size, i; + cairo_int_status_t status; + + size = _cairo_array_num_elements (&surface->jbig2_global); + for (i = 0; i < size; i++) { + *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i); + if ((*entry)->id && global_id && (*entry)->id_length == global_id_length + && memcmp((*entry)->id, global_id, global_id_length) == 0) { + return CAIRO_STATUS_SUCCESS; + } + } + + global.id = _cairo_malloc (global_id_length); + if (unlikely (global.id == NULL)) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (global.id, global_id, global_id_length); + global.id_length = global_id_length; + global.res = _cairo_pdf_surface_new_object (surface); + if (global.res.id == 0) { + free(global.id); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + global.emitted = FALSE; + status = _cairo_array_append (&surface->jbig2_global, &global); + if (unlikely(status)) + return status; + + size = _cairo_array_num_elements (&surface->jbig2_global); + *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, size - 1); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry, + cairo_bool_t test) +{ + cairo_int_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; + const unsigned char *global_id; + unsigned long global_id_length; + const unsigned char *global_data; + unsigned long global_data_length; + cairo_pdf_jbig2_global_t *global_entry = NULL; /* hide compiler warning */ + char smask_buf[30]; + char decode_parms_buf[100]; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jbig2_info (&info, mime_data, mime_data_length); + if (status) + return status; + + /* At this point we know emitting jbig2 will succeed. */ + if (test) + return CAIRO_STATUS_SUCCESS; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, + &global_id, &global_id_length); + if (global_id && global_id_length > 0) { + status = _cairo_pdf_surface_lookup_jbig2_global (surface, global_id, global_id_length, &global_entry); + if (unlikely(status)) + return status; + + if (!global_entry->emitted) { + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL, + &global_data, &global_data_length); + if (global_data) { + status = _cairo_pdf_surface_open_stream (surface, &global_entry->res, FALSE, NULL); + if (unlikely(status)) + return status; + + _cairo_output_stream_write (surface->output, global_data, global_data_length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely(status)) + return status; + + global_entry->emitted = TRUE; + } + } + + snprintf(decode_parms_buf, sizeof(decode_parms_buf), + " /DecodeParms << /JBIG2Globals %d 0 R >>\n", global_entry->res.id); + } else { + decode_parms_buf[0] = 0; + } + + if (surface_entry->smask_res.id) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); + else + smask_buf[0] = 0; + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /JPXDecode\n" + "%s", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false", + decode_parms_buf); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + "%s" + " /Filter /JBIG2Decode\n" + "%s", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false", + smask_buf, + decode_parms_buf); + } + if (unlikely(status)) + return status; + + _cairo_output_stream_write (surface->output, mime_data, mime_data_length); + status = _cairo_pdf_surface_close_stream (surface); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry, + cairo_bool_t test) +{ + cairo_int_status_t status; + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t info; + char smask_buf[30]; if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2019,16 +3042,53 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, if (status) return status; - status = _cairo_pdf_surface_open_stream (surface, - &res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /Filter /JPXDecode\n", - info.width, - info.height); + if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface_entry->stencil_mask) && info.bits_per_component != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface_entry->smask_res.id) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); + else + smask_buf[0] = 0; + + /* At this point we know emitting jpx will succeed. */ + if (test) + return CAIRO_STATUS_SUCCESS; + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /JPXDecode\n", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false"); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + "%s" + " /Filter /JPXDecode\n", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false", + smask_buf); + } if (status) return status; @@ -2039,14 +3099,17 @@ _cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, } static cairo_int_status_t -_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_resource_t res) +_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry, + cairo_bool_t test) { - cairo_status_t status; + cairo_int_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; + const char *colorspace; + char smask_buf[30]; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); @@ -2059,23 +3122,71 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - if (info.num_components != 1 && info.num_components != 3) + if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1) return CAIRO_INT_STATUS_UNSUPPORTED; - status = _cairo_pdf_surface_open_stream (surface, - &res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace %s\n" - " /BitsPerComponent %d\n" - " /Filter /DCTDecode\n", - info.width, - info.height, - info.num_components == 1 ? "/DeviceGray" : "/DeviceRGB", - info.bits_per_component); + if ((surface_entry->stencil_mask) && info.bits_per_component != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + switch (info.num_components) { + case 1: + colorspace = "/DeviceGray"; + break; + case 3: + colorspace = "/DeviceRGB"; + break; + case 4: + colorspace = "/DeviceCMYK"; + break; + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* At this point we know emitting jpeg will succeed. */ + if (test) + return CAIRO_STATUS_SUCCESS; + + if (surface_entry->smask_res.id) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); + else + smask_buf[0] = 0; + + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /DCTDecode\n", + info.width, + info.height, + surface_entry->interpolate ? "true" : "false"); + } else { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace %s\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + "%s" + " /Filter /DCTDecode\n", + info.width, + info.height, + colorspace, + surface_entry->interpolate ? "true" : "false", + info.bits_per_component, + smask_buf); + } if (unlikely (status)) return status; @@ -2085,325 +3196,396 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t -_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_resource_t resource, - cairo_bool_t interpolate) +static cairo_int_status_t +_cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface, + cairo_surface_t *source, + cairo_pdf_source_surface_entry_t *surface_entry, + cairo_bool_t test) { - cairo_image_surface_t *image; - void *image_extra; cairo_status_t status; + const unsigned char *ccitt_data; + unsigned long ccitt_data_len; + const unsigned char *ccitt_params_string; + unsigned long ccitt_params_string_len; + char *params, *p, *end; + cairo_ccitt_params_t ccitt_params; + char buf[300]; - status = _cairo_pdf_surface_emit_jpx_image (surface, source, resource); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX, + &ccitt_data, &ccitt_data_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; - status = _cairo_pdf_surface_emit_jpeg_image (surface, source, resource); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + &ccitt_params_string, &ccitt_params_string_len); + if (unlikely (source->status)) + return source->status; + if (ccitt_params_string == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; - status = _cairo_surface_acquire_source_image (source, &image, &image_extra); - if (unlikely (status)) - return status; + /* 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; + status = _cairo_tag_parse_ccitt_params (params, &ccitt_params); + if (unlikely(status)) + return source->status; - status = _cairo_pdf_surface_emit_image (surface, image, - &resource, interpolate); - if (unlikely (status)) - goto BAIL; + free (params); -BAIL: - _cairo_surface_release_source_image (source, image, image_extra); + /* At this point we know emitting jbig2 will succeed. */ + if (test) + return CAIRO_STATUS_SUCCESS; - return status; -} + p = buf; + *p = 0; + end = buf + sizeof(buf) - 1; + p += snprintf (p, end - p, "/Columns %d /Rows %d /K %d", + ccitt_params.columns, + ccitt_params.rows, + ccitt_params.k); + if (ccitt_params.end_of_line) + p += snprintf (p, end - p, " /EndOfLine true"); -static cairo_status_t -_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern, - cairo_pdf_resource_t *resource, - int *width, - int *height, - int *origin_x, - int *origin_y) -{ - cairo_image_surface_t *image; - cairo_surface_t *pad_image; - void *image_extra; - cairo_status_t status; - cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; - int x = 0; - int y = 0; - cairo_bool_t interpolate; + if (ccitt_params.encoded_byte_align) + p += snprintf (p, end - p, " /EncodedByteAlign true"); - status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); - if (unlikely (status)) - return status; + if (!ccitt_params.end_of_block) + p += snprintf (p, end - p, " /EndOfBlock false"); - pad_image = &image->base; - if (pattern->base.extend == CAIRO_EXTEND_PAD) { - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_surface_pattern_t pad_pattern; + if (ccitt_params.black_is_1) + p += snprintf (p, end - p, " /BlackIs1 true"); - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, &pdf_pattern->extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - x = -rect.x; - y = -rect.y; - - pad_image = _cairo_image_surface_create_with_content (pattern->surface->content, - rect.width, - rect.height); - if (pad_image->status) { - status = pad_image->status; - goto BAIL; - } - - _cairo_pattern_init_for_surface (&pad_pattern, &image->base); - cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); - pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pad_pattern.base, - NULL, - pad_image, - 0, 0, - 0, 0, - 0, 0, - rect.width, - rect.height, - NULL); - _cairo_pattern_fini (&pad_pattern.base); - if (unlikely (status)) - goto BAIL; + if (ccitt_params.damaged_rows_before_error > 0) { + p += snprintf (p, end - p, " /DamagedRowsBeforeError %d", + ccitt_params.damaged_rows_before_error); } - switch (pdf_pattern->pattern->filter) { - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = TRUE; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = FALSE; - break; - } - - *resource = _cairo_pdf_surface_new_object (surface); - if (resource->id == 0) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - - status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image, - resource, interpolate); - if (unlikely (status)) - goto BAIL; - - *width = ((cairo_image_surface_t *)pad_image)->width; - *height = ((cairo_image_surface_t *)pad_image)->height; - *origin_x = x; - *origin_y = y; - -BAIL: - if (pad_image != &image->base) - cairo_surface_destroy (pad_image); - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - - -static cairo_status_t -_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *recording_surface, - cairo_pdf_resource_t resource) -{ - double old_width, old_height; - cairo_paginated_mode_t old_paginated_mode; - cairo_rectangle_int_t recording_extents; - cairo_bool_t is_bounded; - cairo_status_t status; - int alpha = 0; - - is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents); - assert (is_bounded); - - old_width = surface->width; - old_height = surface->height; - old_paginated_mode = surface->paginated_mode; - - _cairo_pdf_surface_set_size_internal (surface, - recording_extents.width, - recording_extents.height); - /* Patterns are emitted after fallback images. The paginated mode - * needs to be set to _RENDER while the recording surface is replayed - * back to this surface. - */ - surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; - _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); - if (unlikely (status)) - return status; - - if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", - alpha, - surface->width, - surface->height); - } - - status = _cairo_recording_surface_replay_region (recording_surface, - NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - surface->paginated_mode = old_paginated_mode; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_recording_subsurface (cairo_pdf_surface_t *surface, - cairo_surface_t *recording_surface, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t resource) -{ - double old_width, old_height; - cairo_paginated_mode_t old_paginated_mode; - cairo_status_t status; - int alpha = 0; - - old_width = surface->width; - old_height = surface->height; - old_paginated_mode = surface->paginated_mode; - - _cairo_pdf_surface_set_size_internal (surface, - extents->width, - extents->height); - /* Patterns are emitted after fallback images. The paginated mode - * needs to be set to _RENDER while the recording surface is replayed - * back to this surface. - */ - surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; - _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE); - if (unlikely (status)) - return status; - - if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n", - alpha, - surface->width, - surface->height); - } - - status = _cairo_recording_surface_replay_region (recording_surface, - extents, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - surface->paginated_mode = old_paginated_mode; - - return status; -} - -static cairo_status_t -_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, - cairo_pdf_source_surface_t *src_surface) -{ - if (src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (src_surface->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) src_surface->surface; - return _cairo_pdf_surface_emit_recording_subsurface (surface, - sub->target, - &sub->extents, - src_surface->hash_entry->surface_res); - } else { - return _cairo_pdf_surface_emit_recording_surface (surface, - src_surface->surface, - src_surface->hash_entry->surface_res); - } + if (surface_entry->stencil_mask) { + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /ImageMask true\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [1 0]\n" + " /Filter /CCITTFaxDecode\n" + " /DecodeParms << %s >> ", + ccitt_params.columns, + ccitt_params.rows, + surface_entry->interpolate ? "true" : "false", + buf); } else { - return _cairo_pdf_surface_emit_image_surface (surface, - src_surface->surface, - src_surface->hash_entry->surface_res, - src_surface->hash_entry->interpolate); + status = _cairo_pdf_surface_open_stream (surface, + &surface_entry->surface_res, + FALSE, + " /Type /XObject\n" + " /Subtype /Image\n" + " /Width %d\n" + " /Height %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + " /Filter /CCITTFaxDecode\n" + " /DecodeParms << %s >> ", + ccitt_params.columns, + ccitt_params.rows, + surface_entry->interpolate ? "true" : "false", + buf); } + if (unlikely (status)) + return status; + + _cairo_output_stream_write (surface->output, ccitt_data, ccitt_data_len); + status = _cairo_pdf_surface_close_stream (surface); + + return status; } -static cairo_status_t +static cairo_int_status_t +_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, + cairo_pdf_source_surface_t *pdf_source) +{ + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; + cairo_paginated_mode_t old_paginated_mode; + cairo_surface_clipper_t old_clipper; + cairo_bool_t old_in_xobject; + cairo_box_double_t bbox; + cairo_int_status_t status; + int alpha = 0; + cairo_surface_t *free_me = NULL; + cairo_surface_t *source; + const cairo_rectangle_int_t *extents; + cairo_bool_t is_subsurface; + cairo_bool_t transparency_group; + cairo_recording_surface_t *recording; + + assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE); + + if (pdf_source->hash_entry->bounded) { + extents = &pdf_source->hash_entry->extents; + } else { + extents = &pdf_source->hash_entry->required_extents; + } + + is_subsurface = FALSE; + source = pdf_source->surface; + if (_cairo_surface_is_snapshot (source)) + free_me = source = _cairo_surface_snapshot_get_target (source); + + if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + + source = sub->target; + extents = &sub->extents; + is_subsurface = TRUE; + } + + assert (source->type == CAIRO_SURFACE_TYPE_RECORDING); + recording = (cairo_recording_surface_t *) source; + + old_in_xobject = surface->in_xobject; + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; + old_paginated_mode = surface->paginated_mode; + old_clipper = surface->clipper; + surface->surface_extents = *extents; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_pdf_surface_clipper_intersect_clip_path); + + _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->in_xobject = TRUE; + surface->surface_extents = *extents; + surface->surface_bounded = TRUE; + + /* Patterns are emitted after fallback images. The paginated mode + * needs to be set to _RENDER while the recording surface is replayed + * back to this surface. + */ + surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; + _cairo_pdf_group_resources_clear (&surface->resources); + _get_bbox_from_extents (extents, &bbox); + + /* We can optimize away the transparency group allowing the viewer + * to replay the group in place when: + * - ca/CA when painting this groups is 1.0 (need_transp_group is FALSE), + * - all operators are OVER, and + * - the recording contains only opaque and/or clear alpha. + */ + transparency_group = pdf_source->hash_entry->need_transp_group || + !(pdf_source->hash_entry->operator == CAIRO_OPERATOR_OVER && + _cairo_recording_surface_has_only_bilevel_alpha (recording) && + _cairo_recording_surface_has_only_op_over (recording)); + + status = _cairo_pdf_surface_open_content_stream (surface, + &bbox, + &pdf_source->hash_entry->surface_res, + TRUE, + transparency_group); + if (unlikely (status)) + goto err; + + if (source->content == CAIRO_CONTENT_COLOR) { + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + goto err; + + _cairo_output_stream_printf (surface->output, + "q /a%d gs 0 0 0 rg %d %d %d %d re f Q\n", + alpha, + extents->x, + extents->y, + extents->width, + extents->height); + } + + status = _cairo_recording_surface_replay_region (source, + is_subsurface ? extents : NULL, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + goto err; + + status = _cairo_pdf_surface_close_content_stream (surface); + + _cairo_surface_clipper_reset (&surface->clipper); + surface->clipper = old_clipper; + _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->in_xobject = old_in_xobject; + surface->paginated_mode = old_paginated_mode; + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; + +err: + cairo_surface_destroy (free_me); + return status; +} + +/** + * _cairo_pdf_surface_emit_surface: + * @surface: [in] the pdf surface + * @source: [in] #cairo_pdf_source_surface_t containing the surface to write + * @test: [in] if true, test what type of surface will be emitted. + * @is_image: [out] if @test is true, returns TRUE if the surface will be emitted + * as an Image XObject. + * + * If @test is FALSE, emit @src_surface as an XObject. + * If @test is TRUE, don't emit anything. Set @is_image based on the output that would be emitted. + **/ +static cairo_int_status_t +_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, + cairo_pdf_source_surface_t *source, + cairo_bool_t test, + cairo_bool_t *is_image) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_int_status_t status; + + /* Try all the supported mime types and recording type, falling through + * each option if unsupported */ + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = _cairo_pdf_surface_emit_jbig2_image (surface, + source->surface, + source->hash_entry, + test); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *is_image = TRUE; + return status; + } + + status = _cairo_pdf_surface_emit_jpx_image (surface, + source->surface, + source->hash_entry, + test); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *is_image = TRUE; + return status; + } + + status = _cairo_pdf_surface_emit_jpeg_image (surface, + source->surface, + source->hash_entry, + test); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *is_image = TRUE; + return status; + } + + status = _cairo_pdf_surface_emit_ccitt_image (surface, + source->surface, + source->hash_entry, + test); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *is_image = TRUE; + return status; + } + + if (source->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (test) { + *is_image = FALSE; + return CAIRO_INT_STATUS_SUCCESS; + } else { + return _cairo_pdf_surface_emit_recording_surface (surface, source); + } + } + } + + /* The only option left is to emit as an image surface */ + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra); + } else { + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, + source->raster_pattern, + &image, + &image_extra); + } + if (unlikely (status)) + return status; + + if (test) { + *is_image = TRUE; + } else { + status = _cairo_pdf_surface_emit_image (surface, + image, + source->hash_entry); + } + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + _cairo_surface_release_source_image (source->surface, image, image_extra); + } else { + _cairo_pdf_surface_release_source_image_from_pattern (surface, + source->raster_pattern, + image, + image_extra); + } + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { - cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern; - cairo_status_t status; - cairo_pdf_resource_t pattern_resource = {0}; + cairo_pattern_t *pattern = pdf_pattern->pattern; + cairo_int_status_t status; cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base); + cairo_extend_t extend = cairo_pattern_get_extend (pattern); double xstep, ystep; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ - int origin_x = 0; /* squelch bogus compiler warning */ - int origin_y = 0; /* squelch bogus compiler warning */ - int bbox_x, bbox_y; - char draw_surface[200]; + cairo_rectangle_int_t pattern_extents; + double x_offset; + double y_offset; + char draw_surface[50]; + char draw_surface2[200]; + cairo_box_double_t bbox; + cairo_matrix_t mat; + cairo_pdf_source_surface_entry_t *pdf_source; + cairo_rectangle_int_t op_extents; - if (pattern->base.extend == CAIRO_EXTEND_PAD && - pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) - { - status = _cairo_pdf_surface_emit_padded_image_surface (surface, - pdf_pattern, - &pattern_resource, - &pattern_width, - &pattern_height, - &origin_x, - &origin_y); - } - else - { + assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); + if (pattern->extend == CAIRO_EXTEND_PAD) { + status = _cairo_pdf_surface_add_padded_image_surface (surface, + pattern, + &pdf_pattern->extents, + &pdf_source, + &x_offset, + &y_offset, + &op_extents); + } else { status = _cairo_pdf_surface_add_source_surface (surface, - pattern->surface, - pdf_pattern->pattern->filter, - &pattern_resource, - &pattern_width, - &pattern_height); + NULL, + pattern, + pdf_pattern->operator, + pattern->filter, + FALSE, /* stencil mask */ + FALSE, /* smask */ + FALSE, /* need_transp_group */ + &pdf_pattern->extents, + NULL, /* smask_res */ + &pdf_source, + &x_offset, + &y_offset, + &op_extents); } if (unlikely (status)) return status; - bbox_x = pattern_width; - bbox_y = pattern_height; + pattern_extents = pdf_source->extents; + if (!pdf_source->bounded) + { + extend = CAIRO_EXTEND_NONE; + _cairo_rectangle_intersect (&pattern_extents, &op_extents); + } + switch (extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: @@ -2421,8 +3603,9 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, * repeat visibly. */ double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; + _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); @@ -2431,21 +3614,23 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); + pattern_extents.width + pattern_extents.height); } break; case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; + case CAIRO_EXTEND_REFLECT: - bbox_x = pattern_width*2; - bbox_y = pattern_height*2; - xstep = bbox_x; - ystep = bbox_y; + pattern_extents.width *= 2; + pattern_extents.height *= 2; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; - /* All the rest (if any) should have been analyzed away, so this - * case should be unreachable. */ + + /* All the rest (if any) should have been analyzed away, so this + * case should be unreachable. */ default: ASSERT_NOT_REACHED; xstep = 0; @@ -2479,66 +3664,94 @@ _cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, * have to scale it up by the image width and height to fill our * pattern cell. */ - cairo_p2d = pattern->base.matrix; + cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf); - cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y); - cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + if (pdf_pattern->inverted_y_axis) + cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); + else + cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); + cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &mat); + cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); + if (pdf_source->emit_image) { + cairo_matrix_translate (&pdf_p2d, 0.0, pdf_source->extents.height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); + } + + _get_bbox_from_extents (&pattern_extents, &bbox); _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); status = _cairo_pdf_surface_open_stream (surface, &pdf_pattern->pattern_res, FALSE, " /PatternType 1\n" - " /BBox [0 0 %d %d]\n" + " /BBox [ %f %f %f %f ]\n" " /XStep %f\n" " /YStep %f\n" " /TilingType 1\n" " /PaintType 1\n" " /Matrix [ %f %f %f %f %f %f ]\n" " /Resources << /XObject << /x%d %d 0 R >> >>\n", - bbox_x, bbox_y, + bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y, xstep, ystep, pdf_p2d.xx, pdf_p2d.yx, pdf_p2d.xy, pdf_p2d.yy, pdf_p2d.x0, pdf_p2d.y0, - pattern_resource.id, - pattern_resource.id); + pdf_source->surface_res.id, + pdf_source->surface_res.id); if (unlikely (status)) return status; - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - snprintf(draw_surface, - sizeof (draw_surface), - "/x%d Do\n", - pattern_resource.id); - } else { + if (pdf_source->emit_image) { snprintf(draw_surface, sizeof (draw_surface), "q %d 0 0 %d 0 0 cm /x%d Do Q", - pattern_width, - pattern_height, - pattern_resource.id); + pdf_source->extents.width, + pdf_source->extents.height, + pdf_source->surface_res.id); + } else { + snprintf(draw_surface, + sizeof (draw_surface), + "/x%d Do", + pdf_source->surface_res.id); } if (extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->output, - "q 0 0 %d %d re W n %s Q\n" - "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n" - "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n" - "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n", - pattern_width, pattern_height, - draw_surface, - pattern_width*2, pattern_width, pattern_height, - draw_surface, - pattern_height*2, pattern_width, pattern_height, - draw_surface, - pattern_width*2, pattern_height*2, pattern_width, pattern_height, - draw_surface); + cairo_rectangle_int_t p_extents = pdf_source->extents; + snprintf(draw_surface2, + sizeof (draw_surface2), + "%d %d %d %d re W n %s", + p_extents.x, p_extents.y, + p_extents.width, p_extents.height, + draw_surface); + + _cairo_output_stream_printf (surface->output, "q %s Q\n", draw_surface2); + + cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); + cairo_matrix_scale (&mat, -1, 1); + cairo_matrix_translate (&mat, -2*p_extents.width, 0); + cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); + _cairo_output_stream_printf (surface->output, "q "); + _cairo_output_stream_print_matrix (surface->output, &mat); + _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); + + cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, 0, -2*p_extents.height); + cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); + _cairo_output_stream_printf (surface->output, "q "); + _cairo_output_stream_print_matrix (surface->output, &mat); + _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); + + cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); + cairo_matrix_scale (&mat, -1, -1); + cairo_matrix_translate (&mat, -2*p_extents.width, -2*p_extents.height); + cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); + _cairo_output_stream_printf (surface->output, "q "); + _cairo_output_stream_print_matrix (surface->output, &mat); + _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); } else { _cairo_output_stream_printf (surface->output, " %s \n", @@ -2558,7 +3771,7 @@ typedef struct _cairo_pdf_color_stop { cairo_pdf_resource_t resource; } cairo_pdf_color_stop_t; -static cairo_status_t +static cairo_int_status_t cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2, @@ -2567,7 +3780,7 @@ cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, int num_elems, i; cairo_pdf_rgb_linear_function_t elem; cairo_pdf_resource_t res; - cairo_status_t status; + cairo_int_status_t status; num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); for (i = 0; i < num_elems; i++) { @@ -2611,7 +3824,7 @@ cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, cairo_pdf_color_stop_t *stop1, cairo_pdf_color_stop_t *stop2, @@ -2620,7 +3833,7 @@ cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, int num_elems, i; cairo_pdf_alpha_linear_function_t elem; cairo_pdf_resource_t res; - cairo_status_t status; + cairo_int_status_t status; num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); for (i = 0; i < num_elems; i++) { @@ -2660,7 +3873,7 @@ cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, unsigned int n_stops, cairo_pdf_color_stop_t *stops, @@ -2669,7 +3882,7 @@ _cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, { cairo_pdf_resource_t res; unsigned int i; - cairo_status_t status; + cairo_int_status_t status; /* emit linear gradients between pairs of subsequent stops... */ for (i = 0; i < n_stops-1; i++) { @@ -2751,7 +3964,7 @@ calc_gradient_color (cairo_pdf_color_stop_t *new_stop, #define COLOR_STOP_EPSILON 1e-6 -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_pdf_resource_t *color_function, @@ -2761,7 +3974,7 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, unsigned int n_stops; unsigned int i; cairo_bool_t emit_alpha = FALSE; - cairo_status_t status; + cairo_int_status_t status; color_function->id = 0; alpha_function->id = 0; @@ -2808,7 +4021,50 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, stops[n_stops-1].offset = 1.0; } - if (n_stops <= 2) { + if (stops[0].offset == stops[n_stops - 1].offset) { + /* + * The first and the last stops have the same offset, but we + * don't want a function with an empty domain, because that + * would provoke underdefined behaviour from rasterisers. + * This can only happen with EXTEND_PAD, because EXTEND_NONE + * is optimised into a clear pattern in cairo-gstate, and + * REFLECT/REPEAT are always transformed to have the first + * stop at t=0 and the last stop at t=1. Thus we want a step + * function going from the first color to the last one. + * + * This can be accomplished by stitching three functions: + * - a constant first color function, + * - a step from the first color to the last color (with empty domain) + * - a constant last color function + */ + cairo_pdf_color_stop_t pad_stops[4]; + + assert (pattern->base.extend == CAIRO_EXTEND_PAD); + + pad_stops[0] = pad_stops[1] = stops[0]; + pad_stops[2] = pad_stops[3] = stops[n_stops - 1]; + + pad_stops[0].offset = 0; + pad_stops[3].offset = 1; + + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + 4, + pad_stops, + FALSE, + color_function); + if (unlikely (status)) + goto BAIL; + + if (emit_alpha) { + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + 4, + pad_stops, + TRUE, + alpha_function); + if (unlikely (status)) + goto BAIL; + } + } else if (n_stops == 2) { /* no need for stitched function */ status = cairo_pdf_surface_emit_rgb_linear_function (surface, &stops[0], @@ -2852,7 +4108,7 @@ BAIL: return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern, cairo_pdf_resource_t *function, @@ -2913,53 +4169,97 @@ _cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern, cairo_pdf_resource_t gstate_resource, cairo_pdf_resource_t gradient_mask) { cairo_pdf_resource_t smask_resource; - cairo_status_t status; + cairo_int_status_t status; + char buf[100]; + double x1, y1, x2, y2; + if (pdf_pattern->is_shading) { + snprintf(buf, sizeof(buf), + " /Shading\n" + " << /sh%d %d 0 R >>\n", + gradient_mask.id, + gradient_mask.id); + } else { + snprintf(buf, sizeof(buf), + " /Pattern\n" + " << /p%d %d 0 R >>\n", + gradient_mask.id, + gradient_mask.id); + } + + if (pdf_pattern->is_shading) { + cairo_box_t box; + + /* When emitting a shading operator we are in cairo pattern + * coordinates. _cairo_pdf_surface_paint_gradient has set the + * ctm to the pattern matrix (including the conversion from + * pdf to cairo coordinates) */ + _cairo_box_from_rectangle (&box, &pdf_pattern->extents); + _cairo_box_to_doubles (&box, &x1, &y1, &x2, &y2); + _cairo_matrix_transform_bounding_box (&pdf_pattern->pattern->matrix, &x1, &y1, &x2, &y2, NULL); + } else { + cairo_box_double_t box; + + /* When emitting a shading pattern we are in pdf page + * coordinates. The color and alpha shading patterns painted + * in the XObject below contain the cairo pattern to pdf page + * matrix in the /Matrix entry of the pattern. */ + _get_bbox_from_extents (&pdf_pattern->extents, &box); + x1 = box.p1.x; + y1 = box.p1.y; + x2 = box.p2.x; + y2 = box.p2.y; + } status = _cairo_pdf_surface_open_stream (surface, NULL, surface->compress_content, " /Type /XObject\n" " /Subtype /Form\n" " /FormType 1\n" - " /BBox [ 0 0 %f %f ]\n" + " /BBox [ %f %f %f %f ]\n" " /Resources\n" " << /ExtGState\n" " << /a0 << /ca 1 /CA 1 >>" " >>\n" - " /Pattern\n" - " << /p%d %d 0 R >>\n" + "%s" " >>\n" " /Group\n" " << /Type /Group\n" " /S /Transparency\n" + " /I true\n" " /CS /DeviceGray\n" " >>\n", - surface->width, - surface->height, - gradient_mask.id, - gradient_mask.id); + x1,y1,x2,y2, + buf); if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->output, - "q\n" - "/a0 gs\n" - "/Pattern cs /p%d scn\n" - "0 0 %f %f re\n" - "f\n" - "Q\n", - gradient_mask.id, - surface->width, - surface->height); + if (pdf_pattern->is_shading) { + _cairo_output_stream_printf (surface->output, + "/a0 gs /sh%d sh\n", + gradient_mask.id); + } else { + _cairo_output_stream_printf (surface->output, + "q\n" + "/a0 gs\n" + "/Pattern cs /p%d scn\n" + "0 0 %f %f re\n" + "f\n" + "Q\n", + gradient_mask.id, + surface->width, + surface->height); + } - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) return status; smask_resource = _cairo_pdf_surface_new_object (surface); @@ -2994,139 +4294,57 @@ cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t -_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) +static void +_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface, + const cairo_pdf_pattern_t *pdf_pattern, + cairo_pdf_resource_t pattern_resource, + const cairo_matrix_t *pat_to_pdf, + const cairo_circle_double_t*start, + const cairo_circle_double_t*end, + const double *domain, + const char *colorspace, + cairo_pdf_resource_t color_function) { - cairo_linear_pattern_t *pattern = (cairo_linear_pattern_t *) pdf_pattern->pattern; - cairo_pdf_resource_t color_function, alpha_function; - double x1, y1, x2, y2; - double _x1, _y1, _x2, _y2; - cairo_matrix_t pat_to_pdf; - cairo_extend_t extend; - cairo_status_t status; - cairo_gradient_pattern_t *gradient = &pattern->base; - double first_stop, last_stop; - int repeat_begin = 0, repeat_end = 1; - - assert (pattern->base.n_stops != 0); - - extend = cairo_pattern_get_extend (pdf_pattern->pattern); - - pat_to_pdf = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - first_stop = gradient->stops[0].offset; - last_stop = gradient->stops[gradient->n_stops - 1].offset; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - double dx, dy; - int x_rep = 0, y_rep = 0; - - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - cairo_matrix_transform_point (&pat_to_pdf, &x1, &y1); - - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - cairo_matrix_transform_point (&pat_to_pdf, &x2, &y2); - - dx = fabs (x2 - x1); - dy = fabs (y2 - y1); - if (dx > 1e-6) - x_rep = ceil (surface->width/dx); - if (dy > 1e-6) - y_rep = ceil (surface->height/dy); - - repeat_end = MAX (x_rep, y_rep); - repeat_begin = -repeat_end; - first_stop = repeat_begin; - last_stop = repeat_end; - } - - /* PDF requires the first and last stop to be the same as the line - * coordinates. For repeating patterns this moves the line - * coordinates out to the begin/end of the repeating function. For - * non repeating patterns this may move the line coordinates in if - * there are not stops at offset 0 and 1. */ - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - - _x1 = x1 + (x2 - x1)*first_stop; - _y1 = y1 + (y2 - y1)*first_stop; - _x2 = x1 + (x2 - x1)*last_stop; - _y2 = y1 + (y2 - y1)*last_stop; - - x1 = _x1; - x2 = _x2; - y1 = _y1; - y2 = _y2; - - /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a - * Type 2 function is used by itself without a stitching - * function. Type 2 functions always have the domain [0 1] */ - if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || - pattern->base.base.extend == CAIRO_EXTEND_PAD) && - gradient->n_stops == 2) { - first_stop = 0.0; - last_stop = 1.0; - } - - status = _cairo_pdf_surface_emit_pattern_stops (surface, - &pattern->base, - &color_function, - &alpha_function); - if (unlikely (status)) - return status; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - status = _cairo_pdf_surface_emit_repeating_function (surface, - &pattern->base, - &color_function, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - - if (alpha_function.id != 0) { - status = _cairo_pdf_surface_emit_repeating_function (surface, - &pattern->base, - &alpha_function, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - } - } - - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function %d 0 R\n", - pdf_pattern->pattern_res.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, x2, y2, - first_stop, last_stop, - color_function.id); + "%d 0 obj\n", + pattern_resource.id); - if (extend == CAIRO_EXTEND_PAD) { + if (!pdf_pattern->is_shading) { + _cairo_output_stream_printf (surface->output, + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ "); + _cairo_output_stream_print_matrix (surface->output, pat_to_pdf); + _cairo_output_stream_printf (surface->output, + " ]\n" + " /Shading\n"); + } + + if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + _cairo_output_stream_printf (surface->output, + " << /ShadingType 2\n" + " /ColorSpace %s\n" + " /Coords [ %f %f %f %f ]\n", + colorspace, + start->center.x, start->center.y, + end->center.x, end->center.y); + } else { + _cairo_output_stream_printf (surface->output, + " << /ShadingType 3\n" + " /ColorSpace %s\n" + " /Coords [ %f %f %f %f %f %f ]\n", + colorspace, + start->center.x, start->center.y, + MAX (start->radius, 0), + end->center.x, end->center.y, + MAX (end->radius, 0)); + } + + _cairo_output_stream_printf (surface->output, + " /Domain [ %f %f ]\n", + domain[0], domain[1]); + + if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) { _cairo_output_stream_printf (surface->output, " /Extend [ true true ]\n"); } else { @@ -3135,9 +4353,145 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, } _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); + " /Function %d 0 R\n" + " >>\n", + color_function.id); + + if (!pdf_pattern->is_shading) { + _cairo_output_stream_printf (surface->output, + ">>\n"); + } + + _cairo_output_stream_printf (surface->output, + "endobj\n"); +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) +{ + cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern; + cairo_pdf_resource_t color_function, alpha_function; + cairo_matrix_t pat_to_pdf; + cairo_circle_double_t start, end; + double domain[2]; + cairo_int_status_t status; + cairo_matrix_t mat; + + assert (pattern->n_stops != 0); + + status = _cairo_pdf_surface_emit_pattern_stops (surface, + pattern, + &color_function, + &alpha_function); + if (unlikely (status)) + return status; + + pat_to_pdf = pattern->base.matrix; + status = cairo_matrix_invert (&pat_to_pdf); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + + if (pdf_pattern->inverted_y_axis) + cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); + else + cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); + + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat); + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { + double bounds_x1, bounds_x2, bounds_y1, bounds_y2; + double x_scale, y_scale, tolerance; + + /* TODO: use tighter extents */ + bounds_x1 = 0; + bounds_y1 = 0; + bounds_x2 = surface->width; + bounds_y2 = surface->height; + _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + &bounds_x1, &bounds_y1, + &bounds_x2, &bounds_y2, + NULL); + + x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution; + y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution; + + tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix)); + tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1); + tolerance *= MIN (x_scale, y_scale); + + _cairo_gradient_pattern_box_to_parameter (pattern, + bounds_x1, bounds_y1, + bounds_x2, bounds_y2, + tolerance, domain); + } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) { + /* + * If the first and the last stop offset are the same, then + * the color function is a step function. + * _cairo_ps_surface_emit_pattern_stops emits it as a stitched + * function no matter how many stops the pattern has. The + * domain of the stitched function will be [0 1] in this case. + * + * This is done to avoid emitting degenerate gradients for + * EXTEND_PAD patterns having a step color function. + */ + domain[0] = 0.0; + domain[1] = 1.0; + + assert (pattern->base.extend == CAIRO_EXTEND_PAD); + } else { + domain[0] = pattern->stops[0].offset; + domain[1] = pattern->stops[pattern->n_stops - 1].offset; + } + + /* PDF requires the first and last stop to be the same as the + * extreme coordinates. For repeating patterns this moves the + * extreme coordinates out to the begin/end of the repeating + * function. For non repeating patterns this may move the extreme + * coordinates in if there are not stops at offset 0 and 1. */ + _cairo_gradient_pattern_interpolate (pattern, domain[0], &start); + _cairo_gradient_pattern_interpolate (pattern, domain[1], &end); + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { + int repeat_begin, repeat_end; + + repeat_begin = floor (domain[0]); + repeat_end = ceil (domain[1]); + + status = _cairo_pdf_surface_emit_repeating_function (surface, + pattern, + &color_function, + repeat_begin, + repeat_end); + if (unlikely (status)) + return status; + + if (alpha_function.id != 0) { + status = _cairo_pdf_surface_emit_repeating_function (surface, + pattern, + &alpha_function, + repeat_begin, + repeat_end); + if (unlikely (status)) + return status; + } + } else if (pattern->n_stops <= 2) { + /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a + * Type 2 function is used by itself without a stitching + * function. Type 2 functions always have the domain [0 1] */ + domain[0] = 0.0; + domain[1] = 1.0; + } + + _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); + _cairo_pdf_surface_output_gradient (surface, pdf_pattern, + pdf_pattern->pattern_res, + &pat_to_pdf, &start, &end, domain, + "/DeviceRGB", color_function); if (alpha_function.id != 0) { cairo_pdf_resource_t mask_resource; @@ -3149,42 +4503,13 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, if (mask_resource.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceGray\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function %d 0 R\n", - mask_resource.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, x2, y2, - first_stop, last_stop, - alpha_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); - status = _cairo_pdf_surface_add_pattern (surface, mask_resource); - if (unlikely (status)) - return status; + _cairo_pdf_surface_output_gradient (surface, pdf_pattern, + mask_resource, + &pat_to_pdf, &start, &end, domain, + "/DeviceGray", alpha_function); status = cairo_pdf_surface_emit_transparency_group (surface, + pdf_pattern, pdf_pattern->gstate_res, mask_resource); if (unlikely (status)) @@ -3194,114 +4519,148 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t -_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) +static cairo_int_status_t +_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) { - cairo_pdf_resource_t color_function, alpha_function; - double x1, y1, x2, y2, r1, r2; cairo_matrix_t pat_to_pdf; - cairo_extend_t extend; - cairo_status_t status; - cairo_radial_pattern_t *pattern = (cairo_radial_pattern_t *) pdf_pattern->pattern; + cairo_int_status_t status; + cairo_pattern_t *pattern = pdf_pattern->pattern; + cairo_pdf_shading_t shading; + int i; + cairo_pdf_resource_t res; + cairo_matrix_t mat; - assert (pattern->base.n_stops != 0); + pat_to_pdf = pattern->matrix; + status = cairo_matrix_invert (&pat_to_pdf); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); - extend = cairo_pattern_get_extend (pdf_pattern->pattern); + if (pdf_pattern->inverted_y_axis) + cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); + else + cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); - status = _cairo_pdf_surface_emit_pattern_stops (surface, - &pattern->base, - &color_function, - &alpha_function); + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat); + + status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern); if (unlikely (status)) return status; - pat_to_pdf = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + res = _cairo_pdf_surface_new_object (surface); + if (unlikely (res.id == 0)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - x1 = _cairo_fixed_to_double (pattern->c1.x); - y1 = _cairo_fixed_to_double (pattern->c1.y); - r1 = _cairo_fixed_to_double (pattern->r1); - x2 = _cairo_fixed_to_double (pattern->c2.x); - y2 = _cairo_fixed_to_double (pattern->c2.y); - r2 = _cairo_fixed_to_double (pattern->r2); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /ShadingType %d\n" + " /ColorSpace /DeviceRGB\n" + " /BitsPerCoordinate %d\n" + " /BitsPerComponent %d\n" + " /BitsPerFlag %d\n" + " /Decode [", + res.id, + shading.shading_type, + shading.bits_per_coordinate, + shading.bits_per_component, + shading.bits_per_flag); + + for (i = 0; i < shading.decode_array_length; i++) + _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); + + _cairo_output_stream_printf (surface->output, + "]\n" + " /Length %ld\n" + ">>\n" + "stream\n", + shading.data_length); + + _cairo_output_stream_write (surface->output, shading.data, shading.data_length); + + _cairo_output_stream_printf (surface->output, + "\nendstream\n" + "endobj\n"); + + _cairo_pdf_shading_fini (&shading); _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Pattern\n" " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function %d 0 R\n", - pdf_pattern->pattern_res.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, r1, x2, y2, r2, - color_function.id); - - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - + " /Matrix [ ", + pdf_pattern->pattern_res.id); + _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); + " ]\n" + " /Shading %d 0 R\n" + ">>\n" + "endobj\n", + res.id); - if (alpha_function.id != 0) { + if (pdf_pattern->gstate_res.id != 0) { cairo_pdf_resource_t mask_resource; - assert (pdf_pattern->gstate_res.id != 0); - /* Create pattern for SMask. */ - mask_resource = _cairo_pdf_surface_new_object (surface); - if (mask_resource.id == 0) + res = _cairo_pdf_surface_new_object (surface); + if (unlikely (res.id == 0)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceGray\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function %d 0 R\n", - mask_resource.id, - pat_to_pdf.xx, pat_to_pdf.yx, - pat_to_pdf.xy, pat_to_pdf.yy, - pat_to_pdf.x0, pat_to_pdf.y0, - x1, y1, r1, x2, y2, r2, - alpha_function.id); + status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern); + if (unlikely (status)) + return status; - if (extend == CAIRO_EXTEND_PAD) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /ShadingType %d\n" + " /ColorSpace /DeviceGray\n" + " /BitsPerCoordinate %d\n" + " /BitsPerComponent %d\n" + " /BitsPerFlag %d\n" + " /Decode [", + res.id, + shading.shading_type, + shading.bits_per_coordinate, + shading.bits_per_component, + shading.bits_per_flag); - _cairo_output_stream_printf (surface->output, - " >>\n" - ">>\n" - "endobj\n"); + for (i = 0; i < shading.decode_array_length; i++) + _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); + + _cairo_output_stream_printf (surface->output, + "]\n" + " /Length %ld\n" + ">>\n" + "stream\n", + shading.data_length); + + _cairo_output_stream_write (surface->output, shading.data, shading.data_length); + + _cairo_output_stream_printf (surface->output, + "\nendstream\n" + "endobj\n"); + _cairo_pdf_shading_fini (&shading); + + mask_resource = _cairo_pdf_surface_new_object (surface); + if (unlikely (mask_resource.id == 0)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Pattern\n" + " /PatternType 2\n" + " /Matrix [ ", + mask_resource.id); + _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); + _cairo_output_stream_printf (surface->output, + " ]\n" + " /Shading %d 0 R\n" + ">>\n" + "endobj\n", + res.id); status = cairo_pdf_surface_emit_transparency_group (surface, + pdf_pattern, pdf_pattern->gstate_res, mask_resource); if (unlikely (status)) @@ -3311,17 +4670,10 @@ _cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) { - double old_width, old_height; - cairo_status_t status; - - old_width = surface->width; - old_height = surface->height; - _cairo_pdf_surface_set_size_internal (surface, - pdf_pattern->width, - pdf_pattern->height); + cairo_int_status_t status; switch (pdf_pattern->pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: @@ -3330,15 +4682,17 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern break; case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern); break; case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_pdf_surface_emit_linear_pattern (surface, pdf_pattern); + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern); break; - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_pdf_surface_emit_radial_pattern (surface, pdf_pattern); + case CAIRO_PATTERN_TYPE_MESH: + status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern); break; default: @@ -3347,73 +4701,233 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern break; } - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - return status; } -static cairo_status_t -_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, - cairo_surface_pattern_t *source) +static cairo_int_status_t +_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_rectangle_int_t *extents, + double alpha, + cairo_pdf_resource_t *smask_res, + cairo_bool_t stencil_mask) { - cairo_pdf_resource_t surface_res; - int width, height; cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_status_t status; - int alpha; + cairo_int_status_t status; + int alpha_id; + double x_offset; + double y_offset; + cairo_pdf_source_surface_entry_t *pdf_source; - status = _cairo_pdf_surface_add_source_surface (surface, - source->surface, - source->base.filter, - &surface_res, - &width, - &height); + if (source->extend == CAIRO_EXTEND_PAD && + !(source->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)) + { + status = _cairo_pdf_surface_add_padded_image_surface (surface, + source, + extents, + &pdf_source, + &x_offset, + &y_offset, + NULL); + } else { + status = _cairo_pdf_surface_add_source_surface (surface, + NULL, + source, + op, + source->filter, + stencil_mask, + FALSE, /* smask */ + alpha != 1.0, /* need_transp_group */ + extents, + smask_res, + &pdf_source, + &x_offset, + &y_offset, + NULL); + } if (unlikely (status)) return status; - cairo_p2d = source->base.matrix; + cairo_p2d = source->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); pdf_p2d = surface->cairo_to_pdf; cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); - cairo_matrix_translate (&pdf_p2d, 0.0, height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - if (source->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); + if (pdf_source->emit_image) { + int width, height; + + if (pdf_source->bounded) { + width = pdf_source->extents.width; + height = pdf_source->extents.height; + } else { + /* We can't scale an image to an unbounded surface size so just set the size to 1 */ + width = 1; + height = 1; + } + + cairo_matrix_translate (&pdf_p2d, 0.0, height); + cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); cairo_matrix_scale (&pdf_p2d, width, height); + } status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; if (! _cairo_matrix_is_identity (&pdf_p2d)) { - _cairo_output_stream_printf (surface->output, - "%f %f %f %f %f %f cm\n", - pdf_p2d.xx, pdf_p2d.yx, - pdf_p2d.xy, pdf_p2d.yy, - pdf_p2d.x0, pdf_p2d.y0); + _cairo_output_stream_print_matrix (surface->output, &pdf_p2d); + _cairo_output_stream_printf (surface->output, " cm\n"); } - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id); if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->output, - "/a%d gs /x%d Do\n", - alpha, - surface_res.id); + if (stencil_mask) { + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + pdf_source->surface_res.id); + } else { + _cairo_output_stream_printf (surface->output, + "/a%d gs /x%d Do\n", + alpha_id, + pdf_source->surface_res.id); + } - return _cairo_pdf_surface_add_xobject (surface, surface_res); + return _cairo_pdf_surface_add_xobject (surface, pdf_source->surface_res); } -static cairo_status_t +static cairo_int_status_t +_cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_rectangle_int_t *extents, + double alpha) +{ + cairo_pdf_resource_t shading_res, gstate_res; + cairo_matrix_t pat_to_pdf; + cairo_int_status_t status; + int alpha_id; + + status = _cairo_pdf_surface_add_pdf_shading (surface, source, + op, extents, + &shading_res, &gstate_res); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + return CAIRO_INT_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + pat_to_pdf = source->matrix; + status = cairo_matrix_invert (&pat_to_pdf); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pat_to_pdf)) { + _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); + _cairo_output_stream_printf (surface->output, " cm\n"); + } + + status = _cairo_pdf_surface_add_shading (surface, shading_res); + if (unlikely (status)) + return status; + + if (gstate_res.id != 0) { + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/s%d gs /sh%d sh\n", + gstate_res.id, + shading_res.id); + } else { + status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/a%d gs /sh%d sh\n", + alpha_id, + shading_res.id); + } + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_rectangle_int_t *extents, + double alpha, + cairo_bool_t mask) +{ + switch (source->type) { + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _cairo_pdf_surface_paint_surface_pattern (surface, + op, + source, + extents, + alpha, + NULL, + mask); + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + return _cairo_pdf_surface_paint_gradient (surface, + op, + source, + extents, + alpha); + + case CAIRO_PATTERN_TYPE_SOLID: + default: + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; + } +} + +static cairo_bool_t +_can_paint_pattern (const cairo_pattern_t *pattern) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return FALSE; + + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return (pattern->extend == CAIRO_EXTEND_NONE || + pattern->extend == CAIRO_EXTEND_PAD); + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + return TRUE; + + case CAIRO_PATTERN_TYPE_MESH: + return FALSE; + + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +static cairo_int_status_t _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, cairo_operator_t op) { - cairo_status_t status; + cairo_int_status_t status; if (op == surface->current_operator) return CAIRO_STATUS_SUCCESS; @@ -3430,13 +4944,13 @@ _cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, const cairo_pattern_t *pattern, cairo_pdf_resource_t pattern_res, cairo_bool_t is_stroke) { - cairo_status_t status; + cairo_int_status_t status; int alpha; const cairo_color_t *solid_color = NULL; @@ -3541,6 +5055,7 @@ _cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) _cairo_output_stream_printf (surface->output, "Q\n"); _cairo_pdf_operators_reset (&surface->pdf_operators); + surface->current_pattern_is_solid_color = FALSE; } surface->select_pattern_gstate_saved = FALSE; @@ -3553,10 +5068,26 @@ _cairo_pdf_surface_show_page (void *abstract_surface) cairo_pdf_surface_t *surface = abstract_surface; cairo_int_status_t status; + status = _cairo_array_append (&surface->page_heights, &surface->height); + if (unlikely (status)) + return status; + + status = _cairo_array_append (&surface->page_labels, &surface->current_page_label); + if (unlikely (status)) + return status; + + surface->current_page_label = NULL; + + status = _cairo_pdf_interchange_end_page_content (surface); + if (unlikely (status)) + return status; + status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) return status; + _cairo_surface_clipper_reset (&surface->clipper); + status = _cairo_pdf_surface_write_page (surface); if (unlikely (status)) return status; @@ -3572,17 +5103,10 @@ _cairo_pdf_surface_get_extents (void *abstract_surface, { cairo_pdf_surface_t *surface = abstract_surface; - rectangle->x = 0; - rectangle->y = 0; + if (surface->surface_bounded) + *rectangle = surface->surface_extents; - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the arbitrary limitation of width to a short(!). We - * may need to come up with a better interface for get_size. - */ - rectangle->width = ceil (surface->width); - rectangle->height = ceil (surface->height); - - return TRUE; + return surface->surface_bounded; } static void @@ -3594,28 +5118,7 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface, cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); -} - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t info; - - info = _cairo_pdf_surface_new_object (surface); - if (info.id == 0) - return info; - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Creator (cairo %s (http://cairographics.org))\n" - " /Producer (cairo %s (http://cairographics.org))\n" - ">>\n" - "endobj\n", - info.id, - cairo_version_string (), - cairo_version_string ()); - - return info; + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static void @@ -3648,19 +5151,78 @@ _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) "endobj\n"); } -static cairo_status_t +cairo_int_status_t +_cairo_utf8_to_pdf_string (const char *utf8, char **str_out) +{ + int i; + int len; + cairo_bool_t ascii; + char *str; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + ascii = TRUE; + len = strlen (utf8); + for (i = 0; i < len; i++) { + unsigned c = utf8[i]; + if (c < 32 || c > 126 || c == '(' || c == ')' || c == '\\') { + ascii = FALSE; + break; + } + } + + if (ascii) { + str = _cairo_malloc (len + 3); + if (str == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + str[0] = '('; + for (i = 0; i < len; i++) + str[i+1] = utf8[i]; + str[i+1] = ')'; + str[i+2] = 0; + } else { + uint16_t *utf16 = NULL; + int utf16_len = 0; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); + if (unlikely (status)) + return status; + + str = _cairo_malloc (utf16_len*4 + 7); + if (str == NULL) { + free (utf16); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + strcpy (str, ""); + free (utf16); + } + *str_out = str; + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, const char *utf8) { uint16_t *utf16 = NULL; int utf16_len = 0; - cairo_status_t status; + cairo_int_status_t status; int i; if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) + if (unlikely (status == CAIRO_INT_STATUS_INVALID_STRING)) { + utf16 = NULL; + utf16_len = 0; + } else if (unlikely (status)) { return status; + } } _cairo_output_stream_printf (surface->output, "<"); @@ -3680,8 +5242,7 @@ _cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, } _cairo_output_stream_printf (surface->output, ">"); - if (utf16) - free (utf16); + free (utf16); return CAIRO_STATUS_SUCCESS; } @@ -3725,16 +5286,16 @@ _hash_data (const unsigned char *data, int length, uint32_t initval) c += length; switch(len) { - case 11: c+= ((uint32_t) data[10] << 24); - case 10: c+= ((uint32_t) data[9] << 16); - case 9 : c+= ((uint32_t) data[8] << 8); - case 8 : b+= ((uint32_t) data[7] << 24); - case 7 : b+= ((uint32_t) data[6] << 16); - case 6 : b+= ((uint32_t) data[5] << 8); - case 5 : b+= data[4]; - case 4 : a+= ((uint32_t) data[3] << 24); - case 3 : a+= ((uint32_t) data[2] << 16); - case 2 : a+= ((uint32_t) data[1] << 8); + case 11: c+= ((uint32_t) data[10] << 24); /* fall through */ + case 10: c+= ((uint32_t) data[9] << 16); /* fall through */ + case 9 : c+= ((uint32_t) data[8] << 8); /* fall through */ + case 8 : b+= ((uint32_t) data[7] << 24); /* fall through */ + case 7 : b+= ((uint32_t) data[6] << 16); /* fall through */ + case 6 : b+= ((uint32_t) data[5] << 8); /* fall through */ + case 5 : b+= data[4]; /* fall through */ + case 4 : a+= ((uint32_t) data[3] << 24); /* fall through */ + case 3 : a+= ((uint32_t) data[2] << 16); /* fall through */ + case 2 : a+= ((uint32_t) data[1] << 8); /* fall through */ case 1 : a+= data[0]; } HASH_MIX (a,b,c); @@ -3749,18 +5310,14 @@ _create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, { uint32_t hash; int i; - long numerator; - ldiv_t d; hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0); hash = _hash_data ((unsigned char *) (font_subset->glyphs), font_subset->num_glyphs * sizeof(unsigned long), hash); - numerator = abs (hash); for (i = 0; i < 6; i++) { - d = ldiv (numerator, 26); - numerator = d.quot; - tag[i] = 'A' + d.rem; + tag[i] = 'A' + (hash % 26); + hash /= 26; } tag[i] = 0; } @@ -3768,7 +5325,6 @@ _create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, static cairo_int_status_t _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, - cairo_bool_t is_composite, cairo_pdf_resource_t *stream) { unsigned int i, num_bfchar; @@ -3796,7 +5352,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, "/CMapType 2 def\n" "1 begincodespacerange\n"); - if (is_composite) { + if (font_subset->is_composite && !font_subset->is_latin) { _cairo_output_stream_printf (surface->output, "<0000> \n"); } else { @@ -3848,7 +5404,9 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, "%d beginbfchar\n", num_bfchar - i > 100 ? 100 : num_bfchar - i); } - if (is_composite) + if (font_subset->is_latin) + _cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]); + else if (font_subset->is_composite) _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); else _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); @@ -3878,7 +5436,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, #define PDF_UNITS_PER_EM 1000 -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_cff_subset_t *subset) @@ -3886,8 +5444,8 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, cairo_pdf_resource_t stream, descriptor, cidfont_dict; cairo_pdf_resource_t subset_resource, to_unicode_stream; cairo_pdf_font_t font; - unsigned int i; - cairo_status_t status; + unsigned int i, last_glyph; + cairo_int_status_t status; char tag[10]; _create_font_subset_tag (font_subset, subset->ps_name, tag); @@ -3901,6 +5459,8 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_open_stream (surface, NULL, TRUE, + font_subset->is_latin ? + " /Subtype /Type1C\n" : " /Subtype /CIDFontType0C\n"); if (unlikely (status)) return status; @@ -3913,9 +5473,9 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, TRUE, + font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; descriptor = _cairo_pdf_surface_new_object (surface); @@ -3930,10 +5490,18 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, tag, subset->ps_name); - if (subset->font_name) { - _cairo_output_stream_printf (surface->output, - " /FontFamily (%s)\n", - subset->font_name); + if (subset->family_name_utf8) { + char *pdf_str; + + status = _cairo_utf8_to_pdf_string (subset->family_name_utf8, &pdf_str); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + _cairo_output_stream_printf (surface->output, + " /FontFamily %s\n", + pdf_str); + free (pdf_str); + } else if (status != CAIRO_INT_STATUS_INVALID_STRING) { + return status; + } } _cairo_output_stream_printf (surface->output, @@ -3957,58 +5525,106 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, (long)(subset->y_max*PDF_UNITS_PER_EM), stream.id); - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (font_subset->is_latin) { + /* find last glyph used */ + for (i = 255; i >= 32; i--) + if (font_subset->latin_to_subset_glyph_index[i] > 0) + break; - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType0\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset->ps_name, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) + last_glyph = i; + _cairo_pdf_surface_update_object (surface, subset_resource); _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type1\n" + " /BaseFont /%s+%s\n" + " /FirstChar 32\n" + " /LastChar %d\n" + " /FontDescriptor %d 0 R\n" + " /Encoding /WinAnsiEncoding\n" + " /Widths [", + subset_resource.id, + tag, + subset->ps_name, + last_glyph, + descriptor.id); - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); + for (i = 32; i < last_glyph + 1; i++) { + int glyph = font_subset->latin_to_subset_glyph_index[i]; + if (glyph > 0) { + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); + } else { + _cairo_output_stream_printf (surface->output, " 0"); + } + } - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset->ps_name, - cidfont_dict.id); + _cairo_output_stream_printf (surface->output, + " ]\n"); - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } else { + cidfont_dict = _cairo_pdf_surface_new_object (surface); + if (cidfont_dict.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /CIDFontType0\n" + " /BaseFont /%s+%s\n" + " /CIDSystemInfo\n" + " << /Registry (Adobe)\n" + " /Ordering (Identity)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %d 0 R\n" + " /W [0 [", + cidfont_dict.id, + tag, + subset->ps_name, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + + _cairo_output_stream_printf (surface->output, + " ]]\n" + ">>\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type0\n" + " /BaseFont /%s+%s\n" + " /Encoding /Identity-H\n" + " /DescendantFonts [ %d 0 R]\n", + subset_resource.id, + tag, + subset->ps_name, + cidfont_dict.id); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; @@ -4018,11 +5634,11 @@ _cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_cff_subset_t subset; char name[64]; @@ -4039,14 +5655,19 @@ _cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_cff_subset_t subset; char name[64]; + /* CFF fallback subsetting does not work with 8-bit glyphs unless + * they are a latin subset */ + if (!font_subset->is_composite && !font_subset->is_latin) + return CAIRO_INT_STATUS_UNSUPPORTED; + snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_cff_fallback_init (&subset, name, font_subset); @@ -4060,16 +5681,16 @@ _cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset, cairo_type1_subset_t *subset) { cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream; cairo_pdf_font_t font; - cairo_status_t status; + cairo_int_status_t status; unsigned long length; - unsigned int i; + unsigned int i, last_glyph; char tag[10]; _create_font_subset_tag (font_subset, subset->base_font, tag); @@ -4100,11 +5721,21 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, return status; status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, FALSE, + font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; + last_glyph = font_subset->num_glyphs - 1; + if (font_subset->is_latin) { + /* find last glyph used */ + for (i = 255; i >= 32; i--) + if (font_subset->latin_to_subset_glyph_index[i] > 0) + break; + + last_glyph = i; + } + descriptor = _cairo_pdf_surface_new_object (surface); if (descriptor.id == 0) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -4142,20 +5773,37 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, "<< /Type /Font\n" " /Subtype /Type1\n" " /BaseFont /%s+%s\n" - " /FirstChar 0\n" + " /FirstChar %d\n" " /LastChar %d\n" - " /FontDescriptor %d 0 R\n" - " /Widths [", + " /FontDescriptor %d 0 R\n", subset_resource.id, tag, subset->base_font, - font_subset->num_glyphs - 1, + font_subset->is_latin ? 32 : 0, + last_glyph, descriptor.id); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + if (font_subset->is_latin) + _cairo_output_stream_printf (surface->output, " /Encoding /WinAnsiEncoding\n"); + + _cairo_output_stream_printf (surface->output, " /Widths ["); + if (font_subset->is_latin) { + for (i = 32; i < last_glyph + 1; i++) { + int glyph = font_subset->latin_to_subset_glyph_index[i]; + if (glyph > 0) { + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); + } else { + _cairo_output_stream_printf (surface->output, " 0"); + } + } + } else { + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset->widths[i]*PDF_UNITS_PER_EM)); + } _cairo_output_stream_printf (surface->output, " ]\n"); @@ -4175,15 +5823,18 @@ _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, return _cairo_array_append (&surface->fonts, &font); } -#if CAIRO_HAS_FT_FONT -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_type1_subset_t subset; char name[64]; + /* 16-bit glyphs not compatible with Type 1 fonts */ + if (font_subset->is_composite && !font_subset->is_latin) + return CAIRO_INT_STATUS_UNSUPPORTED; + snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); @@ -4195,16 +5846,19 @@ _cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, _cairo_type1_subset_fini (&subset); return status; } -#endif -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status; + cairo_int_status_t status; cairo_type1_subset_t subset; char name[64]; + /* 16-bit glyphs not compatible with Type 1 fonts */ + if (font_subset->is_composite && !font_subset->is_latin) + return CAIRO_INT_STATUS_UNSUPPORTED; + snprintf (name, sizeof name, "CairoFont-%d-%d", font_subset->font_id, font_subset->subset_id); status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); @@ -4217,16 +5871,16 @@ _cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { cairo_pdf_resource_t stream, descriptor, cidfont_dict; cairo_pdf_resource_t subset_resource, to_unicode_stream; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_font_t font; cairo_truetype_subset_t subset; - unsigned int i; + unsigned int i, last_glyph; char tag[10]; subset_resource = _cairo_pdf_surface_get_font_resource (surface, @@ -4235,7 +5889,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, if (subset_resource.id == 0) return CAIRO_STATUS_SUCCESS; - status = _cairo_truetype_subset_init (&subset, font_subset); + status = _cairo_truetype_subset_init_pdf (&subset, font_subset); if (unlikely (status)) return status; @@ -4261,9 +5915,9 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, } status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, TRUE, + font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) { + if (_cairo_int_status_is_error (status)) { _cairo_truetype_subset_fini (&subset); return status; } @@ -4282,14 +5936,22 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, tag, subset.ps_name); - if (subset.font_name) { - _cairo_output_stream_printf (surface->output, - " /FontFamily (%s)\n", - subset.font_name); + if (subset.family_name_utf8) { + char *pdf_str; + + status = _cairo_utf8_to_pdf_string (subset.family_name_utf8, &pdf_str); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + _cairo_output_stream_printf (surface->output, + " /FontFamily %s\n", + pdf_str); + free (pdf_str); + } else if (status != CAIRO_INT_STATUS_INVALID_STRING) { + return status; + } } _cairo_output_stream_printf (surface->output, - " /Flags 4\n" + " /Flags %d\n" " /FontBBox [ %ld %ld %ld %ld ]\n" " /ItalicAngle 0\n" " /Ascent %ld\n" @@ -4300,6 +5962,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, " /FontFile2 %u 0 R\n" ">>\n" "endobj\n", + font_subset->is_latin ? 32 : 4, (long)(subset.x_min*PDF_UNITS_PER_EM), (long)(subset.y_min*PDF_UNITS_PER_EM), (long)(subset.x_max*PDF_UNITS_PER_EM), @@ -4309,61 +5972,109 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, (long)(subset.y_max*PDF_UNITS_PER_EM), stream.id); - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) { - _cairo_truetype_subset_fini (&subset); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (font_subset->is_latin) { + /* find last glyph used */ + for (i = 255; i >= 32; i--) + if (font_subset->latin_to_subset_glyph_index[i] > 0) + break; + + last_glyph = i; + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /TrueType\n" + " /BaseFont /%s+%s\n" + " /FirstChar 32\n" + " /LastChar %d\n" + " /FontDescriptor %d 0 R\n" + " /Encoding /WinAnsiEncoding\n" + " /Widths [", + subset_resource.id, + tag, + subset.ps_name, + last_glyph, + descriptor.id); + + for (i = 32; i < last_glyph + 1; i++) { + int glyph = font_subset->latin_to_subset_glyph_index[i]; + if (glyph > 0) { + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset.widths[glyph]*PDF_UNITS_PER_EM)); + } else { + _cairo_output_stream_printf (surface->output, " 0"); + } + } + + _cairo_output_stream_printf (surface->output, + " ]\n"); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + } else { + cidfont_dict = _cairo_pdf_surface_new_object (surface); + if (cidfont_dict.id == 0) { + _cairo_truetype_subset_fini (&subset); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /CIDFontType2\n" + " /BaseFont /%s+%s\n" + " /CIDSystemInfo\n" + " << /Registry (Adobe)\n" + " /Ordering (Identity)\n" + " /Supplement 0\n" + " >>\n" + " /FontDescriptor %d 0 R\n" + " /W [0 [", + cidfont_dict.id, + tag, + subset.ps_name, + descriptor.id); + + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " %ld", + (long)(subset.widths[i]*PDF_UNITS_PER_EM)); + + _cairo_output_stream_printf (surface->output, + " ]]\n" + ">>\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type0\n" + " /BaseFont /%s+%s\n" + " /Encoding /Identity-H\n" + " /DescendantFonts [ %d 0 R]\n", + subset_resource.id, + tag, + subset.ps_name, + cidfont_dict.id); + + if (to_unicode_stream.id != 0) + _cairo_output_stream_printf (surface->output, + " /ToUnicode %d 0 R\n", + to_unicode_stream.id); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); } - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType2\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset.ps_name, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset.widths[i]*PDF_UNITS_PER_EM)); - - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset.ps_name, - cidfont_dict.id); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - font.font_id = font_subset->font_id; font.subset_id = font_subset->subset_id; font.subset_resource = subset_resource; @@ -4374,7 +6085,7 @@ _cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_emit_imagemask (cairo_image_surface_t *image, cairo_output_stream_t *stream) { @@ -4414,13 +6125,13 @@ _cairo_pdf_emit_imagemask (cairo_image_surface_t *image, return _cairo_output_stream_get_status (stream); } -static cairo_status_t +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_status_t status = CAIRO_STATUS_SUCCESS; - cairo_status_t status2; + 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; @@ -4429,7 +6140,8 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, null_stream, _cairo_pdf_emit_imagemask, - surface->font_subsets); + surface->font_subsets, + FALSE); if (unlikely (type3_surface->status)) { status2 = _cairo_output_stream_destroy (null_stream); return type3_surface->status; @@ -4448,17 +6160,17 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su cairo_surface_destroy (type3_surface); status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, cairo_scaled_font_subset_t *font_subset) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream; cairo_pdf_font_t font; double *widths; @@ -4490,7 +6202,8 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_pdf_emit_imagemask, - surface->font_subsets); + surface->font_subsets, + FALSE); if (unlikely (type3_surface->status)) { free (glyphs); free (widths); @@ -4585,9 +6298,9 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, free (glyphs); status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, FALSE, + font_subset, &to_unicode_stream); - if (_cairo_status_is_error (status)) { + if (_cairo_int_status_is_error (status)) { free (widths); return status; } @@ -4598,16 +6311,16 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, "<< /Type /Font\n" " /Subtype /Type3\n" " /FontBBox [%f %f %f %f]\n" - " /FontMatrix [ 1 0 0 1 0 0 ]\n" + " /FontMatrix [ 1 0 0 -1 0 0 ]\n" " /Encoding %d 0 R\n" " /CharProcs %d 0 R\n" " /FirstChar 0\n" " /LastChar %d\n", subset_resource.id, _cairo_fixed_to_double (font_bbox.p1.x), - - _cairo_fixed_to_double (font_bbox.p2.y), + _cairo_fixed_to_double (font_bbox.p1.y), _cairo_fixed_to_double (font_bbox.p2.x), - - _cairo_fixed_to_double (font_bbox.p1.y), + _cairo_fixed_to_double (font_bbox.p2.y), encoding.id, char_procs.id, font_subset->num_glyphs - 1); @@ -4639,61 +6352,56 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, return _cairo_array_append (&surface->fonts, &font); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_pdf_surface_t *surface = closure; - cairo_status_t status; + cairo_int_status_t status; - if (font_subset->is_composite) { - status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } else { -#if CAIRO_HAS_FT_FONT - status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; -#endif + status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; + status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; - } + status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_pdf_surface_t *surface = closure; - cairo_status_t status; + cairo_int_status_t status; status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_int_status_t status; status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, _cairo_pdf_surface_analyze_user_font_subset, @@ -4736,12 +6444,42 @@ _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) _cairo_output_stream_printf (surface->output, "%d 0 obj\n" "<< /Type /Catalog\n" - " /Pages %d 0 R\n" - ">>\n" - "endobj\n", + " /Pages %d 0 R\n", catalog.id, surface->pages_resource.id); + if (surface->struct_tree_root.id != 0) { + _cairo_output_stream_printf (surface->output, + " /StructTreeRoot %d 0 R\n", + surface->struct_tree_root.id); + if (surface->tagged) { + _cairo_output_stream_printf (surface->output, + " /MarkInfo << /Marked true >>\n"); + } + } + + if (surface->outlines_dict_res.id != 0) { + _cairo_output_stream_printf (surface->output, + " /Outlines %d 0 R\n", + surface->outlines_dict_res.id); + } + + if (surface->page_labels_res.id != 0) { + _cairo_output_stream_printf (surface->output, + " /PageLabels %d 0 R\n", + surface->page_labels_res.id); + } + + if (surface->names_dict_res.id != 0) { + _cairo_output_stream_printf (surface->output, + " /Names %d 0 R\n", + surface->names_dict_res.id); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + return catalog; } @@ -4773,7 +6511,7 @@ _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) return offset; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { @@ -4781,58 +6519,81 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_resource_t smask; cairo_pdf_smask_group_t *smask_group; cairo_pdf_resource_t pattern_res, gstate_res; - cairo_status_t status; + cairo_int_status_t status; + cairo_box_double_t bbox; /* Create mask group */ - status = _cairo_pdf_surface_open_group (surface, NULL); + _get_bbox_from_extents (&group->extents, &bbox); + status = _cairo_pdf_surface_open_group (surface, &bbox, NULL); if (unlikely (status)) return status; - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL, - &pattern_res, &gstate_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (smask_group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - smask_group->operation = PDF_PAINT; - smask_group->source = cairo_pattern_reference (group->mask); - smask_group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (smask_group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (_can_paint_pattern (group->mask)) { + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_pattern (surface, + CAIRO_OPERATOR_OVER, + group->mask, + &group->extents, + 1.0, /* alpha */ + FALSE); /* mask */ if (unlikely (status)) return status; - status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - smask_group->group_res.id); + _cairo_output_stream_printf (surface->output, "Q\n"); } else { - status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, + CAIRO_OPERATOR_OVER, + NULL, + &pattern_res, &gstate_res); if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); + if (gstate_res.id != 0) { + smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); + if (unlikely (smask_group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; + smask_group->width = group->width; + smask_group->height = group->height; + smask_group->operation = PDF_PAINT; + smask_group->source = cairo_pattern_reference (group->mask); + smask_group->source_res = pattern_res; + status = _cairo_pdf_surface_add_smask_group (surface, smask_group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (smask_group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + smask_group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "%f %f %f %f re f\n", + bbox.p1.x, + bbox.p1.y, + bbox.p2.x - bbox.p1.x, + bbox.p2.y - bbox.p1.y); + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } } status = _cairo_pdf_surface_close_group (surface, &mask_group); @@ -4840,55 +6601,74 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return status; /* Create source group */ - status = _cairo_pdf_surface_open_group (surface, &group->source_res); + status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res); if (unlikely (status)) return status; - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL, - &pattern_res, &gstate_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (smask_group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - smask_group->operation = PDF_PAINT; - smask_group->source = cairo_pattern_reference (group->source); - smask_group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (smask_group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (_can_paint_pattern (group->source)) { + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_pattern (surface, + CAIRO_OPERATOR_OVER, + group->source, + &group->extents, + 1.0, /* alpha */ + FALSE); /* mask */ if (unlikely (status)) return status; - status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - smask_group->group_res.id); + _cairo_output_stream_printf (surface->output, "Q\n"); } else { - status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, + CAIRO_OPERATOR_OVER, + NULL, + &pattern_res, &gstate_res); if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); + if (gstate_res.id != 0) { + smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); + if (unlikely (smask_group == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; + smask_group->operation = PDF_PAINT; + smask_group->source = cairo_pattern_reference (group->source); + smask_group->source_res = pattern_res; + status = _cairo_pdf_surface_add_smask_group (surface, smask_group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (smask_group); + return status; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + smask_group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "%f %f %f %f re f\n", + bbox.p1.x, + bbox.p1.y, + bbox.p2.x - bbox.p1.x, + bbox.p2.y - bbox.p1.y); + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + } } status = _cairo_pdf_surface_close_group (surface, NULL); @@ -4927,18 +6707,25 @@ _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, return _cairo_output_stream_get_status (surface->output); } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) { double old_width, old_height; - cairo_status_t status; + cairo_bool_t old_in_xobject; + cairo_int_status_t status; + cairo_box_double_t bbox; + cairo_rectangle_int_t old_surface_extents; old_width = surface->width; old_height = surface->height; + old_surface_extents = surface->surface_extents; + old_in_xobject = surface->in_xobject; + surface->in_xobject = TRUE; _cairo_pdf_surface_set_size_internal (surface, group->width, group->height); + _cairo_pdf_operators_reset (&surface->pdf_operators); /* _mask is a special case that requires two groups - source * and mask as well as a smask and gstate dictionary */ if (group->operation == PDF_MASK) { @@ -4946,7 +6733,8 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, goto RESTORE_SIZE; } - status = _cairo_pdf_surface_open_group (surface, &group->group_res); + _get_bbox_from_extents (&group->extents, &bbox); + status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res); if (unlikely (status)) return status; @@ -4997,21 +6785,26 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_close_group (surface, NULL); RESTORE_SIZE: + surface->in_xobject = old_in_xobject; _cairo_pdf_surface_set_size_internal (surface, old_width, old_height); + surface->surface_extents = old_surface_extents; + _cairo_pdf_operators_reset (&surface->pdf_operators); return status; } -static cairo_status_t -_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface) +static cairo_int_status_t +_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface, + cairo_bool_t finish) { cairo_pdf_pattern_t pattern; cairo_pdf_smask_group_t *group; cairo_pdf_source_surface_t src_surface; - int pattern_index, group_index, surface_index; - cairo_status_t status; + unsigned int pattern_index, group_index, surface_index, doc_surface_index; + cairo_int_status_t status; + cairo_bool_t is_image; /* Writing out PDF_MASK groups will cause additional smask groups * to be appended to surface->smask_groups. Additional patterns @@ -5023,9 +6816,11 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface pattern_index = 0; group_index = 0; surface_index = 0; + doc_surface_index = 0; while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || (group_index < _cairo_array_num_elements (&surface->smask_groups)) || - (surface_index < _cairo_array_num_elements (&surface->page_surfaces))) + (surface_index < _cairo_array_num_elements (&surface->page_surfaces)) || + (finish && (doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces)))) { for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { _cairo_array_copy_element (&surface->smask_groups, group_index, &group); @@ -5043,25 +6838,47 @@ _cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) { _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface); - status = _cairo_pdf_surface_emit_surface (surface, &src_surface); + status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image); if (unlikely (status)) return status; } + + if (finish) { + for (; doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces); doc_surface_index++) { + _cairo_array_copy_element (&surface->doc_surfaces, doc_surface_index, &src_surface); + status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image); + if (unlikely (status)) + return status; + } + } } return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) { - cairo_pdf_resource_t page, knockout, res; - cairo_status_t status; - int i, len; + cairo_pdf_resource_t knockout, res, thumbnail_res; + cairo_pdf_resource_t *page; + cairo_int_status_t status; + unsigned int i, len, page_num, num_annots; + + status = _cairo_pdf_interchange_write_page_objects (surface); + if (unlikely (status)) + return status; _cairo_pdf_group_resources_clear (&surface->resources); if (surface->has_fallback_images) { - status = _cairo_pdf_surface_open_knockout_group (surface); + cairo_rectangle_int_t extents; + cairo_box_double_t bbox; + + extents.x = 0; + extents.y = 0; + extents.width = ceil (surface->width); + extents.height = ceil (surface->height); + _get_bbox_from_extents (&extents, &bbox); + status = _cairo_pdf_surface_open_knockout_group (surface, &bbox); if (unlikely (status)) return status; @@ -5087,7 +6904,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) return status; _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, NULL, FALSE); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE); if (unlikely (status)) return status; @@ -5103,36 +6920,70 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) return status; } - page = _cairo_pdf_surface_new_object (surface); - if (page.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + thumbnail_res.id = 0; + if (surface->thumbnail_image) { + cairo_pdf_source_surface_entry_t entry; + memset (&entry, 0, sizeof (entry)); + thumbnail_res = _cairo_pdf_surface_new_object (surface); + entry.surface_res = thumbnail_res; + _cairo_pdf_surface_emit_image (surface, surface->thumbnail_image, &entry); + } + + page_num = _cairo_array_num_elements (&surface->pages); + page = _cairo_array_index (&surface->pages, page_num - 1); + _cairo_pdf_surface_update_object (surface, *page); _cairo_output_stream_printf (surface->output, "%d 0 obj\n" - "<< /Type /Page\n" + "<< /Type /Page %% %d\n" " /Parent %d 0 R\n" " /MediaBox [ 0 0 %f %f ]\n" " /Contents %d 0 R\n" " /Group <<\n" " /Type /Group\n" " /S /Transparency\n" + " /I true\n" " /CS /DeviceRGB\n" " >>\n" - " /Resources %d 0 R\n" - ">>\n" - "endobj\n", - page.id, + " /Resources %d 0 R\n", + page->id, + page_num, surface->pages_resource.id, surface->width, surface->height, surface->content.id, surface->content_resources.id); - status = _cairo_array_append (&surface->pages, &page); - if (unlikely (status)) - return status; + if (surface->page_parent_tree >= 0) { + _cairo_output_stream_printf (surface->output, + " /StructParents %d\n", + surface->page_parent_tree); + } - status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface); + num_annots = _cairo_array_num_elements (&surface->page_annots); + if (num_annots > 0) { + _cairo_output_stream_printf (surface->output, + " /Annots [ "); + for (i = 0; i < num_annots; i++) { + _cairo_array_copy_element (&surface->page_annots, i, &res); + _cairo_output_stream_printf (surface->output, + "%d 0 R ", + res.id); + } + _cairo_output_stream_printf (surface->output, "]\n"); + } + + if (thumbnail_res.id) { + _cairo_output_stream_printf (surface->output, + " /Thumb %d 0 R\n", + thumbnail_res.id); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, FALSE); if (unlikely (status)) return status; @@ -5141,7 +6992,7 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) static cairo_int_status_t _cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface, - cairo_surface_pattern_t *pattern) + cairo_surface_pattern_t *pattern) { cairo_image_surface_t *image; void *image_extra; @@ -5183,8 +7034,7 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern) * don't think it's worth the extra code to support it. */ /* XXX: Need to write this function here... - content = cairo_surface_get_content (pattern->surface); - if (content == CAIRO_CONTENT_ALPHA) + if (pattern->surface->content == CAIRO_CONTENT_ALPHA) return FALSE; */ @@ -5203,55 +7053,24 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern) return FALSE; } -static cairo_bool_t -_gradient_pattern_supported (const cairo_pattern_t *pattern) -{ - cairo_extend_t extend; - - extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); - - - /* Radial gradients are currently only supported with EXTEND_NONE - * and EXTEND_PAD and when one circle is inside the other. */ - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - double x1, y1, x2, y2, r1, r2, d; - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - - if (extend == CAIRO_EXTEND_REPEAT || - extend == CAIRO_EXTEND_REFLECT) { - return FALSE; - } - - x1 = _cairo_fixed_to_double (radial->c1.x); - y1 = _cairo_fixed_to_double (radial->c1.y); - r1 = _cairo_fixed_to_double (radial->r1); - x2 = _cairo_fixed_to_double (radial->c2.x); - y2 = _cairo_fixed_to_double (radial->c2.y); - r2 = _cairo_fixed_to_double (radial->r2); - - d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); - if (d > fabs(r2 - r1)) { - return FALSE; - } - } - - return TRUE; -} - static cairo_bool_t _pattern_supported (const cairo_pattern_t *pattern) { - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: return TRUE; - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) - return _gradient_pattern_supported (pattern); - - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + case CAIRO_PATTERN_TYPE_SURFACE: return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); - return FALSE; + default: + ASSERT_NOT_REACHED; + return FALSE; + } } static cairo_bool_t @@ -5314,10 +7133,28 @@ _cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->extend == CAIRO_EXTEND_PAD) - return CAIRO_INT_STATUS_UNSUPPORTED; - else - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + if (pattern->extend == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_rectangle_int_t rec_extents; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + + /* Check if surface needs padding to fill extents */ + 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.y) > rec_extents.y + rec_extents.height) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + } + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } } @@ -5372,7 +7209,8 @@ _cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, static cairo_int_status_t _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) { - cairo_status_t status; + cairo_box_double_t bbox; + cairo_int_status_t status; status = _cairo_pdf_surface_close_content_stream (surface); if (unlikely (status)) @@ -5383,51 +7221,234 @@ _cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) return status; _cairo_pdf_group_resources_clear (&surface->resources); - return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE); + bbox.p1.x = 0; + bbox.p1.y = 0; + bbox.p2.x = surface->width; + bbox.p2.y = surface->height; + status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE); + if (unlikely (status)) + return status; + + return _cairo_pdf_interchange_begin_page_content (surface); } +/* If source is an opaque image and mask is an image and both images + * have the same bounding box we can emit them as a image/smask pair. + */ static cairo_int_status_t -_cairo_pdf_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) +_cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_rectangle_int_t *extents) { - cairo_pdf_surface_t *surface = abstract_surface; - cairo_status_t status; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; + cairo_int_status_t status; + cairo_image_surface_t *image; + void *image_extra; + cairo_image_transparency_t transparency; + int src_width, src_height; + int mask_width, mask_height; + double src_x_offset, src_y_offset; + double mask_x_offset, mask_y_offset; + double src_x1, src_y1, src_x2, src_y2; + double mask_x1, mask_y1, mask_x2, mask_y2; + cairo_matrix_t p2u; + double src_radius, mask_radius, e; + cairo_bool_t need_smask; + cairo_pdf_source_surface_entry_t *pdf_source; - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; + /* Check that source and mask are images */ - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; + if (!((source->type == CAIRO_PATTERN_TYPE_SURFACE || source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) && + (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE))) + return CAIRO_INT_STATUS_UNSUPPORTED; - return status; + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *) source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + { + return CAIRO_INT_STATUS_UNSUPPORTED; } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (source->extend != CAIRO_EXTEND_NONE || mask->extend != CAIRO_EXTEND_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that source is opaque and get image sizes */ + + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source, + &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + src_width = image->width; + src_height = image->height; + if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_get_device_offset (&image->base, &src_x_offset, &src_y_offset); + } else { + src_x_offset = 0; + src_y_offset = 0; + } + + transparency = _cairo_image_analyze_transparency (image); + _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra); + + if (transparency != CAIRO_IMAGE_IS_OPAQUE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask, + &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + mask_width = image->width; + mask_height = image->height; + if (mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_get_device_offset (&image->base, &mask_x_offset, &mask_y_offset); + } else { + mask_x_offset = 0; + mask_y_offset = 0; + } + + transparency = _cairo_image_analyze_transparency (image); + need_smask = transparency != CAIRO_IMAGE_IS_OPAQUE; + + _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra); + + /* Check that both images have the same extents with a tolerance + * of half the smallest source pixel. */ + + p2u = source->matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + src_x1 = 0; + src_y1 = 0; + src_x2 = src_width; + src_y2 = src_height; + cairo_matrix_transform_point (&p2u, &src_x1, &src_y1); + cairo_matrix_transform_point (&p2u, &src_x2, &src_y2); + src_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5); + + p2u = mask->matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + mask_x1 = 0; + mask_y1 = 0; + mask_x2 = mask_width; + mask_y2 = mask_height; + cairo_matrix_transform_point (&p2u, &mask_x1, &mask_y1); + cairo_matrix_transform_point (&p2u, &mask_x2, &mask_y2); + mask_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5); + + if (src_radius < mask_radius) + e = src_radius; + else + e = mask_radius; + + if (fabs(src_x1 - mask_x1) > e || + fabs(src_x2 - mask_x2) > e || + fabs(src_y1 - mask_y1) > e || + fabs(src_y2 - mask_y2) > e) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check both images have same device offset */ + if (fabs(src_x_offset - mask_x_offset) > e || + fabs(src_y_offset - mask_y_offset) > e) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (need_smask) { + status = _cairo_pdf_surface_add_source_surface (surface, + NULL, + mask, + op, + source->filter, + FALSE, /* stencil mask */ + TRUE, /* smask */ + FALSE, /* need_transp_group */ + extents, + NULL, /* smask_res */ + &pdf_source, + NULL, + NULL, + NULL); if (unlikely (status)) return status; } - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; - status = _cairo_pdf_surface_select_operator (surface, op); + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, extents, + 1.0, /* alpha */ + need_smask ? &pdf_source->surface_res : NULL, + FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + + status = _cairo_output_stream_get_status (surface->output); + + + return status; +} + +/* A PDF stencil mask is an A1 mask used with the current color */ +static cairo_int_status_t +_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_rectangle_int_t *extents) +{ + cairo_int_status_t status; + cairo_image_surface_t *image; + void *image_extra; + cairo_image_transparency_t transparency; + cairo_pdf_resource_t pattern_res = {0}; + + if (! (source->type == CAIRO_PATTERN_TYPE_SOLID && + (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask, + &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->base.status) + return image->base.status; + + transparency = _cairo_image_analyze_transparency (image); + if (transparency != CAIRO_IMAGE_IS_OPAQUE && + transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + status = _cairo_pdf_surface_select_pattern (surface, source, + pattern_res, FALSE); if (unlikely (status)) return status; @@ -5435,54 +7456,135 @@ _cairo_pdf_surface_paint (void *abstract_surface, if (unlikely (status)) return status; - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - source->extend == CAIRO_EXTEND_NONE) - { - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) source); + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, 1.0, NULL, TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + + status = _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface, + cairo_composite_rectangles_t *composite) +{ + cairo_clip_t *clip = composite->clip; + + if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) + clip = NULL; + + if (clip == NULL) { + if (_cairo_composite_rectangles_can_reduce_clip (composite, + surface->clipper.clip)) + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_surface_clipper_set_clip (&surface->clipper, clip); +} + +static cairo_int_status_t +_cairo_pdf_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_smask_group_t *group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + &surface->base, + op, source, clip); + if (unlikely (status)) + return status; + + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + return status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup; + } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { + status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) - return status; + goto cleanup; + } + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + + status = _cairo_pdf_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup; + + if (_can_paint_pattern (source)) { + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_pattern (surface, + op, + source, + &extents.bounded, + 1.0, /* alpha */ + FALSE); /* mask */ + if (unlikely (status)) + goto cleanup; _cairo_output_stream_printf (surface->output, "Q\n"); + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); } pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; if (unlikely (status)) - return status; + goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); + if (unlikely (group == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } group->operation = PDF_PAINT; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->source_res = pattern_res; status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", @@ -5492,215 +7594,292 @@ _cairo_pdf_surface_paint (void *abstract_surface, status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); + "%d %d %d %d re f\n", + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) - return status; + goto cleanup; } + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t -_cairo_pdf_surface_mask (void *abstract_surface, +_cairo_pdf_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; - cairo_status_t status; cairo_composite_rectangles_t extents; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; + cairo_int_status_t status; + cairo_rectangle_int_t r; + cairo_box_t box; + double alpha; status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, + &surface->base, op, source, mask, clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - + if (unlikely (status)) + return status; + + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) return status; - } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - cairo_status_t source_status, mask_status; + cairo_int_status_t source_status, mask_status; - source_status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - if (_cairo_status_is_error (source_status)) - return source_status; + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + if (_cairo_int_status_is_error (status)) + goto cleanup; + source_status = status; if (mask->has_component_alpha) { - mask_status = CAIRO_INT_STATUS_UNSUPPORTED; + status = CAIRO_INT_STATUS_UNSUPPORTED; } else { - mask_status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded); - if (_cairo_status_is_error (mask_status)) - return mask_status; + status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded); + if (_cairo_int_status_is_error (status)) + goto cleanup; } + mask_status = status; + _cairo_composite_rectangles_fini (&extents); return _cairo_analysis_surface_merge_status (source_status, mask_status); } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) - return status; + goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + /* get the accurate extents */ + status = _cairo_pattern_get_ink_extents (source, &r); if (unlikely (status)) - return status; + goto cleanup; - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + /* XXX slight impedance mismatch */ + _cairo_box_from_rectangle (&box, &r); + status = _cairo_composite_rectangles_intersect_source_extents (&extents, + &box); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pattern_get_ink_extents (mask, &r); + if (unlikely (status)) + goto cleanup; + + _cairo_box_from_rectangle (&box, &r); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, + &box); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + goto cleanup; + + /* Check if we can combine source and mask into a smask image */ + status = _cairo_pdf_surface_emit_combined_smask (surface, op, source, mask, &extents.bounded); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto cleanup; + + /* Check if we can use a stencil mask */ + status = _cairo_pdf_surface_emit_stencil_mask (surface, op, source, mask, &extents.bounded); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto cleanup; + + /* Check if we can set ca/CA instead of an smask. We could handle + * other source patterns as well but for now this is the easiest, + * and most common, case to handle. */ + if (_cairo_pattern_is_constant_alpha (mask, &extents.bounded, &alpha) && + _can_paint_pattern (source)) { + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, "q\n"); + status = _cairo_pdf_surface_paint_pattern (surface, + op, + source, + &extents.bounded, + alpha, + FALSE); /* mask */ + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, "Q\n"); + _cairo_composite_rectangles_fini (&extents); + return _cairo_output_stream_get_status (surface->output); + } + + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); + if (unlikely (group == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } group->operation = PDF_MASK; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } status = _cairo_pattern_create_copy (&group->mask, mask); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->source_res = _cairo_pdf_surface_new_object (surface); if (group->source_res.id == 0) { _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; } status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, group->group_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->source_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", group->group_res.id, group->source_res.id); + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t _cairo_pdf_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; + cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, + &surface->base, op, source, path, style, ctm, clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - + if (unlikely (status)) return status; - } /* use the more accurate extents */ if (extents.is_bounded) { + cairo_rectangle_int_t mask; + cairo_box_t box; + status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, - &extents.mask); + &mask); if (unlikely (status)) - return status; + goto cleanup; - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&box, &mask); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, + &box); + if (unlikely (status)) + goto cleanup; } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + goto cleanup; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup; + } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup; pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) - return status; + goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); + if (unlikely (group == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } group->operation = PDF_STROKE; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->source_res = pattern_res; status = _cairo_path_fixed_init_copy (&group->path, path); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->style = *style; @@ -5709,20 +7888,20 @@ _cairo_pdf_surface_stroke (void *abstract_surface, status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", @@ -5731,7 +7910,7 @@ _cairo_pdf_surface_stroke (void *abstract_surface, } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_stroke (&surface->pdf_operators, path, @@ -5739,146 +7918,156 @@ _cairo_pdf_surface_stroke (void *abstract_surface, ctm, ctm_inverse); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) - return status; + goto cleanup; } + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t _cairo_pdf_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, + &surface->base, op, source, path, clip); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - + if (unlikely (status)) return status; - } /* use the more accurate extents */ if (extents.is_bounded) { + cairo_rectangle_int_t mask; + cairo_box_t box; + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &extents.mask); + &mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&box, &mask); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, + &box); + if (unlikely (status)) + goto cleanup; } + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + goto cleanup; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup; } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { status = _cairo_pdf_surface_start_fallback (surface); if (unlikely (status)) - return status; + goto cleanup; } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) - return status; + goto cleanup; - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - source->extend == CAIRO_EXTEND_NONE) - { + if (_can_paint_pattern (source)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "q\n"); status = _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); if (unlikely (status)) - return status; + goto cleanup; - status = _cairo_pdf_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) source); + status = _cairo_pdf_surface_paint_pattern (surface, + op, + source, + &extents.bounded, + 1.0, /* alpha */ + FALSE); /* mask */ if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "Q\n"); - return _cairo_output_stream_get_status (surface->output); + status = _cairo_output_stream_get_status (surface->output); + goto cleanup; } pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; if (unlikely (status)) - return status; + goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); + if (unlikely (group == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } group->operation = PDF_FILL; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->source_res = pattern_res; status = _cairo_path_fixed_init_copy (&group->path, path); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->fill_rule = fill_rule; status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", @@ -5887,20 +8076,25 @@ _cairo_pdf_surface_fill (void *abstract_surface, } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) - return status; + goto cleanup; } + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t @@ -5910,7 +8104,7 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, @@ -5918,12 +8112,12 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; - cairo_status_t status; + cairo_int_status_t status; cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; - cairo_rectangle_int_t extents; + cairo_composite_rectangles_t extents; /* During analysis we return unsupported and let the _fill and * _stroke functions that are on the fallback path do the analysis @@ -5947,50 +8141,87 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, if (fill_op != stroke_op) return CAIRO_INT_STATUS_UNSUPPORTED; - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + /* Compute the operation extents using the stroke which will naturally + * be larger than the fill extents. + */ + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, + stroke_op, stroke_source, + path, stroke_style, stroke_ctm, + clip); if (unlikely (status)) return status; + /* use the more accurate extents */ + if (extents.is_bounded) { + cairo_rectangle_int_t mask; + cairo_box_t box; + + status = _cairo_path_fixed_stroke_extents (path, stroke_style, + stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, + &mask); + if (unlikely (status)) + goto cleanup; + + _cairo_box_from_rectangle (&box, &mask); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, + &box); + if (unlikely (status)) + goto cleanup; + } + + status = _cairo_pdf_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup; + status = _cairo_pdf_surface_select_operator (surface, fill_op); if (unlikely (status)) - return status; + goto cleanup; - status = _cairo_surface_fill_extents (&surface->base, - fill_op, fill_source, path, fill_rule, - fill_tolerance, fill_antialias, - clip, &extents); + /* use the more accurate extents */ + if (extents.is_bounded) { + cairo_rectangle_int_t mask; + cairo_box_t box; + + _cairo_path_fixed_fill_extents (path, + fill_rule, + fill_tolerance, + &mask); + + _cairo_box_from_rectangle (&box, &mask); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, + &box); + if (unlikely (status)) + goto cleanup; + } + + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); if (unlikely (status)) - return status; - + goto cleanup; fill_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, - &extents, + fill_op, + &extents.bounded, &fill_pattern_res, &gstate_res); if (unlikely (status)) - return status; + goto cleanup; assert (gstate_res.id == 0); - status = _cairo_surface_stroke_extents (&surface->base, - stroke_op, stroke_source, path, - stroke_style, stroke_ctm, stroke_ctm_inverse, - stroke_tolerance, stroke_antialias, - clip, &extents); - if (unlikely (status)) - return status; - stroke_pattern_res.id = 0; gstate_res.id = 0; status = _cairo_pdf_surface_add_pdf_pattern (surface, stroke_source, - &extents, + stroke_op, + &extents.bounded, &stroke_pattern_res, &gstate_res); if (unlikely (status)) - return status; + goto cleanup; assert (gstate_res.id == 0); @@ -5999,12 +8230,12 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, status = _cairo_pdf_surface_select_pattern (surface, fill_source, fill_pattern_res, FALSE); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_select_pattern (surface, stroke_source, stroke_pattern_res, TRUE); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, path, @@ -6013,13 +8244,18 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, stroke_ctm, stroke_ctm_inverse); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) - return status; + goto cleanup; + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_bool_t @@ -6040,75 +8276,73 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; cairo_bool_t overlap; - cairo_status_t status; - - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; + cairo_int_status_t status; status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, + &surface->base, op, source, scaled_font, glyphs, num_glyphs, clip, &overlap); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_STATUS_SUCCESS; - + if (unlikely (status)) return status; - } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); + if (unlikely (status)) + return status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup; + } assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_pdf_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup; pattern_res.id = 0; gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, &extents.bounded, &pattern_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_select_operator (surface, op); if (unlikely (status)) - return status; + goto cleanup; if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface); - if (unlikely (group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); + if (unlikely (group == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } group->operation = PDF_SHOW_GLYPHS; status = _cairo_pattern_create_copy (&group->source, source); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } group->source_res = pattern_res; if (utf8_len) { - group->utf8 = malloc (utf8_len); + group->utf8 = _cairo_malloc (utf8_len); if (unlikely (group->utf8 == NULL)) { _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; } memcpy (group->utf8, utf8, utf8_len); } @@ -6118,7 +8352,8 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (group->glyphs == NULL)) { _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; } memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); } @@ -6128,7 +8363,8 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); if (unlikely (group->clusters == NULL)) { _cairo_pdf_smask_group_destroy (group); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; } memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters); } @@ -6138,20 +8374,20 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, status = _cairo_pdf_surface_add_smask_group (surface, group); if (unlikely (status)) { _cairo_pdf_smask_group_destroy (group); - return status; + goto cleanup; } status = _cairo_pdf_surface_add_smask (surface, gstate_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_add_xobject (surface, group->group_res); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup; _cairo_output_stream_printf (surface->output, "q /s%d gs /x%d Do Q\n", @@ -6160,7 +8396,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, } else { status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); if (unlikely (status)) - return status; + goto cleanup; /* Each call to show_glyphs() with a transclucent pattern must * be in a separate text object otherwise overlapping text @@ -6169,7 +8405,7 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, if (! _cairo_pattern_is_opaque (source, &extents.bounded)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup; } status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, @@ -6179,65 +8415,107 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, cluster_flags, scaled_font); if (unlikely (status)) - return status; + goto cleanup; status = _cairo_pdf_surface_unselect_pattern (surface); if (unlikely (status)) - return status; + goto cleanup; } + _cairo_composite_rectangles_fini (&extents); return _cairo_output_stream_get_status (surface->output); + +cleanup: + _cairo_composite_rectangles_fini (&extents); + return status; } +static const char ** +_cairo_pdf_surface_get_supported_mime_types (void *abstract_surface) +{ + return _cairo_pdf_supported_mime_types; +} -static void +static cairo_int_status_t +_cairo_pdf_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status = 0; + + if (begin) + status = _cairo_pdf_interchange_tag_begin (surface, tag_name, attributes); + else + status = _cairo_pdf_interchange_tag_end (surface, tag_name); + + return status; +} + +static cairo_int_status_t _cairo_pdf_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status; surface->paginated_mode = paginated_mode; + status = _cairo_pdf_interchange_begin_page_content (surface); + if (unlikely (status)) + return status; + + if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + } + + return CAIRO_INT_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_pdf_surface_backend = { CAIRO_SURFACE_TYPE_PDF, - NULL, /* create similar: handled by wrapper */ _cairo_pdf_surface_finish, + + _cairo_default_context_create, + + NULL, /* create similar: handled by wrapper */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* _cairo_pdf_surface_copy_page */ _cairo_pdf_surface_show_page, + _cairo_pdf_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_pdf_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* Here are the drawing functions */ - _cairo_pdf_surface_paint, _cairo_pdf_surface_mask, _cairo_pdf_surface_stroke, _cairo_pdf_surface_fill, - NULL, /* show_glyphs */ - NULL, /* snapshot */ - - NULL, /* is_compatible */ _cairo_pdf_surface_fill_stroke, - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* show_glyphs */ _cairo_pdf_surface_has_show_text_glyphs, _cairo_pdf_surface_show_text_glyphs, + _cairo_pdf_surface_get_supported_mime_types, + _cairo_pdf_surface_tag, }; static const cairo_paginated_surface_backend_t @@ -6247,4 +8525,6 @@ cairo_pdf_surface_paginated_backend = { NULL, /* set_bounding_box */ _cairo_pdf_surface_has_fallback_images, _cairo_pdf_surface_supports_fine_grained_fallbacks, + _cairo_pdf_surface_requires_thumbnail_image, + _cairo_pdf_surface_set_thumbnail_image, }; diff --git a/gfx/cairo/cairo/src/cairo-pdf.h b/gfx/cairo/cairo/src/cairo-pdf.h index 50460ccdfbdc..5be0b3f1bea2 100644 --- a/gfx/cairo/cairo/src/cairo-pdf.h +++ b/gfx/cairo/cairo/src/cairo-pdf.h @@ -45,14 +45,14 @@ CAIRO_BEGIN_DECLS /** * cairo_pdf_version_t: - * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. - * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. + * @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. (Since 1.10) + * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. (Since 1.10) * * #cairo_pdf_version_t is used to describe the version number of the PDF * specification that a generated PDF file will conform to. * - * Since 1.10 - */ + * Since: 1.10 + **/ typedef enum _cairo_pdf_version { CAIRO_PDF_VERSION_1_4, CAIRO_PDF_VERSION_1_5 @@ -85,6 +85,73 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points); +/** + * cairo_pdf_outline_flags_t: + * @CAIRO_PDF_OUTLINE_FLAG_OPEN: The outline item defaults to open in the PDF viewer (Since 1.16) + * @CAIRO_PDF_OUTLINE_FLAG_BOLD: The outline item is displayed by the viewer in bold text (Since 1.16) + * @CAIRO_PDF_OUTLINE_FLAG_ITALIC: The outline item is displayed by the viewer in italic text (Since 1.16) + * + * #cairo_pdf_outline_flags_t is used by the + * cairo_pdf_surface_add_outline() function specify the attributes of + * an outline item. These flags may be bitwise-or'd to produce any + * combination of flags. + * + * Since: 1.16 + **/ +typedef enum _cairo_pdf_outline_flags { + CAIRO_PDF_OUTLINE_FLAG_OPEN = 0x1, + CAIRO_PDF_OUTLINE_FLAG_BOLD = 0x2, + CAIRO_PDF_OUTLINE_FLAG_ITALIC = 0x4, +} cairo_pdf_outline_flags_t; + +#define CAIRO_PDF_OUTLINE_ROOT 0 + +cairo_public int +cairo_pdf_surface_add_outline (cairo_surface_t *surface, + int parent_id, + const char *utf8, + const char *link_attribs, + cairo_pdf_outline_flags_t flags); + +/** + * cairo_pdf_metadata_t: + * @CAIRO_PDF_METADATA_TITLE: The document title (Since 1.16) + * @CAIRO_PDF_METADATA_AUTHOR: The document author (Since 1.16) + * @CAIRO_PDF_METADATA_SUBJECT: The document subject (Since 1.16) + * @CAIRO_PDF_METADATA_KEYWORDS: The document keywords (Since 1.16) + * @CAIRO_PDF_METADATA_CREATOR: The document creator (Since 1.16) + * @CAIRO_PDF_METADATA_CREATE_DATE: The document creation date (Since 1.16) + * @CAIRO_PDF_METADATA_MOD_DATE: The document modification date (Since 1.16) + * + * #cairo_pdf_metadata_t is used by the + * cairo_pdf_surface_set_metadata() function specify the metadata to set. + * + * Since: 1.16 + **/ +typedef enum _cairo_pdf_metadata { + CAIRO_PDF_METADATA_TITLE, + CAIRO_PDF_METADATA_AUTHOR, + CAIRO_PDF_METADATA_SUBJECT, + CAIRO_PDF_METADATA_KEYWORDS, + CAIRO_PDF_METADATA_CREATOR, + CAIRO_PDF_METADATA_CREATE_DATE, + CAIRO_PDF_METADATA_MOD_DATE, +} cairo_pdf_metadata_t; + +cairo_public void +cairo_pdf_surface_set_metadata (cairo_surface_t *surface, + cairo_pdf_metadata_t metadata, + const char *utf8); + +cairo_public void +cairo_pdf_surface_set_page_label (cairo_surface_t *surface, + const char *utf8); + +cairo_public void +cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface, + int width, + int height); + CAIRO_END_DECLS #else /* CAIRO_HAS_PDF_SURFACE */ diff --git a/gfx/cairo/cairo/src/cairo-pen.c b/gfx/cairo/cairo/src/cairo-pen.c index e71f7b561b41..9bf960423d02 100644 --- a/gfx/cairo/cairo/src/cairo-pen.c +++ b/gfx/cairo/cairo/src/cairo-pen.c @@ -41,11 +41,6 @@ #include "cairo-error-private.h" #include "cairo-slope-private.h" -static int -_cairo_pen_vertices_needed (double tolerance, - double radius, - const cairo_matrix_t *matrix); - static void _cairo_pen_compute_slopes (cairo_pen_t *pen); @@ -88,10 +83,12 @@ _cairo_pen_init (cairo_pen_t *pen, * is reflecting */ for (i=0; i < pen->num_vertices; i++) { - double theta = 2 * M_PI * i / (double) pen->num_vertices; - double dx = radius * cos (reflect ? -theta : theta); - double dy = radius * sin (reflect ? -theta : theta); cairo_pen_vertex_t *v = &pen->vertices[i]; + double theta = 2 * M_PI * i / (double) pen->num_vertices, dx, dy; + if (reflect) + theta = -theta; + dx = radius * cos (theta); + dy = radius * sin (theta); cairo_matrix_transform_distance (ctm, &dx, &dy); v->point.x = _cairo_fixed_from_double (dx); v->point.y = _cairo_fixed_from_double (dy); @@ -109,7 +106,7 @@ _cairo_pen_fini (cairo_pen_t *pen) free (pen->vertices); - VG (VALGRIND_MAKE_MEM_NOACCESS (pen, sizeof (cairo_pen_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); } cairo_status_t @@ -273,7 +270,7 @@ Note that this also equation works for M == m (a circle) as it doesn't matter where on the circle the error is computed. */ -static int +int _cairo_pen_vertices_needed (double tolerance, double radius, const cairo_matrix_t *matrix) @@ -283,21 +280,16 @@ _cairo_pen_vertices_needed (double tolerance, * compute major axis length for a pen with the specified radius. * we don't need the minor axis length. */ + double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, + radius); + int num_vertices; - double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix, - radius); - - /* - * compute number of vertices needed - */ - int num_vertices; - - /* Where tolerance / M is > 1, we use 4 points */ - if (tolerance >= major_axis) { + if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */ + num_vertices = 1; + } else if (tolerance >= major_axis) { num_vertices = 4; } else { - double delta = acos (1 - tolerance / major_axis); - num_vertices = ceil (M_PI / delta); + num_vertices = ceil (2*M_PI / acos (1 - tolerance / major_axis)); /* number of vertices must be even */ if (num_vertices % 2) @@ -396,3 +388,88 @@ _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, return i; } + +void +_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen, + const cairo_slope_t *in, + const cairo_slope_t *out, + int *start, int *stop) +{ + + int lo = 0, hi = pen->num_vertices; + int i; + + i = (lo + hi) >> 1; + do { + if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) + lo = i; + else + hi = i; + i = (lo + hi) >> 1; + } while (hi - lo > 1); + if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) + if (++i == pen->num_vertices) + i = 0; + *start = i; + + if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) { + lo = i; + hi = i + pen->num_vertices; + i = (lo + hi) >> 1; + do { + int j = i; + if (j >= pen->num_vertices) + j -= pen->num_vertices; + if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0) + hi = i; + else + lo = i; + i = (lo + hi) >> 1; + } while (hi - lo > 1); + if (i >= pen->num_vertices) + i -= pen->num_vertices; + } + *stop = i; +} + +void +_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen, + const cairo_slope_t *in, + const cairo_slope_t *out, + int *start, int *stop) +{ + int lo = 0, hi = pen->num_vertices; + int i; + + i = (lo + hi) >> 1; + do { + if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0) + lo = i; + else + hi = i; + i = (lo + hi) >> 1; + } while (hi - lo > 1); + if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0) + if (++i == pen->num_vertices) + i = 0; + *start = i; + + if (_cairo_slope_compare (&pen->vertices[i].slope_cw, out) <= 0) { + lo = i; + hi = i + pen->num_vertices; + i = (lo + hi) >> 1; + do { + int j = i; + if (j >= pen->num_vertices) + j -= pen->num_vertices; + if (_cairo_slope_compare (out, &pen->vertices[j].slope_ccw) > 0) + hi = i; + else + lo = i; + i = (lo + hi) >> 1; + } while (hi - lo > 1); + if (i >= pen->num_vertices) + i -= pen->num_vertices; + } + *stop = i; +} diff --git a/gfx/cairo/cairo/src/cairo-pixman-private.h b/gfx/cairo/cairo/src/cairo-pixman-private.h new file mode 100644 index 000000000000..d705025c8ee7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pixman-private.h @@ -0,0 +1,51 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright ©2013 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_PIXMAN_PRIVATE_H +#define CAIRO_PIXMAN_PRIVATE_H + +#include "cairo-pixman-private.h" /* keep make check happy */ + +#include + +#if PIXMAN_VERSION < PIXMAN_VERSION_ENCODE(0,22,0) +#define pixman_image_composite32 pixman_image_composite +#define pixman_image_get_component_alpha(i) 0 +#define pixman_image_set_component_alpha(i, x) do { } while (0) +#endif + +#endif diff --git a/gfx/cairo/cairo/src/cairo-png.c b/gfx/cairo/cairo/src/cairo-png.c index 41a33d75338f..f576047b19c9 100644 --- a/gfx/cairo/cairo/src/cairo-png.c +++ b/gfx/cairo/cairo/src/cairo-png.c @@ -39,6 +39,7 @@ #include "cairoint.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" #include "cairo-output-stream-private.h" #include @@ -53,7 +54,15 @@ * * The PNG functions allow reading PNG images into image surfaces, and writing * any surface to a PNG file. - */ + * + * It is a toy API. It only offers very simple support for reading and + * writing PNG files, which is sufficient for testing and + * demonstration purposes. Applications which need more control over + * the generated PNG file should access the pixel data directly, using + * cairo_image_surface_get_data() or a backend-specific access + * function, and process it with another library, e.g. gdk-pixbuf or + * libpng. + **/ /** * CAIRO_HAS_PNG_FUNCTIONS: @@ -61,7 +70,9 @@ * Defined if the PNG functions are available. * This macro can be used to conditionally compile code using the cairo * PNG functions. - */ + * + * Since: 1.0 + **/ struct png_read_closure_t { cairo_read_func_t read_func; @@ -94,6 +105,84 @@ unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) } } +static uint16_t f_to_u16(float val) +{ + if (val < 0) + return 0; + else if (val > 1) + return 65535; + else + return (uint16_t)(val * 65535.f); +} + +static void +unpremultiply_float (float *f, uint16_t *d16, unsigned width) +{ + unsigned int i; + + for (i = 0; i < width; i++) { + float r, g, b, a; + + r = *f++; + g = *f++; + b = *f++; + a = *f++; + + if (a > 0) { + *d16++ = f_to_u16(r / a); + *d16++ = f_to_u16(g / a); + *d16++ = f_to_u16(b / a); + *d16++ = f_to_u16(a); + } else { + *d16++ = 0; + *d16++ = 0; + *d16++ = 0; + *d16++ = 0; + } + } +} + +static void +premultiply_float (float *f, const uint16_t *d16, unsigned int width) +{ + unsigned int i = width; + + /* Convert d16 in place back to float */ + while (i--) { + float a = d16[i * 4 + 3] / 65535.f; + + f[i * 4 + 3] = a; + f[i * 4 + 2] = (float)d16[i * 4 + 2] / 65535.f * a; + f[i * 4 + 1] = (float)d16[i * 4 + 1] / 65535.f * a; + f[i * 4] = (float)d16[i * 4] / 65535.f * a; + } +} + +static void convert_u16_to_float (float *f, const uint16_t *d16, unsigned int width) +{ + /* Convert d16 in place back to float */ + unsigned int i = width; + + while (i--) { + f[i * 3 + 2] = (float)d16[i * 4 + 2] / 65535.f; + f[i * 3 + 1] = (float)d16[i * 4 + 1] / 65535.f; + f[i * 3] = (float)d16[i * 4] / 65535.f; + } +} + +static void +convert_float_to_u16 (float *f, uint16_t *d16, unsigned int width) +{ + unsigned int i; + + for (i = 0; i < width; i++) { + *d16++ = f_to_u16(*f++); + *d16++ = f_to_u16(*f++); + *d16++ = f_to_u16(*f++); + *d16++ = 0; + } +} + /* Converts native endian xRGB => RGBx bytes */ static void convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) @@ -125,7 +214,7 @@ png_simple_error_callback (png_structp png, /* default to the most likely error */ if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); + *error = _cairo_error (CAIRO_STATUS_PNG_ERROR); #ifdef PNG_SETJMP_SUPPORTED longjmp (png_jmpbuf (png), 1); @@ -138,13 +227,13 @@ static void png_simple_warning_callback (png_structp png, png_const_charp error_msg) { - cairo_status_t *error = png_get_error_ptr (png); - - /* default to the most likely error */ - if (*error == CAIRO_STATUS_SUCCESS) - *error = _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* png does not expect to abort and will try to tidy up after a warning */ + /* png does not expect to abort and will try to tidy up and continue + * loading the image after a warning. So we also want to return the + * (incorrect?) surface. + * + * We use our own warning callback to squelch any attempts by libpng + * to write to stderr as we may not be in control of that output. + */ } @@ -161,7 +250,7 @@ write_png (cairo_surface_t *surface, void *closure) { int i; - cairo_status_t status; + cairo_int_status_t status; cairo_image_surface_t *image; cairo_image_surface_t * volatile clone; void *image_extra; @@ -170,7 +259,8 @@ write_png (cairo_surface_t *surface, png_byte **volatile rows = NULL; png_color_16 white; int png_color_type; - int depth; + int bpc; + unsigned char *volatile u16_copy = NULL; status = _cairo_surface_acquire_source_image (surface, &image, @@ -187,11 +277,22 @@ write_png (cairo_surface_t *surface, goto BAIL1; } - /* Handle the various fallback formats (e.g. low bit-depth XServers) - * by coercing them to a simpler format using pixman. - */ - clone = _cairo_image_surface_coerce (image); - status = clone->base.status; + /* Don't coerce to a lower resolution format */ + if (image->format == CAIRO_FORMAT_RGB96F || + image->format == CAIRO_FORMAT_RGBA128F) { + u16_copy = _cairo_malloc_ab (image->width * 8, image->height); + if (!u16_copy) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL1; + } + clone = (cairo_image_surface_t *)cairo_surface_reference (&image->base); + } else { + /* Handle the various fallback formats (e.g. low bit-depth XServers) + * by coercing them to a simpler format using pixman. + */ + clone = _cairo_image_surface_coerce (image); + status = clone->base.status; + } if (unlikely (status)) goto BAIL1; @@ -201,8 +302,22 @@ write_png (cairo_surface_t *surface, goto BAIL2; } - for (i = 0; i < clone->height; i++) - rows[i] = (png_byte *) clone->data + i * clone->stride; + if (!u16_copy) { + for (i = 0; i < clone->height; i++) + rows[i] = (png_byte *)clone->data + i * clone->stride; + } else { + for (i = 0; i < clone->height; i++) { + float *float_line = (float *)&clone->data[i * clone->stride]; + uint16_t *u16_line = (uint16_t *)&u16_copy[i * clone->width * 8]; + + if (image->format == CAIRO_FORMAT_RGBA128F) + unpremultiply_float (float_line, u16_line, clone->width); + else + convert_float_to_u16 (float_line, u16_line, clone->width); + + rows[i] = (png_byte *)u16_line; + } + } png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status, png_simple_error_callback, @@ -227,27 +342,39 @@ write_png (cairo_surface_t *surface, switch (clone->format) { case CAIRO_FORMAT_ARGB32: - depth = 8; + bpc = 8; if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE) png_color_type = PNG_COLOR_TYPE_RGB; else png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; + case CAIRO_FORMAT_RGB30: + bpc = 10; + png_color_type = PNG_COLOR_TYPE_RGB; + break; case CAIRO_FORMAT_RGB24: - depth = 8; + bpc = 8; png_color_type = PNG_COLOR_TYPE_RGB; break; case CAIRO_FORMAT_A8: - depth = 8; + bpc = 8; png_color_type = PNG_COLOR_TYPE_GRAY; break; case CAIRO_FORMAT_A1: - depth = 1; + bpc = 1; png_color_type = PNG_COLOR_TYPE_GRAY; #ifndef WORDS_BIGENDIAN png_set_packswap (png); #endif break; + case CAIRO_FORMAT_RGB96F: + bpc = 16; + png_color_type = PNG_COLOR_TYPE_RGB; + break; + case CAIRO_FORMAT_RGBA128F: + bpc = 16; + png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + break; case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: default: @@ -257,13 +384,13 @@ write_png (cairo_surface_t *surface, png_set_IHDR (png, info, clone->width, - clone->height, depth, + clone->height, bpc, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - white.gray = (1 << depth) - 1; + white.gray = (1 << bpc) - 1; white.red = white.blue = white.green = white.gray; png_set_bKGD (png, info, &white); @@ -281,9 +408,11 @@ write_png (cairo_surface_t *surface, png_write_info (png, info); if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - png_set_write_user_transform_fn (png, unpremultiply_data); + if (clone->format != CAIRO_FORMAT_RGBA128F) + png_set_write_user_transform_fn (png, unpremultiply_data); } else if (png_color_type == PNG_COLOR_TYPE_RGB) { - png_set_write_user_transform_fn (png, convert_data_to_bytes); + if (clone->format != CAIRO_FORMAT_RGB96F) + png_set_write_user_transform_fn (png, convert_data_to_bytes); png_set_filler (png, 0, PNG_FILLER_AFTER); } @@ -296,6 +425,7 @@ BAIL3: free (rows); BAIL2: cairo_surface_destroy (&clone->base); + free (u16_copy); BAIL1: _cairo_surface_release_source_image (surface, image, image_extra); @@ -324,7 +454,8 @@ stdio_write_func (png_structp png, png_bytep data, png_size_t size) /** * cairo_surface_write_to_png: * @surface: a #cairo_surface_t with pixel contents - * @filename: the name of a file to write to + * @filename: the name of a file to write to; on Windows this filename + * is encoded in UTF-8. * * Writes the contents of @surface to a new file @filename as a PNG * image. @@ -334,7 +465,10 @@ stdio_write_func (png_structp png, png_bytep data, png_size_t size) * be allocated for the operation or * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs - * while attempting to write the file. + * while attempting to write the file, or %CAIRO_STATUS_PNG_ERROR if libpng + * returned an error. + * + * Since: 1.0 **/ cairo_status_t cairo_surface_write_to_png (cairo_surface_t *surface, @@ -349,7 +483,11 @@ cairo_surface_write_to_png (cairo_surface_t *surface, if (surface->finished) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - fp = fopen (filename, "wb"); + status = _cairo_fopen (filename, "wb", &fp); + + if (status != CAIRO_STATUS_SUCCESS) + return _cairo_error (status); + if (fp == NULL) { switch (errno) { case ENOMEM: @@ -400,7 +538,10 @@ stream_write_func (png_structp png, png_bytep data, png_size_t size) * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if * memory could not be allocated for the operation, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have - * pixel contents. + * pixel contents, or %CAIRO_STATUS_PNG_ERROR if libpng + * returned an error. + * + * Since: 1.0 **/ cairo_status_t cairo_surface_write_to_png_stream (cairo_surface_t *surface, @@ -454,7 +595,7 @@ premultiply_data (png_structp png, green = multiply_alpha (alpha, green); blue = multiply_alpha (alpha, blue); } - p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + p = ((uint32_t)alpha << 24) | (red << 16) | (green << 8) | (blue << 0); } memcpy (base, &p, sizeof (uint32_t)); } @@ -473,7 +614,7 @@ convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) uint8_t blue = base[2]; uint32_t pixel; - pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); + pixel = (0xffu << 24) | (red << 16) | (green << 8) | (blue << 0); memcpy (base, &pixel, sizeof (uint32_t)); } } @@ -518,11 +659,11 @@ stream_read_func (png_structp png, png_bytep data, png_size_t size) static cairo_surface_t * read_png (struct png_read_closure_t *png_closure) { - cairo_surface_t *surface; + cairo_surface_t * volatile surface; png_struct *png = NULL; png_info *info; - png_byte *data = NULL; - png_byte **row_pointers = NULL; + png_byte * volatile data = NULL; + png_byte ** volatile row_pointers = NULL; png_uint_32 png_width, png_height; int depth, color_type, interlace, stride; unsigned int i; @@ -586,9 +727,6 @@ read_png (struct png_read_closure_t *png_closure) if (png_get_valid (png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha (png); - if (depth == 16) - png_set_strip_16 (png); - if (depth < 8) png_set_packing (png); @@ -609,7 +747,7 @@ read_png (struct png_read_closure_t *png_closure) png_get_IHDR (png, info, &png_width, &png_height, &depth, &color_type, &interlace, NULL, NULL); - if (depth != 8 || + if ((depth != 8 && depth != 16) || ! (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { @@ -623,13 +761,21 @@ read_png (struct png_read_closure_t *png_closure) /* fall-through just in case ;-) */ case PNG_COLOR_TYPE_RGB_ALPHA: - format = CAIRO_FORMAT_ARGB32; - png_set_read_user_transform_fn (png, premultiply_data); + if (depth == 8) { + format = CAIRO_FORMAT_ARGB32; + png_set_read_user_transform_fn (png, premultiply_data); + } else { + format = CAIRO_FORMAT_RGBA128F; + } break; case PNG_COLOR_TYPE_RGB: - format = CAIRO_FORMAT_RGB24; - png_set_read_user_transform_fn (png, convert_bytes_to_data); + if (depth == 8) { + format = CAIRO_FORMAT_RGB24; + png_set_read_user_transform_fn (png, convert_bytes_to_data); + } else { + format = CAIRO_FORMAT_RGB96F; + } break; } @@ -652,7 +798,7 @@ read_png (struct png_read_closure_t *png_closure) } for (i = 0; i < png_height; i++) - row_pointers[i] = &data[i * stride]; + row_pointers[i] = &data[i * (ptrdiff_t)stride]; png_read_image (png, row_pointers); png_read_end (png, info); @@ -662,6 +808,26 @@ read_png (struct png_read_closure_t *png_closure) goto BAIL; } + if (format == CAIRO_FORMAT_RGBA128F) { + i = png_height; + + while (i--) { + float *float_line = (float *)row_pointers[i]; + uint16_t *u16_line = (uint16_t *)row_pointers[i]; + + premultiply_float (float_line, u16_line, png_width); + } + } else if (format == CAIRO_FORMAT_RGB96F) { + i = png_height; + + while (i--) { + float *float_line = (float *)row_pointers[i]; + uint16_t *u16_line = (uint16_t *)row_pointers[i]; + + convert_u16_to_float (float_line, u16_line, png_width); + } + } + surface = cairo_image_surface_create_for_data (data, format, png_width, png_height, stride); @@ -697,10 +863,8 @@ read_png (struct png_read_closure_t *png_closure) } BAIL: - if (row_pointers != NULL) - free (row_pointers); - if (data != NULL) - free (data); + free (row_pointers); + free (data); if (png != NULL) png_destroy_read_struct (&png, &info, NULL); if (png_closure->png_data != NULL) { @@ -714,7 +878,8 @@ read_png (struct png_read_closure_t *png_closure) /** * cairo_image_surface_create_from_png: - * @filename: name of PNG file to load + * @filename: name of PNG file to load. On Windows this filename + * is encoded in UTF-8. * * Creates a new image surface and initializes the contents to the * given PNG file. @@ -727,20 +892,27 @@ read_png (struct png_read_closure_t *png_closure) * %CAIRO_STATUS_NO_MEMORY * %CAIRO_STATUS_FILE_NOT_FOUND * %CAIRO_STATUS_READ_ERROR + * %CAIRO_STATUS_PNG_ERROR * * Alternatively, you can allow errors to propagate through the drawing * operations and check the status on the context upon completion * using cairo_status(). + * + * Since: 1.0 **/ cairo_surface_t * cairo_image_surface_create_from_png (const char *filename) { struct png_read_closure_t png_closure; cairo_surface_t *surface; + cairo_status_t status; + + status = _cairo_fopen (filename, "rb", (FILE **) &png_closure.closure); + + if (status != CAIRO_STATUS_SUCCESS) + return _cairo_surface_create_in_error (status); - png_closure.closure = fopen (filename, "rb"); if (png_closure.closure == NULL) { - cairo_status_t status; switch (errno) { case ENOMEM: status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -780,10 +952,13 @@ cairo_image_surface_create_from_png (const char *filename) * * %CAIRO_STATUS_NO_MEMORY * %CAIRO_STATUS_READ_ERROR + * %CAIRO_STATUS_PNG_ERROR * * Alternatively, you can allow errors to propagate through the drawing * operations and check the status on the context upon completion * using cairo_status(). + * + * Since: 1.0 **/ cairo_surface_t * cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, diff --git a/gfx/cairo/cairo/src/cairo-polygon-intersect.c b/gfx/cairo/cairo/src/cairo-polygon-intersect.c new file mode 100644 index 000000000000..001e55ee06a2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-polygon-intersect.c @@ -0,0 +1,1472 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 Carl Worth + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-combsort-inline.h" + + +typedef struct _cairo_bo_intersect_ordinate { + int32_t ordinate; + enum { EXCESS = -1, EXACT = 0, DEFAULT = 1 } approx; +} cairo_bo_intersect_ordinate_t; + +typedef struct _cairo_bo_intersect_point { + cairo_bo_intersect_ordinate_t x; + cairo_bo_intersect_ordinate_t y; +} cairo_bo_intersect_point_t; + +typedef struct _cairo_bo_edge cairo_bo_edge_t; + +typedef struct _cairo_bo_deferred { + cairo_bo_edge_t *other; + int32_t top; +} cairo_bo_deferred_t; + +struct _cairo_bo_edge { + int a_or_b; + cairo_edge_t edge; + cairo_bo_edge_t *prev; + cairo_bo_edge_t *next; + cairo_bo_deferred_t deferred; +}; + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef enum { + CAIRO_BO_EVENT_TYPE_STOP = -1, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + CAIRO_BO_EVENT_TYPE_START +} cairo_bo_event_type_t; + +typedef struct _cairo_bo_event { + cairo_bo_event_type_t type; + cairo_bo_intersect_point_t point; +} cairo_bo_event_t; + +typedef struct _cairo_bo_start_event { + cairo_bo_event_type_t type; + cairo_bo_intersect_point_t point; + cairo_bo_edge_t edge; +} cairo_bo_start_event_t; + +typedef struct _cairo_bo_queue_event { + cairo_bo_event_type_t type; + cairo_bo_intersect_point_t point; + cairo_bo_edge_t *e1; + cairo_bo_edge_t *e2; +} cairo_bo_queue_event_t; + +typedef struct _pqueue { + int size, max_size; + + cairo_bo_event_t **elements; + cairo_bo_event_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct _cairo_bo_event_queue { + cairo_freepool_t pool; + pqueue_t pqueue; + cairo_bo_event_t **start_events; +} cairo_bo_event_queue_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_edge_t *head; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == line->p1.y) + return line->p1.x; + if (y == line->p2.y) + return line->p2.x; + + x = line->p1.x; + dy = line->p2.y - line->p1.y; + if (dy != 0) { + x += _cairo_fixed_mul_div_floor (y - line->p1.y, + line->p2.x - line->p1.x, + dy); + } + + return x; +} + +static inline int +_cairo_bo_point32_compare (cairo_bo_intersect_point_t const *a, + cairo_bo_intersect_point_t const *b) +{ + int cmp; + + cmp = a->y.ordinate - b->y.ordinate; + if (cmp) + return cmp; + + cmp = a->y.approx - b->y.approx; + if (cmp) + return cmp; + + return a->x.ordinate - b->x.ordinate; +} + +/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the + * slope a is respectively greater than, equal to, or less than the + * slope of b. + * + * For each edge, consider the direction vector formed from: + * + * top -> bottom + * + * which is: + * + * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) + * + * We then define the slope of each edge as dx/dy, (which is the + * inverse of the slope typically used in math instruction). We never + * compute a slope directly as the value approaches infinity, but we + * can derive a slope comparison without division as follows, (where + * the ? represents our compare operator). + * + * 1. slope(a) ? slope(b) + * 2. adx/ady ? bdx/bdy + * 3. (adx * bdy) ? (bdx * ady) + * + * Note that from step 2 to step 3 there is no change needed in the + * sign of the result since both ady and bdy are guaranteed to be + * greater than or equal to 0. + * + * When using this slope comparison to sort edges, some care is needed + * when interpreting the results. Since the slope compare operates on + * distance vectors from top to bottom it gives a correct left to + * right sort for edges that have a common top point, (such as two + * edges with start events at the same location). On the other hand, + * the sense of the result will be exactly reversed for two edges that + * have a common stop point. + */ +static inline int +_slope_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; + int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; + + /* Since the dy's are all positive by construction we can fast + * path several common cases. + */ + + /* First check for vertical lines. */ + if (adx == 0) + return -bdx; + if (bdx == 0) + return adx; + + /* Then where the two edges point in different directions wrt x. */ + if ((adx ^ bdx) < 0) + return adx; + + /* Finally we actually need to do the general comparison. */ + { + int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; + int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; + cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } +} + +/* + * We need to compare the x-coordinates of a pair of lines for a particular y, + * without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy + * - (Y - A_y) * A_dx * B_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 128 bit arithmetic. For certain, but common, + * input we can reduce this down to a single 32 bit compare by inspecting the + * deltas. + * + * (And put the burden of the work on developing fast 128 bit ops, which are + * required throughout the tessellator.) + * + * See the similar discussion for _slope_compare(). + */ +static int +edges_compare_x_for_y_general (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b, + int32_t y) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t dx; + int32_t adx, ady; + int32_t bdx, bdy; + enum { + HAVE_NONE = 0x0, + HAVE_DX = 0x1, + HAVE_ADX = 0x2, + HAVE_DX_ADX = HAVE_DX | HAVE_ADX, + HAVE_BDX = 0x4, + HAVE_DX_BDX = HAVE_DX | HAVE_BDX, + HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, + HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX + } have_dx_adx_bdx = HAVE_ALL; + + /* don't bother solving for abscissa if the edges' bounding boxes + * can be used to order them. */ + { + int32_t amin, amax; + int32_t bmin, bmax; + if (a->edge.line.p1.x < a->edge.line.p2.x) { + amin = a->edge.line.p1.x; + amax = a->edge.line.p2.x; + } else { + amin = a->edge.line.p2.x; + amax = a->edge.line.p1.x; + } + if (b->edge.line.p1.x < b->edge.line.p2.x) { + bmin = b->edge.line.p1.x; + bmax = b->edge.line.p2.x; + } else { + bmin = b->edge.line.p2.x; + bmax = b->edge.line.p1.x; + } + if (amax < bmin) return -1; + if (amin > bmax) return +1; + } + + ady = a->edge.line.p2.y - a->edge.line.p1.y; + adx = a->edge.line.p2.x - a->edge.line.p1.x; + if (adx == 0) + have_dx_adx_bdx &= ~HAVE_ADX; + + bdy = b->edge.line.p2.y - b->edge.line.p1.y; + bdx = b->edge.line.p2.x - b->edge.line.p1.x; + if (bdx == 0) + have_dx_adx_bdx &= ~HAVE_BDX; + + dx = a->edge.line.p1.x - b->edge.line.p1.x; + if (dx == 0) + have_dx_adx_bdx &= ~HAVE_DX; + +#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) +#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) + switch (have_dx_adx_bdx) { + default: + case HAVE_NONE: + return 0; + case HAVE_DX: + /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ + return dx; /* ady * bdy is positive definite */ + case HAVE_ADX: + /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ + return adx; /* bdy * (y - a->top.y) is positive definite */ + case HAVE_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy */ + return -bdx; /* ady * (y - b->top.y) is positive definite */ + case HAVE_ADX_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ + if ((adx ^ bdx) < 0) { + return adx; + } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ + cairo_int64_t adx_bdy, bdx_ady; + + /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ + + adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } else + return _cairo_int128_cmp (A, B); + case HAVE_DX_ADX: + /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ + if ((-adx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t ady_dx, dy_adx; + + ady_dx = _cairo_int32x32_64_mul (ady, dx); + dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); + + return _cairo_int64_cmp (ady_dx, dy_adx); + } + case HAVE_DX_BDX: + /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ + if ((bdx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t bdy_dx, dy_bdx; + + bdy_dx = _cairo_int32x32_64_mul (bdy, dx); + dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); + + return _cairo_int64_cmp (bdy_dx, dy_bdx); + } + case HAVE_ALL: + /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ + return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); + } +#undef B +#undef A +#undef L +} + +/* + * We need to compare the x-coordinate of a line for a particular y wrt to a + * given x, without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ X + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy (and (Y - A_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 64 bit arithmetic. + * + * See the similar discussion for _slope_compare() and + * edges_compare_x_for_y_general(). + */ +static int +edge_compare_for_y_against_x (const cairo_bo_edge_t *a, + int32_t y, + int32_t x) +{ + int32_t adx, ady; + int32_t dx, dy; + cairo_int64_t L, R; + + if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) + return 1; + if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) + return -1; + + adx = a->edge.line.p2.x - a->edge.line.p1.x; + dx = x - a->edge.line.p1.x; + + if (adx == 0) + return -dx; + if (dx == 0 || (adx ^ dx) < 0) + return adx; + + dy = y - a->edge.line.p1.y; + ady = a->edge.line.p2.y - a->edge.line.p1.y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +static int +edges_compare_x_for_y (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b, + int32_t y) +{ + /* If the sweep-line is currently on an end-point of a line, + * then we know its precise x value (and considering that we often need to + * compare events at end-points, this happens frequently enough to warrant + * special casing). + */ + enum { + HAVE_NEITHER = 0x0, + HAVE_AX = 0x1, + HAVE_BX = 0x2, + HAVE_BOTH = HAVE_AX | HAVE_BX + } have_ax_bx = HAVE_BOTH; + int32_t ax = 0, bx = 0; + + if (y == a->edge.line.p1.y) + ax = a->edge.line.p1.x; + else if (y == a->edge.line.p2.y) + ax = a->edge.line.p2.x; + else + have_ax_bx &= ~HAVE_AX; + + if (y == b->edge.line.p1.y) + bx = b->edge.line.p1.x; + else if (y == b->edge.line.p2.y) + bx = b->edge.line.p2.x; + else + have_ax_bx &= ~HAVE_BX; + + switch (have_ax_bx) { + default: + case HAVE_NEITHER: + return edges_compare_x_for_y_general (a, b, y); + case HAVE_AX: + return -edge_compare_for_y_against_x (b, y, ax); + case HAVE_BX: + return edge_compare_for_y_against_x (a, y, bx); + case HAVE_BOTH: + return ax - bx; + } +} + +static inline int +_line_equal (const cairo_line_t *a, const cairo_line_t *b) +{ + return a->p1.x == b->p1.x && a->p1.y == b->p1.y && + a->p2.x == b->p2.x && a->p2.y == b->p2.y; +} + +static int +_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, + const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + int cmp; + + /* compare the edges if not identical */ + if (! _line_equal (&a->edge.line, &b->edge.line)) { + cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); + if (cmp) + return cmp; + + /* The two edges intersect exactly at y, so fall back on slope + * comparison. We know that this compare_edges function will be + * called only when starting a new edge, (not when stopping an + * edge), so we don't have to worry about conditionally inverting + * the sense of _slope_compare. */ + cmp = _slope_compare (a, b); + if (cmp) + return cmp; + } + + /* We've got two collinear edges now. */ + return b->edge.bottom - a->edge.bottom; +} + +static inline cairo_int64_t +det32_64 (int32_t a, int32_t b, + int32_t c, int32_t d) +{ + /* det = a * d - b * c */ + return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), + _cairo_int32x32_64_mul (b, c)); +} + +static inline cairo_int128_t +det64x32_128 (cairo_int64_t a, int32_t b, + cairo_int64_t c, int32_t d) +{ + /* det = a * d - b * c */ + return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), + _cairo_int64x32_128_mul (c, b)); +} + +static inline cairo_bo_intersect_ordinate_t +round_to_nearest (cairo_quorem64_t d, + cairo_int64_t den) +{ + cairo_bo_intersect_ordinate_t ordinate; + int32_t quo = d.quo; + cairo_int64_t drem_2 = _cairo_int64_mul (d.rem, _cairo_int32_to_int64 (2)); + + /* assert (! _cairo_int64_negative (den));*/ + + if (_cairo_int64_lt (drem_2, _cairo_int64_negate (den))) { + quo -= 1; + drem_2 = _cairo_int64_negate (drem_2); + } else if (_cairo_int64_le (den, drem_2)) { + quo += 1; + drem_2 = _cairo_int64_negate (drem_2); + } + + ordinate.ordinate = quo; + ordinate.approx = _cairo_int64_is_zero (drem_2) ? EXACT : _cairo_int64_negative (drem_2) ? EXCESS : DEFAULT; + + return ordinate; +} + +/* Compute the intersection of two lines as defined by two edges. The + * result is provided as a coordinate pair of 128-bit integers. + * + * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or + * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. + */ +static cairo_bool_t +intersect_lines (cairo_bo_edge_t *a, + cairo_bo_edge_t *b, + cairo_bo_intersect_point_t *intersection) +{ + cairo_int64_t a_det, b_det; + + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm begins. + * What we're doing to mitigate this is to perform clamping in + * cairo_bo_tessellate_polygon(). + */ + int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; + int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; + + int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; + int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; + + cairo_int64_t den_det; + cairo_int64_t R; + cairo_quorem64_t qr; + + den_det = det32_64 (dx1, dy1, dx2, dy2); + + /* Q: Can we determine that the lines do not intersect (within range) + * much more cheaply than computing the intersection point i.e. by + * avoiding the division? + * + * X = ax + t * adx = bx + s * bdx; + * Y = ay + t * ady = by + s * bdy; + * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) + * => t * L = R + * + * Therefore we can reject any intersection (under the criteria for + * valid intersection events) if: + * L^R < 0 => t < 0, or + * L t > 1 + * + * (where top/bottom must at least extend to the line endpoints). + * + * A similar substitution can be performed for s, yielding: + * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) + */ + R = det32_64 (dx2, dy2, + b->edge.line.p1.x - a->edge.line.p1.x, + b->edge.line.p1.y - a->edge.line.p1.y); + if (_cairo_int64_le (den_det, R)) + return FALSE; + + R = det32_64 (dy1, dx1, + a->edge.line.p1.y - b->edge.line.p1.y, + a->edge.line.p1.x - b->edge.line.p1.x); + if (_cairo_int64_le (den_det, R)) + return FALSE; + + /* We now know that the two lines should intersect within range. */ + + a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, + a->edge.line.p2.x, a->edge.line.p2.y); + b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, + b->edge.line.p2.x, b->edge.line.p2.y); + + /* x = det (a_det, dx1, b_det, dx2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, + b_det, dx2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; + + intersection->x = round_to_nearest (qr, den_det); + + /* y = det (a_det, dy1, b_det, dy2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, + b_det, dy2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; + + intersection->y = round_to_nearest (qr, den_det); + + return TRUE; +} + +static int +_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, + int32_t b) +{ + /* First compare the quotient */ + if (a.ordinate > b) + return +1; + if (a.ordinate < b) + return -1; + + return a.approx; /* == EXCESS ? -1 : a.approx == EXACT ? 0 : 1;*/ +} + +/* Does the given edge contain the given point. The point must already + * be known to be contained within the line determined by the edge, + * (most likely the point results from an intersection of this edge + * with another). + * + * If we had exact arithmetic, then this function would simply be a + * matter of examining whether the y value of the point lies within + * the range of y values of the edge. But since intersection points + * are not exact due to being rounded to the nearest integer within + * the available precision, we must also examine the x value of the + * point. + * + * The definition of "contains" here is that the given intersection + * point will be seen by the sweep line after the start event for the + * given edge and before the stop event for the edge. See the comments + * in the implementation for more details. + */ +static cairo_bool_t +_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, + cairo_bo_intersect_point_t *point) +{ + return _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.bottom) < 0; +} + +/* Compute the intersection of two edges. The result is provided as a + * coordinate pair of 128-bit integers. + * + * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection + * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the + * intersection of the lines defined by the edges occurs outside of + * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges + * are exactly parallel. + * + * Note that when determining if a candidate intersection is "inside" + * an edge, we consider both the infinitesimal shortening and the + * infinitesimal tilt rules described by John Hobby. Specifically, if + * the intersection is exactly the same as an edge point, it is + * effectively outside (no intersection is returned). Also, if the + * intersection point has the same + */ +static cairo_bool_t +_cairo_bo_edge_intersect (cairo_bo_edge_t *a, + cairo_bo_edge_t *b, + cairo_bo_intersect_point_t *intersection) +{ + if (! intersect_lines (a, b, intersection)) + return FALSE; + + if (! _cairo_bo_edge_contains_intersect_point (a, intersection)) + return FALSE; + + if (! _cairo_bo_edge_contains_intersect_point (b, intersection)) + return FALSE; + + return TRUE; +} + +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) +{ + int cmp; + + cmp = _cairo_bo_point32_compare (&a->point, &b->point); + if (cmp) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a < b ? -1 : a == b ? 0 : 1; +} + +static inline void +_pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; +} + +static inline void +_pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_status_t +_pqueue_grow (pqueue_t *pq) +{ + cairo_bo_event_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pq->elements = new_elements; + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) +{ + cairo_bo_event_t **elements; + int i, parent; + + if (unlikely (pq->size + 1 == pq->max_size)) { + cairo_status_t status; + + status = _pqueue_grow (pq); + if (unlikely (status)) + return status; + } + + elements = pq->elements; + + for (i = ++pq->size; + i != PQ_FIRST_ENTRY && + cairo_bo_event_compare (event, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = event; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_pop (pqueue_t *pq) +{ + cairo_bo_event_t **elements = pq->elements; + cairo_bo_event_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + cairo_bo_event_compare (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (cairo_bo_event_compare (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline cairo_status_t +_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, + cairo_bo_event_type_t type, + cairo_bo_edge_t *e1, + cairo_bo_edge_t *e2, + const cairo_bo_intersect_point_t *point) +{ + cairo_bo_queue_event_t *event; + + event = _cairo_freepool_alloc (&queue->pool); + if (unlikely (event == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event->type = type; + event->e1 = e1; + event->e2 = e2; + event->point = *point; + + return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); +} + +static void +_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, + cairo_bo_event_t *event) +{ + _cairo_freepool_free (&queue->pool, event); +} + +static cairo_bo_event_t * +_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) +{ + cairo_bo_event_t *event, *cmp; + + event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; + cmp = *event_queue->start_events; + if (event == NULL || + (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) + { + event = cmp; + event_queue->start_events++; + } + else + { + _pqueue_pop (&event_queue->pqueue); + } + + return event; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare) + +static void +_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, + cairo_bo_event_t **start_events, + int num_events) +{ + _cairo_bo_event_queue_sort (start_events, num_events); + start_events[num_events] = NULL; + + event_queue->start_events = start_events; + + _cairo_freepool_init (&event_queue->pool, + sizeof (cairo_bo_queue_event_t)); + _pqueue_init (&event_queue->pqueue); + event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; +} + +static cairo_status_t +event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *edge) +{ + cairo_bo_intersect_point_t point; + + point.y.ordinate = edge->edge.bottom; + point.y.approx = EXACT; + point.x.ordinate = _line_compute_intersection_x_for_y (&edge->edge.line, + point.y.ordinate); + point.x.approx = EXACT; + + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_STOP, + edge, NULL, + &point); +} + +static void +_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) +{ + _pqueue_fini (&event_queue->pqueue); + _cairo_freepool_fini (&event_queue->pool); +} + +static inline cairo_status_t +event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *left, + cairo_bo_edge_t *right) +{ + cairo_bo_intersect_point_t intersection; + + if (_line_equal (&left->edge.line, &right->edge.line)) + return CAIRO_STATUS_SUCCESS; + + /* The names "left" and "right" here are correct descriptions of + * the order of the two edges within the active edge list. So if a + * slope comparison also puts left less than right, then we know + * that the intersection of these two segments has already + * occurred before the current sweep line position. */ + if (_slope_compare (left, right) <= 0) + return CAIRO_STATUS_SUCCESS; + + if (! _cairo_bo_edge_intersect (left, right, &intersection)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + left, right, + &intersection); +} + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) +{ + sweep_line->head = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static cairo_status_t +sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (sweep_line->current_edge != NULL) { + cairo_bo_edge_t *prev, *next; + int cmp; + + cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, + sweep_line->current_edge, + edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + next, edge) < 0) + { + prev = next, next = prev->next; + } + + prev->next = edge; + edge->prev = prev; + edge->next = next; + if (next != NULL) + next->prev = edge; + } else if (cmp > 0) { + next = sweep_line->current_edge; + prev = next->prev; + while (prev != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + prev, edge) > 0) + { + next = prev, prev = next->prev; + } + + next->prev = edge; + edge->next = next; + edge->prev = prev; + if (prev != NULL) + prev->next = edge; + else + sweep_line->head = edge; + } else { + prev = sweep_line->current_edge; + edge->prev = prev; + edge->next = prev->next; + if (prev->next != NULL) + prev->next->prev = edge; + prev->next = edge; + } + } else { + sweep_line->head = edge; + } + + sweep_line->current_edge = edge; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (edge->prev != NULL) + edge->prev->next = edge->next; + else + sweep_line->head = edge->next; + + if (edge->next != NULL) + edge->next->prev = edge->prev; + + if (sweep_line->current_edge == edge) + sweep_line->current_edge = edge->prev ? edge->prev : edge->next; +} + +static void +_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *left, + cairo_bo_edge_t *right) +{ + if (left->prev != NULL) + left->prev->next = right; + else + sweep_line->head = right; + + if (right->next != NULL) + right->next->prev = left; + + left->next = right->next; + right->next = left; + + right->prev = left->prev; + left->prev = right; +} + +static inline cairo_bool_t +edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + if (_line_equal (&a->edge.line, &b->edge.line)) + return TRUE; + + if (_slope_compare (a, b)) + return FALSE; + + /* The choice of y is not truly arbitrary since we must guarantee that it + * is greater than the start of either line. + */ + if (a->edge.line.p1.y == b->edge.line.p1.y) { + return a->edge.line.p1.x == b->edge.line.p1.x; + } else if (a->edge.line.p1.y < b->edge.line.p1.y) { + return edge_compare_for_y_against_x (b, + a->edge.line.p1.y, + a->edge.line.p1.x) == 0; + } else { + return edge_compare_for_y_against_x (a, + b->edge.line.p1.y, + b->edge.line.p1.x) == 0; + } +} + +static void +edges_end (cairo_bo_edge_t *left, + int32_t bot, + cairo_polygon_t *polygon) +{ + cairo_bo_deferred_t *l = &left->deferred; + cairo_bo_edge_t *right = l->other; + + assert(right->deferred.other == NULL); + if (likely (l->top < bot)) { + _cairo_polygon_add_line (polygon, &left->edge.line, l->top, bot, 1); + _cairo_polygon_add_line (polygon, &right->edge.line, l->top, bot, -1); + } + + l->other = NULL; +} + +static inline void +edges_start_or_continue (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_polygon_t *polygon) +{ + assert (right != NULL); + assert (right->deferred.other == NULL); + + if (left->deferred.other == right) + return; + + if (left->deferred.other != NULL) { + if (edges_colinear (left->deferred.other, right)) { + cairo_bo_edge_t *old = left->deferred.other; + + /* continuation on right, extend right to cover both */ + assert (old->deferred.other == NULL); + assert (old->edge.line.p2.y > old->edge.line.p1.y); + + if (old->edge.line.p1.y < right->edge.line.p1.y) + right->edge.line.p1 = old->edge.line.p1; + if (old->edge.line.p2.y > right->edge.line.p2.y) + right->edge.line.p2 = old->edge.line.p2; + left->deferred.other = right; + return; + } + + edges_end (left, top, polygon); + } + + if (! edges_colinear (left, right)) { + left->deferred.top = top; + left->deferred.other = right; + } +} + +#define is_zero(w) ((w)[0] == 0 || (w)[1] == 0) + +static inline void +active_edges (cairo_bo_edge_t *left, + int32_t top, + cairo_polygon_t *polygon) +{ + cairo_bo_edge_t *right; + int winding[2] = {0, 0}; + + /* Yes, this is naive. Consider this a placeholder. */ + + while (left != NULL) { + assert (is_zero (winding)); + + do { + winding[left->a_or_b] += left->edge.dir; + if (! is_zero (winding)) + break; + + if unlikely ((left->deferred.other)) + edges_end (left, top, polygon); + + left = left->next; + if (! left) + return; + } while (1); + + right = left->next; + do { + if unlikely ((right->deferred.other)) + edges_end (right, top, polygon); + + winding[right->a_or_b] += right->edge.dir; + if (is_zero (winding)) { + if (right->next == NULL || + ! edges_colinear (right, right->next)) + break; + } + + right = right->next; + } while (1); + + edges_start_or_continue (left, right, top, polygon); + + left = right->next; + } +} + +static cairo_status_t +intersection_sweep (cairo_bo_event_t **start_events, + int num_events, + cairo_polygon_t *polygon) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ + cairo_bo_event_queue_t event_queue; + cairo_bo_sweep_line_t sweep_line; + cairo_bo_event_t *event; + cairo_bo_edge_t *left, *right; + cairo_bo_edge_t *e1, *e2; + + _cairo_bo_event_queue_init (&event_queue, start_events, num_events); + _cairo_bo_sweep_line_init (&sweep_line); + + while ((event = _cairo_bo_event_dequeue (&event_queue))) { + if (event->point.y.ordinate != sweep_line.current_y) { + active_edges (sweep_line.head, + sweep_line.current_y, + polygon); + sweep_line.current_y = event->point.y.ordinate; + } + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + e1 = &((cairo_bo_start_event_t *) event)->edge; + + status = sweep_line_insert (&sweep_line, e1); + if (unlikely (status)) + goto unwind; + + status = event_queue_insert_stop (&event_queue, e1); + if (unlikely (status)) + goto unwind; + + left = e1->prev; + right = e1->next; + + if (left != NULL) { + status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + + break; + + case CAIRO_BO_EVENT_TYPE_STOP: + e1 = ((cairo_bo_queue_event_t *) event)->e1; + _cairo_bo_event_queue_delete (&event_queue, event); + + if (e1->deferred.other) + edges_end (e1, sweep_line.current_y, polygon); + + left = e1->prev; + right = e1->next; + + _cairo_bo_sweep_line_delete (&sweep_line, e1); + + if (left != NULL && right != NULL) { + status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); + if (unlikely (status)) + goto unwind; + } + + break; + + case CAIRO_BO_EVENT_TYPE_INTERSECTION: + e1 = ((cairo_bo_queue_event_t *) event)->e1; + e2 = ((cairo_bo_queue_event_t *) event)->e2; + _cairo_bo_event_queue_delete (&event_queue, event); + + /* skip this intersection if its edges are not adjacent */ + if (e2 != e1->next) + break; + + if (e1->deferred.other) + edges_end (e1, sweep_line.current_y, polygon); + if (e2->deferred.other) + edges_end (e2, sweep_line.current_y, polygon); + + left = e1->prev; + right = e2->next; + + _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); + + /* after the swap e2 is left of e1 */ + + if (left != NULL) { + status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + + break; + } + } + + unwind: + _cairo_bo_event_queue_fini (&event_queue); + + return status; +} + +cairo_status_t +_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a, + cairo_polygon_t *b, int winding_b) +{ + cairo_status_t status; + cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; + cairo_bo_start_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + int num_events; + int i, j; + + /* XXX lazy */ + if (winding_a != CAIRO_FILL_RULE_WINDING) { + status = _cairo_polygon_reduce (a, winding_a); + if (unlikely (status)) + return status; + } + + if (winding_b != CAIRO_FILL_RULE_WINDING) { + status = _cairo_polygon_reduce (b, winding_b); + if (unlikely (status)) + return status; + } + + if (unlikely (0 == a->num_edges)) + return CAIRO_STATUS_SUCCESS; + + if (unlikely (0 == b->num_edges)) { + a->num_edges = 0; + return CAIRO_STATUS_SUCCESS; + } + + events = stack_events; + event_ptrs = stack_event_ptrs; + num_events = a->num_edges + b->num_edges; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_start_event_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); + } + + j = 0; + for (i = 0; i < a->num_edges; i++) { + event_ptrs[j] = (cairo_bo_event_t *) &events[j]; + + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y.ordinate = a->edges[i].top; + events[j].point.y.approx = EXACT; + events[j].point.x.ordinate = + _line_compute_intersection_x_for_y (&a->edges[i].line, + events[j].point.y.ordinate); + events[j].point.x.approx = EXACT; + + events[j].edge.a_or_b = 0; + events[j].edge.edge = a->edges[i]; + events[j].edge.deferred.other = NULL; + events[j].edge.prev = NULL; + events[j].edge.next = NULL; + j++; + } + + for (i = 0; i < b->num_edges; i++) { + event_ptrs[j] = (cairo_bo_event_t *) &events[j]; + + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y.ordinate = b->edges[i].top; + events[j].point.y.approx = EXACT; + events[j].point.x.ordinate = + _line_compute_intersection_x_for_y (&b->edges[i].line, + events[j].point.y.ordinate); + events[j].point.x.approx = EXACT; + + events[j].edge.a_or_b = 1; + events[j].edge.edge = b->edges[i]; + events[j].edge.deferred.other = NULL; + events[j].edge.prev = NULL; + events[j].edge.next = NULL; + j++; + } + assert (j == num_events); + +#if 0 + { + FILE *file = fopen ("clip_a.txt", "w"); + _cairo_debug_print_polygon (file, a); + fclose (file); + } + { + FILE *file = fopen ("clip_b.txt", "w"); + _cairo_debug_print_polygon (file, b); + fclose (file); + } +#endif + + a->num_edges = 0; + status = intersection_sweep (event_ptrs, num_events, a); + if (events != stack_events) + free (events); + +#if 0 + { + FILE *file = fopen ("clip_result.txt", "w"); + _cairo_debug_print_polygon (file, a); + fclose (file); + } +#endif + + return status; +} + +cairo_status_t +_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon, + cairo_fill_rule_t *winding, + cairo_box_t *boxes, + int num_boxes) +{ + cairo_polygon_t b; + cairo_status_t status; + int n; + + if (num_boxes == 0) { + polygon->num_edges = 0; + return CAIRO_STATUS_SUCCESS; + } + + for (n = 0; n < num_boxes; n++) { + if (polygon->extents.p1.x >= boxes[n].p1.x && + polygon->extents.p2.x <= boxes[n].p2.x && + polygon->extents.p1.y >= boxes[n].p1.y && + polygon->extents.p2.y <= boxes[n].p2.y) + { + return CAIRO_STATUS_SUCCESS; + } + } + + _cairo_polygon_init (&b, NULL, 0); + for (n = 0; n < num_boxes; n++) { + if (boxes[n].p2.x > polygon->extents.p1.x && + boxes[n].p1.x < polygon->extents.p2.x && + boxes[n].p2.y > polygon->extents.p1.y && + boxes[n].p1.y < polygon->extents.p2.y) + { + cairo_point_t p1, p2; + + p1.y = boxes[n].p1.y; + p2.y = boxes[n].p2.y; + + p2.x = p1.x = boxes[n].p1.x; + _cairo_polygon_add_external_edge (&b, &p1, &p2); + + p2.x = p1.x = boxes[n].p2.x; + _cairo_polygon_add_external_edge (&b, &p2, &p1); + } + } + + status = _cairo_polygon_intersect (polygon, *winding, + &b, CAIRO_FILL_RULE_WINDING); + _cairo_polygon_fini (&b); + + *winding = CAIRO_FILL_RULE_WINDING; + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-polygon-reduce.c b/gfx/cairo/cairo/src/cairo-polygon-reduce.c new file mode 100644 index 000000000000..da6c9ab7c78d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-polygon-reduce.c @@ -0,0 +1,1438 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 Carl Worth + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-combsort-inline.h" + +#define DEBUG_POLYGON 0 + +typedef cairo_point_t cairo_bo_point32_t; + +typedef struct _cairo_bo_intersect_ordinate { + int32_t ordinate; + enum { EXACT, INEXACT } exactness; +} cairo_bo_intersect_ordinate_t; + +typedef struct _cairo_bo_intersect_point { + cairo_bo_intersect_ordinate_t x; + cairo_bo_intersect_ordinate_t y; +} cairo_bo_intersect_point_t; + +typedef struct _cairo_bo_edge cairo_bo_edge_t; + +typedef struct _cairo_bo_deferred { + cairo_bo_edge_t *right; + int32_t top; +} cairo_bo_deferred_t; + +struct _cairo_bo_edge { + cairo_edge_t edge; + cairo_bo_edge_t *prev; + cairo_bo_edge_t *next; + cairo_bo_deferred_t deferred; +}; + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +typedef enum { + CAIRO_BO_EVENT_TYPE_STOP, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + CAIRO_BO_EVENT_TYPE_START +} cairo_bo_event_type_t; + +typedef struct _cairo_bo_event { + cairo_bo_event_type_t type; + cairo_point_t point; +} cairo_bo_event_t; + +typedef struct _cairo_bo_start_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t edge; +} cairo_bo_start_event_t; + +typedef struct _cairo_bo_queue_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t *e1; + cairo_bo_edge_t *e2; +} cairo_bo_queue_event_t; + +typedef struct _pqueue { + int size, max_size; + + cairo_bo_event_t **elements; + cairo_bo_event_t *elements_embedded[1024]; +} pqueue_t; + +typedef struct _cairo_bo_event_queue { + cairo_freepool_t pool; + pqueue_t pqueue; + cairo_bo_event_t **start_events; +} cairo_bo_event_queue_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_edge_t *head; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == line->p1.y) + return line->p1.x; + if (y == line->p2.y) + return line->p2.x; + + x = line->p1.x; + dy = line->p2.y - line->p1.y; + if (dy != 0) { + x += _cairo_fixed_mul_div_floor (y - line->p1.y, + line->p2.x - line->p1.x, + dy); + } + + return x; +} + +static inline int +_cairo_bo_point32_compare (cairo_bo_point32_t const *a, + cairo_bo_point32_t const *b) +{ + int cmp; + + cmp = a->y - b->y; + if (cmp) + return cmp; + + return a->x - b->x; +} + +/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the + * slope a is respectively greater than, equal to, or less than the + * slope of b. + * + * For each edge, consider the direction vector formed from: + * + * top -> bottom + * + * which is: + * + * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) + * + * We then define the slope of each edge as dx/dy, (which is the + * inverse of the slope typically used in math instruction). We never + * compute a slope directly as the value approaches infinity, but we + * can derive a slope comparison without division as follows, (where + * the ? represents our compare operator). + * + * 1. slope(a) ? slope(b) + * 2. adx/ady ? bdx/bdy + * 3. (adx * bdy) ? (bdx * ady) + * + * Note that from step 2 to step 3 there is no change needed in the + * sign of the result since both ady and bdy are guaranteed to be + * greater than or equal to 0. + * + * When using this slope comparison to sort edges, some care is needed + * when interpreting the results. Since the slope compare operates on + * distance vectors from top to bottom it gives a correct left to + * right sort for edges that have a common top point, (such as two + * edges with start events at the same location). On the other hand, + * the sense of the result will be exactly reversed for two edges that + * have a common stop point. + */ +static inline int +_slope_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x; + int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x; + + /* Since the dy's are all positive by construction we can fast + * path several common cases. + */ + + /* First check for vertical lines. */ + if (adx == 0) + return -bdx; + if (bdx == 0) + return adx; + + /* Then where the two edges point in different directions wrt x. */ + if ((adx ^ bdx) < 0) + return adx; + + /* Finally we actually need to do the general comparison. */ + { + int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y; + int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y; + cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } +} + +/* + * We need to compare the x-coordinates of a pair of lines for a particular y, + * without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy + * - (Y - A_y) * A_dx * B_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 128 bit arithmetic. For certain, but common, + * input we can reduce this down to a single 32 bit compare by inspecting the + * deltas. + * + * (And put the burden of the work on developing fast 128 bit ops, which are + * required throughout the tessellator.) + * + * See the similar discussion for _slope_compare(). + */ +static int +edges_compare_x_for_y_general (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b, + int32_t y) +{ + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm + * begins. + */ + int32_t dx; + int32_t adx, ady; + int32_t bdx, bdy; + enum { + HAVE_NONE = 0x0, + HAVE_DX = 0x1, + HAVE_ADX = 0x2, + HAVE_DX_ADX = HAVE_DX | HAVE_ADX, + HAVE_BDX = 0x4, + HAVE_DX_BDX = HAVE_DX | HAVE_BDX, + HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX, + HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX + } have_dx_adx_bdx = HAVE_ALL; + + /* don't bother solving for abscissa if the edges' bounding boxes + * can be used to order them. */ + { + int32_t amin, amax; + int32_t bmin, bmax; + if (a->edge.line.p1.x < a->edge.line.p2.x) { + amin = a->edge.line.p1.x; + amax = a->edge.line.p2.x; + } else { + amin = a->edge.line.p2.x; + amax = a->edge.line.p1.x; + } + if (b->edge.line.p1.x < b->edge.line.p2.x) { + bmin = b->edge.line.p1.x; + bmax = b->edge.line.p2.x; + } else { + bmin = b->edge.line.p2.x; + bmax = b->edge.line.p1.x; + } + if (amax < bmin) return -1; + if (amin > bmax) return +1; + } + + ady = a->edge.line.p2.y - a->edge.line.p1.y; + adx = a->edge.line.p2.x - a->edge.line.p1.x; + if (adx == 0) + have_dx_adx_bdx &= ~HAVE_ADX; + + bdy = b->edge.line.p2.y - b->edge.line.p1.y; + bdx = b->edge.line.p2.x - b->edge.line.p1.x; + if (bdx == 0) + have_dx_adx_bdx &= ~HAVE_BDX; + + dx = a->edge.line.p1.x - b->edge.line.p1.x; + if (dx == 0) + have_dx_adx_bdx &= ~HAVE_DX; + +#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx) +#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y) + switch (have_dx_adx_bdx) { + default: + case HAVE_NONE: + return 0; + case HAVE_DX: + /* A_dy * B_dy * (A_x - B_x) ∘ 0 */ + return dx; /* ady * bdy is positive definite */ + case HAVE_ADX: + /* 0 ∘ - (Y - A_y) * A_dx * B_dy */ + return adx; /* bdy * (y - a->top.y) is positive definite */ + case HAVE_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy */ + return -bdx; /* ady * (y - b->top.y) is positive definite */ + case HAVE_ADX_BDX: + /* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */ + if ((adx ^ bdx) < 0) { + return adx; + } else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */ + cairo_int64_t adx_bdy, bdx_ady; + + /* ∴ A_dx * B_dy ∘ B_dx * A_dy */ + + adx_bdy = _cairo_int32x32_64_mul (adx, bdy); + bdx_ady = _cairo_int32x32_64_mul (bdx, ady); + + return _cairo_int64_cmp (adx_bdy, bdx_ady); + } else + return _cairo_int128_cmp (A, B); + case HAVE_DX_ADX: + /* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */ + if ((-adx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t ady_dx, dy_adx; + + ady_dx = _cairo_int32x32_64_mul (ady, dx); + dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx); + + return _cairo_int64_cmp (ady_dx, dy_adx); + } + case HAVE_DX_BDX: + /* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */ + if ((bdx ^ dx) < 0) { + return dx; + } else { + cairo_int64_t bdy_dx, dy_bdx; + + bdy_dx = _cairo_int32x32_64_mul (bdy, dx); + dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx); + + return _cairo_int64_cmp (bdy_dx, dy_bdx); + } + case HAVE_ALL: + /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ + return _cairo_int128_cmp (L, _cairo_int128_sub (B, A)); + } +#undef B +#undef A +#undef L +} + +/* + * We need to compare the x-coordinate of a line for a particular y wrt to a + * given x, without loss of precision. + * + * The x-coordinate along an edge for a given y is: + * X = A_x + (Y - A_y) * A_dx / A_dy + * + * So the inequality we wish to test is: + * A_x + (Y - A_y) * A_dx / A_dy ∘ X + * where ∘ is our inequality operator. + * + * By construction, we know that A_dy (and (Y - A_y)) are + * all positive, so we can rearrange it thus without causing a sign change: + * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy + * + * Given the assumption that all the deltas fit within 32 bits, we can compute + * this comparison directly using 64 bit arithmetic. + * + * See the similar discussion for _slope_compare() and + * edges_compare_x_for_y_general(). + */ +static int +edge_compare_for_y_against_x (const cairo_bo_edge_t *a, + int32_t y, + int32_t x) +{ + int32_t adx, ady; + int32_t dx, dy; + cairo_int64_t L, R; + + if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) + return 1; + if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) + return -1; + + adx = a->edge.line.p2.x - a->edge.line.p1.x; + dx = x - a->edge.line.p1.x; + + if (adx == 0) + return -dx; + if (dx == 0 || (adx ^ dx) < 0) + return adx; + + dy = y - a->edge.line.p1.y; + ady = a->edge.line.p2.y - a->edge.line.p1.y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +static int +edges_compare_x_for_y (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b, + int32_t y) +{ + /* If the sweep-line is currently on an end-point of a line, + * then we know its precise x value (and considering that we often need to + * compare events at end-points, this happens frequently enough to warrant + * special casing). + */ + enum { + HAVE_NEITHER = 0x0, + HAVE_AX = 0x1, + HAVE_BX = 0x2, + HAVE_BOTH = HAVE_AX | HAVE_BX + } have_ax_bx = HAVE_BOTH; + int32_t ax = 0, bx = 0; + + if (y == a->edge.line.p1.y) + ax = a->edge.line.p1.x; + else if (y == a->edge.line.p2.y) + ax = a->edge.line.p2.x; + else + have_ax_bx &= ~HAVE_AX; + + if (y == b->edge.line.p1.y) + bx = b->edge.line.p1.x; + else if (y == b->edge.line.p2.y) + bx = b->edge.line.p2.x; + else + have_ax_bx &= ~HAVE_BX; + + switch (have_ax_bx) { + default: + case HAVE_NEITHER: + return edges_compare_x_for_y_general (a, b, y); + case HAVE_AX: + return -edge_compare_for_y_against_x (b, y, ax); + case HAVE_BX: + return edge_compare_for_y_against_x (a, y, bx); + case HAVE_BOTH: + return ax - bx; + } +} + +static inline int +_line_equal (const cairo_line_t *a, const cairo_line_t *b) +{ + return (a->p1.x == b->p1.x && a->p1.y == b->p1.y && + a->p2.x == b->p2.x && a->p2.y == b->p2.y); +} + +static int +_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line, + const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + int cmp; + + /* compare the edges if not identical */ + if (! _line_equal (&a->edge.line, &b->edge.line)) { + cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); + if (cmp) + return cmp; + + /* The two edges intersect exactly at y, so fall back on slope + * comparison. We know that this compare_edges function will be + * called only when starting a new edge, (not when stopping an + * edge), so we don't have to worry about conditionally inverting + * the sense of _slope_compare. */ + cmp = _slope_compare (a, b); + if (cmp) + return cmp; + } + + /* We've got two collinear edges now. */ + return b->edge.bottom - a->edge.bottom; +} + +static inline cairo_int64_t +det32_64 (int32_t a, int32_t b, + int32_t c, int32_t d) +{ + /* det = a * d - b * c */ + return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), + _cairo_int32x32_64_mul (b, c)); +} + +static inline cairo_int128_t +det64x32_128 (cairo_int64_t a, int32_t b, + cairo_int64_t c, int32_t d) +{ + /* det = a * d - b * c */ + return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d), + _cairo_int64x32_128_mul (c, b)); +} + +/* Compute the intersection of two lines as defined by two edges. The + * result is provided as a coordinate pair of 128-bit integers. + * + * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or + * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel. + */ +static cairo_bool_t +intersect_lines (cairo_bo_edge_t *a, + cairo_bo_edge_t *b, + cairo_bo_intersect_point_t *intersection) +{ + cairo_int64_t a_det, b_det; + + /* XXX: We're assuming here that dx and dy will still fit in 32 + * bits. That's not true in general as there could be overflow. We + * should prevent that before the tessellation algorithm begins. + * What we're doing to mitigate this is to perform clamping in + * cairo_bo_tessellate_polygon(). + */ + int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x; + int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y; + + int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x; + int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y; + + cairo_int64_t den_det; + cairo_int64_t R; + cairo_quorem64_t qr; + + den_det = det32_64 (dx1, dy1, dx2, dy2); + + /* Q: Can we determine that the lines do not intersect (within range) + * much more cheaply than computing the intersection point i.e. by + * avoiding the division? + * + * X = ax + t * adx = bx + s * bdx; + * Y = ay + t * ady = by + s * bdy; + * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) + * => t * L = R + * + * Therefore we can reject any intersection (under the criteria for + * valid intersection events) if: + * L^R < 0 => t < 0, or + * L t > 1 + * + * (where top/bottom must at least extend to the line endpoints). + * + * A similar substitution can be performed for s, yielding: + * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by) + */ + R = det32_64 (dx2, dy2, + b->edge.line.p1.x - a->edge.line.p1.x, + b->edge.line.p1.y - a->edge.line.p1.y); + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return FALSE; + } else { + if (_cairo_int64_le (den_det, R)) + return FALSE; + } + + R = det32_64 (dy1, dx1, + a->edge.line.p1.y - b->edge.line.p1.y, + a->edge.line.p1.x - b->edge.line.p1.x); + if (_cairo_int64_negative (den_det)) { + if (_cairo_int64_ge (den_det, R)) + return FALSE; + } else { + if (_cairo_int64_le (den_det, R)) + return FALSE; + } + + /* We now know that the two lines should intersect within range. */ + + a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y, + a->edge.line.p2.x, a->edge.line.p2.y); + b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y, + b->edge.line.p2.x, b->edge.line.p2.y); + + /* x = det (a_det, dx1, b_det, dx2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1, + b_det, dx2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; +#if 0 + intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->x.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->x.exactness = INEXACT; + } +#endif + intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo); + + /* y = det (a_det, dy1, b_det, dy2) / den_det */ + qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1, + b_det, dy2), + den_det); + if (_cairo_int64_eq (qr.rem, den_det)) + return FALSE; +#if 0 + intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT; +#else + intersection->y.exactness = EXACT; + if (! _cairo_int64_is_zero (qr.rem)) { + if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem)) + qr.rem = _cairo_int64_negate (qr.rem); + qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2)); + if (_cairo_int64_ge (qr.rem, den_det)) { + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + } else + intersection->y.exactness = INEXACT; + } +#endif + intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); + + return TRUE; +} + +static int +_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a, + int32_t b) +{ + /* First compare the quotient */ + if (a.ordinate > b) + return +1; + if (a.ordinate < b) + return -1; + /* With quotient identical, if remainder is 0 then compare equal */ + /* Otherwise, the non-zero remainder makes a > b */ + return INEXACT == a.exactness; +} + +/* Does the given edge contain the given point. The point must already + * be known to be contained within the line determined by the edge, + * (most likely the point results from an intersection of this edge + * with another). + * + * If we had exact arithmetic, then this function would simply be a + * matter of examining whether the y value of the point lies within + * the range of y values of the edge. But since intersection points + * are not exact due to being rounded to the nearest integer within + * the available precision, we must also examine the x value of the + * point. + * + * The definition of "contains" here is that the given intersection + * point will be seen by the sweep line after the start event for the + * given edge and before the stop event for the edge. See the comments + * in the implementation for more details. + */ +static cairo_bool_t +_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge, + cairo_bo_intersect_point_t *point) +{ + int cmp_top, cmp_bottom; + + /* XXX: When running the actual algorithm, we don't actually need to + * compare against edge->top at all here, since any intersection above + * top is eliminated early via a slope comparison. We're leaving these + * here for now only for the sake of the quadratic-time intersection + * finder which needs them. + */ + + cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.top); + cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, + edge->edge.bottom); + + if (cmp_top < 0 || cmp_bottom > 0) + { + return FALSE; + } + + if (cmp_top > 0 && cmp_bottom < 0) + { + return TRUE; + } + + /* At this stage, the point lies on the same y value as either + * edge->top or edge->bottom, so we have to examine the x value in + * order to properly determine containment. */ + + /* If the y value of the point is the same as the y value of the + * top of the edge, then the x value of the point must be greater + * to be considered as inside the edge. Similarly, if the y value + * of the point is the same as the y value of the bottom of the + * edge, then the x value of the point must be less to be + * considered as inside. */ + + if (cmp_top == 0) { + cairo_fixed_t top_x; + + top_x = _line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.top); + return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0; + } else { /* cmp_bottom == 0 */ + cairo_fixed_t bot_x; + + bot_x = _line_compute_intersection_x_for_y (&edge->edge.line, + edge->edge.bottom); + return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0; + } +} + +/* Compute the intersection of two edges. The result is provided as a + * coordinate pair of 128-bit integers. + * + * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection + * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the + * intersection of the lines defined by the edges occurs outside of + * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges + * are exactly parallel. + * + * Note that when determining if a candidate intersection is "inside" + * an edge, we consider both the infinitesimal shortening and the + * infinitesimal tilt rules described by John Hobby. Specifically, if + * the intersection is exactly the same as an edge point, it is + * effectively outside (no intersection is returned). Also, if the + * intersection point has the same + */ +static cairo_bool_t +_cairo_bo_edge_intersect (cairo_bo_edge_t *a, + cairo_bo_edge_t *b, + cairo_bo_point32_t *intersection) +{ + cairo_bo_intersect_point_t quorem; + + if (! intersect_lines (a, b, &quorem)) + return FALSE; + + if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) + return FALSE; + + if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) + return FALSE; + + /* Now that we've correctly compared the intersection point and + * determined that it lies within the edge, then we know that we + * no longer need any more bits of storage for the intersection + * than we do for our edge coordinates. We also no longer need the + * remainder from the division. */ + intersection->x = quorem.x.ordinate; + intersection->y = quorem.y.ordinate; + + return TRUE; +} + +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) +{ + int cmp; + + cmp = _cairo_bo_point32_compare (&a->point, &b->point); + if (cmp) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a - b; +} + +static inline void +_pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; +} + +static inline void +_pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_status_t +_pqueue_grow (pqueue_t *pq) +{ + cairo_bo_event_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (new_elements, pq->elements_embedded, + sizeof (pq->elements_embedded)); + } else { + new_elements = _cairo_realloc_ab (pq->elements, + pq->max_size, + sizeof (cairo_bo_event_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pq->elements = new_elements; + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_status_t +_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event) +{ + cairo_bo_event_t **elements; + int i, parent; + + if (unlikely (pq->size + 1 == pq->max_size)) { + cairo_status_t status; + + status = _pqueue_grow (pq); + if (unlikely (status)) + return status; + } + + elements = pq->elements; + + for (i = ++pq->size; + i != PQ_FIRST_ENTRY && + cairo_bo_event_compare (event, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = event; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_pop (pqueue_t *pq) +{ + cairo_bo_event_t **elements = pq->elements; + cairo_bo_event_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + cairo_bo_event_compare (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (cairo_bo_event_compare (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline cairo_status_t +_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue, + cairo_bo_event_type_t type, + cairo_bo_edge_t *e1, + cairo_bo_edge_t *e2, + const cairo_point_t *point) +{ + cairo_bo_queue_event_t *event; + + event = _cairo_freepool_alloc (&queue->pool); + if (unlikely (event == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event->type = type; + event->e1 = e1; + event->e2 = e2; + event->point = *point; + + return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event); +} + +static void +_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue, + cairo_bo_event_t *event) +{ + _cairo_freepool_free (&queue->pool, event); +} + +static cairo_bo_event_t * +_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue) +{ + cairo_bo_event_t *event, *cmp; + + event = event_queue->pqueue.elements[PQ_FIRST_ENTRY]; + cmp = *event_queue->start_events; + if (event == NULL || + (cmp != NULL && cairo_bo_event_compare (cmp, event) < 0)) + { + event = cmp; + event_queue->start_events++; + } + else + { + _pqueue_pop (&event_queue->pqueue); + } + + return event; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare) + +static void +_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue, + cairo_bo_event_t **start_events, + int num_events) +{ + _cairo_bo_event_queue_sort (start_events, num_events); + start_events[num_events] = NULL; + + event_queue->start_events = start_events; + + _cairo_freepool_init (&event_queue->pool, + sizeof (cairo_bo_queue_event_t)); + _pqueue_init (&event_queue->pqueue); + event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL; +} + +static cairo_status_t +_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *edge) +{ + cairo_bo_point32_t point; + + point.y = edge->edge.bottom; + point.x = _line_compute_intersection_x_for_y (&edge->edge.line, + point.y); + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_STOP, + edge, NULL, + &point); +} + +static void +_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue) +{ + _pqueue_fini (&event_queue->pqueue); + _cairo_freepool_fini (&event_queue->pool); +} + +static inline cairo_status_t +_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue, + cairo_bo_edge_t *left, + cairo_bo_edge_t *right) +{ + cairo_bo_point32_t intersection; + + if (_line_equal (&left->edge.line, &right->edge.line)) + return CAIRO_STATUS_SUCCESS; + + /* The names "left" and "right" here are correct descriptions of + * the order of the two edges within the active edge list. So if a + * slope comparison also puts left less than right, then we know + * that the intersection of these two segments has already + * occurred before the current sweep line position. */ + if (_slope_compare (left, right) <= 0) + return CAIRO_STATUS_SUCCESS; + + if (! _cairo_bo_edge_intersect (left, right, &intersection)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_bo_event_queue_insert (event_queue, + CAIRO_BO_EVENT_TYPE_INTERSECTION, + left, right, + &intersection); +} + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line) +{ + sweep_line->head = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static cairo_status_t +_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (sweep_line->current_edge != NULL) { + cairo_bo_edge_t *prev, *next; + int cmp; + + cmp = _cairo_bo_sweep_line_compare_edges (sweep_line, + sweep_line->current_edge, + edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + next, edge) < 0) + { + prev = next, next = prev->next; + } + + prev->next = edge; + edge->prev = prev; + edge->next = next; + if (next != NULL) + next->prev = edge; + } else if (cmp > 0) { + next = sweep_line->current_edge; + prev = next->prev; + while (prev != NULL && + _cairo_bo_sweep_line_compare_edges (sweep_line, + prev, edge) > 0) + { + next = prev, prev = next->prev; + } + + next->prev = edge; + edge->next = next; + edge->prev = prev; + if (prev != NULL) + prev->next = edge; + else + sweep_line->head = edge; + } else { + prev = sweep_line->current_edge; + edge->prev = prev; + edge->next = prev->next; + if (prev->next != NULL) + prev->next->prev = edge; + prev->next = edge; + } + } else { + sweep_line->head = edge; + } + + sweep_line->current_edge = edge; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *edge) +{ + if (edge->prev != NULL) + edge->prev->next = edge->next; + else + sweep_line->head = edge->next; + + if (edge->next != NULL) + edge->next->prev = edge->prev; + + if (sweep_line->current_edge == edge) + sweep_line->current_edge = edge->prev ? edge->prev : edge->next; +} + +static void +_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_edge_t *left, + cairo_bo_edge_t *right) +{ + if (left->prev != NULL) + left->prev->next = right; + else + sweep_line->head = right; + + if (right->next != NULL) + right->next->prev = left; + + left->next = right->next; + right->next = left; + + right->prev = left->prev; + left->prev = right; +} + +static inline cairo_bool_t +edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + if (_line_equal (&a->edge.line, &b->edge.line)) + return TRUE; + + if (_slope_compare (a, b)) + return FALSE; + + /* The choice of y is not truly arbitrary since we must guarantee that it + * is greater than the start of either line. + */ + if (a->edge.line.p1.y == b->edge.line.p1.y) { + return a->edge.line.p1.x == b->edge.line.p1.x; + } else if (a->edge.line.p2.y == b->edge.line.p2.y) { + return a->edge.line.p2.x == b->edge.line.p2.x; + } else if (a->edge.line.p1.y < b->edge.line.p1.y) { + return edge_compare_for_y_against_x (b, + a->edge.line.p1.y, + a->edge.line.p1.x) == 0; + } else { + return edge_compare_for_y_against_x (a, + b->edge.line.p1.y, + b->edge.line.p1.x) == 0; + } +} + +static void +_cairo_bo_edge_end (cairo_bo_edge_t *left, + int32_t bot, + cairo_polygon_t *polygon) +{ + cairo_bo_deferred_t *d = &left->deferred; + + if (likely (d->top < bot)) { + _cairo_polygon_add_line (polygon, + &left->edge.line, + d->top, bot, + 1); + _cairo_polygon_add_line (polygon, + &d->right->edge.line, + d->top, bot, + -1); + } + + d->right = NULL; +} + + +static inline void +_cairo_bo_edge_start_or_continue (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_polygon_t *polygon) +{ + if (left->deferred.right == right) + return; + + if (left->deferred.right != NULL) { + if (right != NULL && edges_colinear (left->deferred.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred.right = right; + return; + } + + _cairo_bo_edge_end (left, top, polygon); + } + + if (right != NULL && ! edges_colinear (left, right)) { + left->deferred.top = top; + left->deferred.right = right; + } +} + +static inline void +_active_edges_to_polygon (cairo_bo_edge_t *left, + int32_t top, + cairo_fill_rule_t fill_rule, + cairo_polygon_t *polygon) +{ + cairo_bo_edge_t *right; + unsigned int mask; + + if (fill_rule == CAIRO_FILL_RULE_WINDING) + mask = ~0; + else + mask = 1; + + while (left != NULL) { + int in_out = left->edge.dir; + + right = left->next; + if (left->deferred.right == NULL) { + while (right != NULL && right->deferred.right == NULL) + right = right->next; + + if (right != NULL && edges_colinear (left, right)) { + /* continuation on left */ + left->deferred = right->deferred; + right->deferred.right = NULL; + } + } + + right = left->next; + while (right != NULL) { + if (right->deferred.right != NULL) + _cairo_bo_edge_end (right, top, polygon); + + in_out += right->edge.dir; + if ((in_out & mask) == 0) { + /* skip co-linear edges */ + if (right->next == NULL || !edges_colinear (right, right->next)) + break; + } + + right = right->next; + } + + _cairo_bo_edge_start_or_continue (left, right, top, polygon); + + left = right; + if (left != NULL) + left = left->next; + } +} + + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, + int num_events, + cairo_fill_rule_t fill_rule, + cairo_polygon_t *polygon) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */ + cairo_bo_event_queue_t event_queue; + cairo_bo_sweep_line_t sweep_line; + cairo_bo_event_t *event; + cairo_bo_edge_t *left, *right; + cairo_bo_edge_t *e1, *e2; + + _cairo_bo_event_queue_init (&event_queue, start_events, num_events); + _cairo_bo_sweep_line_init (&sweep_line); + + while ((event = _cairo_bo_event_dequeue (&event_queue))) { + if (event->point.y != sweep_line.current_y) { + _active_edges_to_polygon (sweep_line.head, + sweep_line.current_y, + fill_rule, polygon); + + sweep_line.current_y = event->point.y; + } + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + e1 = &((cairo_bo_start_event_t *) event)->edge; + + status = _cairo_bo_sweep_line_insert (&sweep_line, e1); + if (unlikely (status)) + goto unwind; + + status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); + if (unlikely (status)) + goto unwind; + + left = e1->prev; + right = e1->next; + + if (left != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + + break; + + case CAIRO_BO_EVENT_TYPE_STOP: + e1 = ((cairo_bo_queue_event_t *) event)->e1; + _cairo_bo_event_queue_delete (&event_queue, event); + + left = e1->prev; + right = e1->next; + + _cairo_bo_sweep_line_delete (&sweep_line, e1); + + if (e1->deferred.right != NULL) + _cairo_bo_edge_end (e1, e1->edge.bottom, polygon); + + if (left != NULL && right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); + if (unlikely (status)) + goto unwind; + } + + break; + + case CAIRO_BO_EVENT_TYPE_INTERSECTION: + e1 = ((cairo_bo_queue_event_t *) event)->e1; + e2 = ((cairo_bo_queue_event_t *) event)->e2; + _cairo_bo_event_queue_delete (&event_queue, event); + + /* skip this intersection if its edges are not adjacent */ + if (e2 != e1->next) + break; + + left = e1->prev; + right = e2->next; + + _cairo_bo_sweep_line_swap (&sweep_line, e1, e2); + + /* after the swap e2 is left of e1 */ + + if (left != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2); + if (unlikely (status)) + goto unwind; + } + + if (right != NULL) { + status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); + if (unlikely (status)) + goto unwind; + } + + break; + } + } + + unwind: + _cairo_bo_event_queue_fini (&event_queue); + + return status; +} + +cairo_status_t +_cairo_polygon_reduce (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)]; + cairo_bo_start_event_t *events; + cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + cairo_bo_event_t **event_ptrs; + int num_limits; + int num_events; + int i; + + num_events = polygon->num_edges; + if (unlikely (0 == num_events)) + return CAIRO_STATUS_SUCCESS; + + if (DEBUG_POLYGON) { + FILE *file = fopen ("reduce_in.txt", "w"); + _cairo_debug_print_polygon (file, polygon); + fclose (file); + } + + events = stack_events; + event_ptrs = stack_event_ptrs; + if (num_events > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (cairo_bo_start_event_t) + + sizeof (cairo_bo_event_t *), + sizeof (cairo_bo_event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (cairo_bo_event_t **) (events + num_events); + } + + for (i = 0; i < num_events; i++) { + event_ptrs[i] = (cairo_bo_event_t *) &events[i]; + + events[i].type = CAIRO_BO_EVENT_TYPE_START; + events[i].point.y = polygon->edges[i].top; + events[i].point.x = + _line_compute_intersection_x_for_y (&polygon->edges[i].line, + events[i].point.y); + + events[i].edge.edge = polygon->edges[i]; + events[i].edge.deferred.right = NULL; + events[i].edge.prev = NULL; + events[i].edge.next = NULL; + } + + num_limits = polygon->num_limits; polygon->num_limits = 0; + polygon->num_edges = 0; + + status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, + num_events, + fill_rule, + polygon); + polygon->num_limits = num_limits; + + if (events != stack_events) + free (events); + + if (DEBUG_POLYGON) { + FILE *file = fopen ("reduce_out.txt", "w"); + _cairo_debug_print_polygon (file, polygon); + fclose (file); + } + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-polygon.c b/gfx/cairo/cairo/src/cairo-polygon.c index 1b5fab02bae1..0e0b81328531 100644 --- a/gfx/cairo/cairo/src/cairo-polygon.c +++ b/gfx/cairo/cairo/src/cairo-polygon.c @@ -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 © 2002 University of Southern California @@ -36,33 +37,52 @@ #include "cairoint.h" +#include "cairo-boxes-private.h" +#include "cairo-contour-private.h" #include "cairo-error-private.h" -#include "cairo-slope-private.h" -void -_cairo_polygon_init (cairo_polygon_t *polygon) +#define DEBUG_POLYGON 0 + +#if DEBUG_POLYGON && !NDEBUG +static void +assert_last_edge_is_valid(cairo_polygon_t *polygon, + const cairo_box_t *limit) { - VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); + cairo_edge_t *edge; + cairo_fixed_t x; - polygon->status = CAIRO_STATUS_SUCCESS; + edge = &polygon->edges[polygon->num_edges-1]; - polygon->num_edges = 0; + assert (edge->bottom > edge->top); + assert (edge->top >= limit->p1.y); + assert (edge->bottom <= limit->p2.y); - polygon->edges = polygon->edges_embedded; - polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); + x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1, + &edge->line.p2, + edge->top); + assert (x >= limit->p1.x); + assert (x <= limit->p2.x); - polygon->has_current_point = FALSE; - polygon->has_current_edge = FALSE; - polygon->num_limits = 0; - - polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; - polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; + x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1, + &edge->line.p2, + edge->bottom); + assert (x >= limit->p1.x); + assert (x <= limit->p2.x); } +#else +#define assert_last_edge_is_valid(p, l) +#endif + +static void +_cairo_polygon_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + int dir); void -_cairo_polygon_limit (cairo_polygon_t *polygon, - const cairo_box_t *limits, - int num_limits) +_cairo_polygon_limit (cairo_polygon_t *polygon, + const cairo_box_t *limits, + int num_limits) { int n; @@ -87,13 +107,148 @@ _cairo_polygon_limit (cairo_polygon_t *polygon, } } +void +_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon, + const cairo_clip_t *clip) +{ + if (clip) + _cairo_polygon_limit (polygon, clip->boxes, clip->num_boxes); + else + _cairo_polygon_limit (polygon, 0, 0); +} + +void +_cairo_polygon_init (cairo_polygon_t *polygon, + const cairo_box_t *limits, + int num_limits) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); + + polygon->status = CAIRO_STATUS_SUCCESS; + + polygon->num_edges = 0; + + polygon->edges = polygon->edges_embedded; + polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); + + polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; + polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; + + _cairo_polygon_limit (polygon, limits, num_limits); +} + +void +_cairo_polygon_init_with_clip (cairo_polygon_t *polygon, + const cairo_clip_t *clip) +{ + if (clip) + _cairo_polygon_init (polygon, clip->boxes, clip->num_boxes); + else + _cairo_polygon_init (polygon, 0, 0); +} + +cairo_status_t +_cairo_polygon_init_boxes (cairo_polygon_t *polygon, + const cairo_boxes_t *boxes) +{ + const struct _cairo_boxes_chunk *chunk; + int i; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); + + polygon->status = CAIRO_STATUS_SUCCESS; + + polygon->num_edges = 0; + + polygon->edges = polygon->edges_embedded; + polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); + if (boxes->num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) { + polygon->edges_size = 2 * boxes->num_boxes; + polygon->edges = _cairo_malloc_ab (polygon->edges_size, + 2*sizeof(cairo_edge_t)); + if (unlikely (polygon->edges == NULL)) + return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; + polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; + + polygon->limits = NULL; + polygon->num_limits = 0; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + cairo_point_t p1, p2; + + p1 = chunk->base[i].p1; + p2.x = p1.x; + p2.y = chunk->base[i].p2.y; + _cairo_polygon_add_edge (polygon, &p1, &p2, 1); + + p1 = chunk->base[i].p2; + p2.x = p1.x; + p2.y = chunk->base[i].p1.y; + _cairo_polygon_add_edge (polygon, &p1, &p2, 1); + } + } + + return polygon->status; +} + +cairo_status_t +_cairo_polygon_init_box_array (cairo_polygon_t *polygon, + cairo_box_t *boxes, + int num_boxes) +{ + int i; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); + + polygon->status = CAIRO_STATUS_SUCCESS; + + polygon->num_edges = 0; + + polygon->edges = polygon->edges_embedded; + polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded); + if (num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) { + polygon->edges_size = 2 * num_boxes; + polygon->edges = _cairo_malloc_ab (polygon->edges_size, + 2*sizeof(cairo_edge_t)); + if (unlikely (polygon->edges == NULL)) + return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX; + polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN; + + polygon->limits = NULL; + polygon->num_limits = 0; + + for (i = 0; i < num_boxes; i++) { + cairo_point_t p1, p2; + + p1 = boxes[i].p1; + p2.x = p1.x; + p2.y = boxes[i].p2.y; + _cairo_polygon_add_edge (polygon, &p1, &p2, 1); + + p1 = boxes[i].p2; + p2.x = p1.x; + p2.y = boxes[i].p1.y; + _cairo_polygon_add_edge (polygon, &p1, &p2, 1); + } + + return polygon->status; +} + + void _cairo_polygon_fini (cairo_polygon_t *polygon) { if (polygon->edges != polygon->edges_embedded) free (polygon->edges); - VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t))); } /* make room for at least one more edge */ @@ -185,156 +340,156 @@ _add_clipped_edge (cairo_polygon_t *polygon, const int top, const int bottom, const int dir) { - cairo_point_t p[2]; - int top_y, bot_y; + cairo_point_t bot_left, top_right; + cairo_fixed_t top_y, bot_y; int n; for (n = 0; n < polygon->num_limits; n++) { const cairo_box_t *limits = &polygon->limits[n]; + cairo_fixed_t pleft, pright; if (top >= limits->p2.y) continue; if (bottom <= limits->p1.y) continue; - if (p1->x >= limits->p1.x && p2->x >= limits->p1.x && - p1->x <= limits->p2.x && p2->x <= limits->p2.x) - { - top_y = top; - if (top_y < limits->p1.y) - top_y = limits->p1.y; + bot_left.x = limits->p1.x; + bot_left.y = limits->p2.y; - bot_y = bottom; - if (bot_y > limits->p2.y) - bot_y = limits->p2.y; + top_right.x = limits->p2.x; + top_right.y = limits->p1.y; + + /* The useful region */ + top_y = MAX (top, limits->p1.y); + bot_y = MIN (bottom, limits->p2.y); + + /* The projection of the edge on the horizontal axis */ + pleft = MIN (p1->x, p2->x); + pright = MAX (p1->x, p2->x); + + if (limits->p1.x <= pleft && pright <= limits->p2.x) { + /* Projection of the edge completely contained in the box: + * clip vertically by restricting top and bottom */ _add_edge (polygon, p1, p2, top_y, bot_y, dir); - } - else if (p1->x <= limits->p1.x && p2->x <= limits->p1.x) - { - p[0].x = limits->p1.x; - p[0].y = limits->p1.y; - top_y = top; - if (top_y < p[0].y) - top_y = p[0].y; + assert_last_edge_is_valid (polygon, limits); + } else if (pright <= limits->p1.x) { + /* Projection of the edge to the left of the box: + * replace with the left side of the box (clipped top/bottom) */ - p[1].x = limits->p1.x; - p[1].y = limits->p2.y; - bot_y = bottom; - if (bot_y > p[1].y) - bot_y = p[1].y; + _add_edge (polygon, &limits->p1, &bot_left, top_y, bot_y, dir); + assert_last_edge_is_valid (polygon, limits); + } else if (limits->p2.x <= pleft) { + /* Projection of the edge to the right of the box: + * replace with the right side of the box (clipped top/bottom) */ - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - } - else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x) - { - p[0].x = limits->p2.x; - p[0].y = limits->p1.y; - top_y = top; - if (top_y < p[0].y) - top_y = p[0].y; + _add_edge (polygon, &top_right, &limits->p2, top_y, bot_y, dir); + assert_last_edge_is_valid (polygon, limits); + } else { + /* The edge and the box intersect in a generic way */ + cairo_fixed_t left_y, right_y; + cairo_bool_t top_left_to_bottom_right; - p[1].x = limits->p2.x; - p[1].y = limits->p2.y; - bot_y = bottom; - if (bot_y > p[1].y) - bot_y = p[1].y; + /* + * The edge intersects the lines corresponding to the left + * and right sides of the limit box at left_y and right_y, + * but we need to add edges for the range from top_y to + * bot_y. + * + * For both intersections, there are three cases: + * + * 1) It is outside the vertical range of the limit + * box. In this case we can simply further clip the + * edge we will be emitting (i.e. restrict its + * top/bottom limits to those of the limit box). + * + * 2) It is inside the vertical range of the limit + * box. In this case, we need to add the vertical edge + * connecting the correct vertex to the intersection, + * in order to preserve the winding count. + * + * 3) It is exactly on the box. In this case, do nothing. + * + * These operations restrict the active range (stored in + * top_y/bot_y) so that the p1-p2 edge is completely + * inside the box if it is clipped to this vertical range. + */ - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - } - else - { - int left_y, right_y; - int p1_y, p2_y; - - left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, - limits->p1.x); - right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, - limits->p2.x); - - p1_y = top; - p2_y = bottom; - - if (p1->x < p2->x) { - if (p1->x < limits->p1.x && left_y > limits->p1.y) { - p[0].x = limits->p1.x; - p[0].y = limits->p1.y; - top_y = p1_y; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p1.x; - p[1].y = limits->p2.y; - bot_y = left_y; - if (bot_y > p[1].y) - bot_y = p[1].y; - - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p1_y = bot_y; + top_left_to_bottom_right = (p1->x <= p2->x) == (p1->y <= p2->y); + if (top_left_to_bottom_right) { + if (pleft >= limits->p1.x) { + left_y = top_y; + } else { + left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p1.x); + if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x) + left_y++; } - if (p2->x > limits->p2.x && right_y < limits->p2.y) { - p[0].x = limits->p2.x; - p[0].y = limits->p1.y; - top_y = right_y; - if (top_y < p[0].y) - top_y = p[0].y; + left_y = MIN (left_y, bot_y); + if (top_y < left_y) { + _add_edge (polygon, &limits->p1, &bot_left, + top_y, left_y, dir); + assert_last_edge_is_valid (polygon, limits); + top_y = left_y; + } - p[1].x = limits->p2.x; - p[1].y = limits->p2.y; - bot_y = p2_y; - if (bot_y > p[1].y) - bot_y = p[1].y; + if (pright <= limits->p2.x) { + right_y = bot_y; + } else { + right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p2.x); + if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x) + right_y--; + } - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p2_y = top_y; + right_y = MAX (right_y, top_y); + if (bot_y > right_y) { + _add_edge (polygon, &top_right, &limits->p2, + right_y, bot_y, dir); + assert_last_edge_is_valid (polygon, limits); + bot_y = right_y; } } else { - if (p1->x > limits->p2.x && right_y > limits->p1.y) { - p[0].x = limits->p2.x; - p[0].y = limits->p1.y; - top_y = p1_y; - if (top_y < p[0].y) - top_y = p[0].y; - - p[1].x = limits->p2.x; - p[1].y = limits->p2.y; - bot_y = right_y; - if (bot_y > p[1].y) - bot_y = p[1].y; - - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p1_y = bot_y; + if (pright <= limits->p2.x) { + right_y = top_y; + } else { + right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p2.x); + if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x) + right_y++; } - if (p2->x < limits->p1.x && left_y < limits->p2.y) { - p[0].x = limits->p1.x; - p[0].y = limits->p1.y; - top_y = left_y; - if (top_y < p[0].y) - top_y = p[0].y; + right_y = MIN (right_y, bot_y); + if (top_y < right_y) { + _add_edge (polygon, &top_right, &limits->p2, + top_y, right_y, dir); + assert_last_edge_is_valid (polygon, limits); + top_y = right_y; + } - p[1].x = limits->p1.x; - p[1].y = limits->p2.y; - bot_y = p2_y; - if (bot_y > p[1].y) - bot_y = p[1].y; + if (pleft >= limits->p1.x) { + left_y = bot_y; + } else { + left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2, + limits->p1.x); + if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x) + left_y--; + } - if (bot_y > top_y) - _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir); - p2_y = top_y; + left_y = MAX (left_y, top_y); + if (bot_y > left_y) { + _add_edge (polygon, &limits->p1, &bot_left, + left_y, bot_y, dir); + assert_last_edge_is_valid (polygon, limits); + bot_y = left_y; } } - if (p1_y < limits->p1.y) - p1_y = limits->p1.y; - if (p2_y > limits->p2.y) - p2_y = limits->p2.y; - if (p2_y > p1_y) - _add_edge (polygon, p1, p2, p1_y, p2_y, dir); + if (top_y != bot_y) { + _add_edge (polygon, p1, p2, top_y, bot_y, dir); + assert_last_edge_is_valid (polygon, limits); + } } } } @@ -342,20 +497,17 @@ _add_clipped_edge (cairo_polygon_t *polygon, static void _cairo_polygon_add_edge (cairo_polygon_t *polygon, const cairo_point_t *p1, - const cairo_point_t *p2) + const cairo_point_t *p2, + int dir) { - int dir; - /* drop horizontal edges */ if (p1->y == p2->y) return; - if (p1->y < p2->y) { - dir = 1; - } else { + if (p1->y > p2->y) { const cairo_point_t *t; t = p1, p1 = p2, p2 = t; - dir = -1; + dir = -dir; } if (polygon->num_limits) { @@ -375,7 +527,7 @@ _cairo_polygon_add_external_edge (void *polygon, const cairo_point_t *p1, const cairo_point_t *p2) { - _cairo_polygon_add_edge (polygon, p1, p2); + _cairo_polygon_add_edge (polygon, p1, p2, 1); return _cairo_polygon_status (polygon); } @@ -406,87 +558,51 @@ _cairo_polygon_add_line (cairo_polygon_t *polygon, return polygon->status; } -/* flattened path operations */ - cairo_status_t -_cairo_polygon_move_to (cairo_polygon_t *polygon, - const cairo_point_t *point) +_cairo_polygon_add_contour (cairo_polygon_t *polygon, + const cairo_contour_t *contour) { - if (polygon->has_current_edge) { - _cairo_polygon_add_edge (polygon, - &polygon->last_point, - &polygon->current_point); - polygon->has_current_edge = FALSE; - } + const struct _cairo_contour_chain *chain; + const cairo_point_t *prev = NULL; + int i; - if (! polygon->has_current_point) { - polygon->first_point = *point; - polygon->has_current_point = TRUE; - } + if (contour->chain.num_points <= 1) + return CAIRO_INT_STATUS_SUCCESS; - polygon->current_point = *point; - return polygon->status; -} - -cairo_status_t -_cairo_polygon_line_to (cairo_polygon_t *polygon, - const cairo_point_t *point) -{ - /* squash collinear edges */ - if (polygon->has_current_edge) { - if (polygon->current_point.x != point->x || - polygon->current_point.y != point->y) - { - cairo_slope_t this; - - _cairo_slope_init (&this, &polygon->current_point, point); - if (_cairo_slope_equal (&polygon->current_edge, &this)) { - polygon->current_point = *point; - return CAIRO_STATUS_SUCCESS; - } - - _cairo_polygon_add_edge (polygon, - &polygon->last_point, - &polygon->current_point); - - polygon->last_point = polygon->current_point; - polygon->current_edge = this; + prev = &contour->chain.points[0]; + for (chain = &contour->chain; chain; chain = chain->next) { + for (i = 0; i < chain->num_points; i++) { + _cairo_polygon_add_edge (polygon, prev, &chain->points[i], + contour->direction); + prev = &chain->points[i]; } - } else if (polygon->has_current_point) { - if (polygon->current_point.x != point->x || - polygon->current_point.y != point->y) - { - polygon->last_point = polygon->current_point; - _cairo_slope_init (&polygon->current_edge, - &polygon->last_point, - point); - polygon->has_current_edge = TRUE; - } - } else { - polygon->first_point = *point; - polygon->has_current_point = TRUE; } - polygon->current_point = *point; return polygon->status; } -cairo_status_t -_cairo_polygon_close (cairo_polygon_t *polygon) +void +_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy) { - cairo_status_t status; + int n; - if (polygon->has_current_point) { - status = _cairo_polygon_line_to (polygon, &polygon->first_point); - polygon->has_current_point = FALSE; + dx = _cairo_fixed_from_int (dx); + dy = _cairo_fixed_from_int (dy); + + polygon->extents.p1.x += dx; + polygon->extents.p2.x += dx; + polygon->extents.p1.y += dy; + polygon->extents.p2.y += dy; + + for (n = 0; n < polygon->num_edges; n++) { + cairo_edge_t *e = &polygon->edges[n]; + + e->top += dy; + e->bottom += dy; + + e->line.p1.x += dx; + e->line.p2.x += dx; + e->line.p1.y += dy; + e->line.p2.y += dy; } - - if (polygon->has_current_edge) { - _cairo_polygon_add_edge (polygon, - &polygon->last_point, - &polygon->current_point); - polygon->has_current_edge = FALSE; - } - - return polygon->status; } diff --git a/gfx/cairo/cairo/src/cairo-private.h b/gfx/cairo/cairo/src/cairo-private.h index 901a69a31952..9f4f55b7cea6 100644 --- a/gfx/cairo/cairo/src/cairo-private.h +++ b/gfx/cairo/cairo/src/cairo-private.h @@ -36,22 +36,29 @@ #ifndef CAIRO_PRIVATE_H #define CAIRO_PRIVATE_H +#include "cairo-types-private.h" #include "cairo-reference-count-private.h" -#include "cairo-gstate-private.h" -#include "cairo-path-fixed-private.h" + +CAIRO_BEGIN_DECLS struct _cairo { cairo_reference_count_t ref_count; - cairo_status_t status; - cairo_user_data_array_t user_data; - cairo_gstate_t *gstate; - cairo_gstate_t gstate_tail[2]; - cairo_gstate_t *gstate_freelist; - - cairo_path_fixed_t path[1]; + const cairo_backend_t *backend; }; +cairo_private cairo_t * +_cairo_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_init (cairo_t *cr, + const cairo_backend_t *backend); + +cairo_private void +_cairo_fini (cairo_t *cr); + +CAIRO_END_DECLS + #endif /* CAIRO_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-ps-surface-private.h b/gfx/cairo/cairo/src/cairo-ps-surface-private.h index a5a8cd0dae3d..f184031902c7 100644 --- a/gfx/cairo/cairo/src/cairo-ps-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-ps-surface-private.h @@ -49,6 +49,21 @@ #include +typedef struct _cairo_ps_form { + cairo_hash_entry_t base; + unsigned char *unique_id; + unsigned long unique_id_length; + cairo_bool_t is_image; + int id; + cairo_surface_t *src_surface; + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_filter_t filter; + + /* Union of source extents required for all operations using this form */ + cairo_rectangle_int_t required_extents; +} cairo_ps_form_t; + typedef struct cairo_ps_surface { cairo_surface_t base; @@ -63,19 +78,15 @@ typedef struct cairo_ps_surface { cairo_output_stream_t *stream; cairo_bool_t eps; + cairo_bool_t contains_eps; cairo_content_t content; double width; double height; - cairo_rectangle_int_t page_bbox; - int bbox_x1, bbox_y1, bbox_x2, bbox_y2; + cairo_point_int_t document_bbox_p1, document_bbox_p2; /* in PS coordinates */ + cairo_rectangle_int_t surface_extents; + cairo_bool_t surface_bounded; cairo_matrix_t cairo_to_ps; - - /* XXX These 3 are used as temporary storage whilst emitting patterns */ - cairo_image_surface_t *image; - cairo_image_surface_t *acquired_image; - void *image_extra; - - cairo_bool_t use_string_datasource; + cairo_bool_t paint_proc; /* TRUE if surface will be used in a PaintProc */ cairo_bool_t current_pattern_is_solid_color; cairo_color_t current_color; @@ -95,6 +106,8 @@ typedef struct cairo_ps_surface { cairo_array_t dsc_setup_comments; cairo_array_t dsc_page_setup_comments; + cairo_array_t recording_surf_stack; + cairo_array_t *dsc_comment_target; cairo_ps_level_t ps_level; @@ -104,6 +117,9 @@ typedef struct cairo_ps_surface { cairo_pdf_operators_t pdf_operators; cairo_surface_t *paginated_surface; + cairo_hash_table_t *forms; + int num_forms; + long total_form_size; } cairo_ps_surface_t; #endif /* CAIRO_PS_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-ps-surface.c b/gfx/cairo/cairo/src/cairo-ps-surface.c index 4e7fb132ba10..4adccad6b7b0 100644 --- a/gfx/cairo/cairo/src/cairo-ps-surface.c +++ b/gfx/cairo/cairo/src/cairo-ps-surface.c @@ -53,21 +53,31 @@ * 2. Using gs to do PS -> PDF and PDF -> PS will always work well. */ -#define _BSD_SOURCE /* for ctime_r(), snprintf(), strdup() */ +#define _DEFAULT_SOURCE /* for ctime_r(), snprintf(), strdup() */ #include "cairoint.h" + #include "cairo-ps.h" #include "cairo-ps-surface-private.h" + #include "cairo-pdf-operators-private.h" +#include "cairo-pdf-shading-private.h" + +#include "cairo-array-private.h" #include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-list-inline.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-paginated-private.h" #include "cairo-recording-surface-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 #include @@ -75,7 +85,14 @@ #include #include -#define DEBUG_PS 0 +/* Forms are emitted at the start and stored in memory so we limit the + * total size of all forms to prevent running out of memory. If this + * limit is exceeded, surfaces that would be stored in forms are + * emitted each time the surface is used. */ +#define MAX_L2_FORM_DATA (256*1024) +#define MAX_L3_FORM_DATA (2*1024*1024) /* Assume Level 3 printers have more memory */ + +/* #define DEBUG_PS 1 */ #if DEBUG_PS #define DEBUG_FALLBACK(s) \ @@ -96,21 +113,88 @@ * * 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, + * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + * %CAIRO_MIME_TYPE_EPS, %CAIRO_MIME_TYPE_EPS_PARAMS. + * + * Source surfaces used by the PostScript surface that have a + * %CAIRO_MIME_TYPE_UNIQUE_ID mime type will be stored in PostScript + * printer memory for the duration of the print + * job. %CAIRO_MIME_TYPE_UNIQUE_ID should only be used for small + * frequently used sources. + * + * The %CAIRO_MIME_TYPE_CCITT_FAX and %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime types + * are documented in [CCITT Fax Images][ccitt]. + * + * # Embedding EPS files # {#eps} + * + * Encapsulated PostScript files can be embedded in the PS output by + * setting the CAIRO_MIME_TYPE_EPS mime data on a surface to the EPS + * data and painting the surface. The EPS will be scaled and + * translated to the extents of the surface the EPS data is attached + * to. + * + * The %CAIRO_MIME_TYPE_EPS mime type requires the + * %CAIRO_MIME_TYPE_EPS_PARAMS mime data to also be provided in order + * to specify the embeddding parameters. %CAIRO_MIME_TYPE_EPS_PARAMS + * mime data must contain a string of the form "bbox=[llx lly urx + * 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. + * + **/ /** * CAIRO_HAS_PS_SURFACE: - * + * * Defined if the PostScript surface backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.2 + **/ + +typedef enum { + CAIRO_PS_COMPRESS_NONE, + CAIRO_PS_COMPRESS_LZW, + CAIRO_PS_COMPRESS_DEFLATE +} cairo_ps_compress_t; + +typedef enum { + CAIRO_EMIT_SURFACE_ANALYZE, + CAIRO_EMIT_SURFACE_EMIT, + CAIRO_EMIT_SURFACE_EMIT_FORM +} cairo_emit_surface_mode_t; + +typedef struct { + /* input params */ + cairo_surface_t *src_surface; + cairo_operator_t op; + const cairo_rectangle_int_t *src_surface_extents; + cairo_bool_t src_surface_bounded; + const cairo_rectangle_int_t *src_op_extents; /* operation extents in src space */ + cairo_filter_t filter; + cairo_bool_t stencil_mask; /* TRUE if source is to be used as a mask */ + + /* output params */ + cairo_bool_t is_image; /* returns TRUE if PS image will be emitted */ + /* FALSE if recording will be emitted */ + long approx_size; + int eod_count; +} cairo_emit_surface_params_t; static const cairo_surface_backend_t cairo_ps_surface_backend; static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend; +static cairo_bool_t +_cairo_ps_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + static void -_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern); +_cairo_ps_form_emit (void *entry, void *closure); static const cairo_ps_level_t _cairo_ps_levels[] = { @@ -126,6 +210,14 @@ static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = "PS Level 3" }; +static const char *_cairo_ps_supported_mime_types[] = +{ + CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_CCITT_FAX, + CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + NULL +}; + typedef struct _cairo_page_standard_media { const char *name; int width; @@ -160,6 +252,40 @@ typedef struct _cairo_page_media { cairo_list_t link; } cairo_page_media_t; +static void +_cairo_ps_form_init_key (cairo_ps_form_t *key) +{ + key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + key->unique_id, key->unique_id_length); +} + +static cairo_bool_t +_cairo_ps_form_equal (const void *key_a, const void *key_b) +{ + const cairo_ps_form_t *a = key_a; + const cairo_ps_form_t *b = key_b; + + if (a->filter != b->filter) + return FALSE; + + if (a->unique_id_length != b->unique_id_length) + return FALSE; + + return memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0; +} + +static void +_cairo_ps_form_pluck (void *entry, void *closure) +{ + cairo_ps_form_t *surface_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &surface_entry->base); + free (surface_entry->unique_id); + cairo_surface_destroy (surface_entry->src_surface); + free (surface_entry); +} + static void _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) { @@ -169,6 +295,7 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) int i, num_comments; int level; const char *eps_header = ""; + cairo_bool_t has_bbox; if (surface->has_creation_date) now = surface->creation_date; @@ -185,18 +312,13 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) _cairo_output_stream_printf (surface->final_stream, "%%!PS-Adobe-3.0%s\n" - "%%%%Creator: cairo %s (http://cairographics.org)\n" + "%%%%Creator: cairo %s (https://cairographics.org)\n" "%%%%CreationDate: %s" - "%%%%Pages: %d\n" - "%%%%BoundingBox: %d %d %d %d\n", + "%%%%Pages: %d\n", eps_header, cairo_version_string (), ctime_r (&now, ctime_buf), - surface->num_pages, - surface->bbox_x1, - surface->bbox_y1, - surface->bbox_x2, - surface->bbox_y2); + surface->num_pages); _cairo_output_stream_printf (surface->final_stream, "%%%%DocumentData: Clean7Bit\n" @@ -224,15 +346,28 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) } } + has_bbox = FALSE; num_comments = _cairo_array_num_elements (&surface->dsc_header_comments); comments = _cairo_array_index (&surface->dsc_header_comments, 0); for (i = 0; i < num_comments; i++) { _cairo_output_stream_printf (surface->final_stream, "%s\n", comments[i]); + if (strncmp (comments[i], "%%BoundingBox:", 14) == 0) + has_bbox = TRUE; + free (comments[i]); comments[i] = NULL; } + if (!has_bbox) { + _cairo_output_stream_printf (surface->final_stream, + "%%%%BoundingBox: %d %d %d %d\n", + surface->document_bbox_p1.x, + surface->document_bbox_p1.y, + surface->document_bbox_p2.x, + surface->document_bbox_p2.y); + } + _cairo_output_stream_printf (surface->final_stream, "%%%%EndComments\n"); @@ -241,10 +376,7 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) if (surface->eps) { _cairo_output_stream_printf (surface->final_stream, - "/cairo_eps_state save def\n" - "/dict_count countdictstack def\n" - "/op_count count 1 sub def\n" - "userdict begin\n"); + "50 dict begin\n"); } else { _cairo_output_stream_printf (surface->final_stream, "/languagelevel where\n" @@ -279,9 +411,6 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) "/W* { eoclip } bind def\n" "/BT { } bind def\n" "/ET { } bind def\n" - "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n" - " { globaldict begin /?pdfmark /pop load def /pdfmark\n" - " /cleartomark load def end } ifelse\n" "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n" "/EMC { mark /EMC pdfmark } bind def\n" "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n" @@ -305,16 +434,72 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) " cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n" "/g { setgray } bind def\n" "/rg { setrgbcolor } bind def\n" - "/d1 { setcachedevice } bind def\n"); + "/d1 { setcachedevice } bind def\n" + "/cairo_data_source {\n" + " CairoDataIndex CairoData length lt\n" + " { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }\n" + " { () } ifelse\n" + "} def\n" + "/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def\n" + "/cairo_image { image cairo_flush_ascii85_file } def\n" + "/cairo_imagemask { imagemask cairo_flush_ascii85_file } def\n"); + + if (!surface->eps) { + _cairo_output_stream_printf (surface->final_stream, + "/cairo_set_page_size {\n" + " %% Change paper size, but only if different from previous paper size otherwise\n" + " %% duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size\n" + " %% so we use the same when checking if the size changes.\n" + " /setpagedevice where {\n" + " pop currentpagedevice\n" + " /PageSize known {\n" + " 2 copy\n" + " currentpagedevice /PageSize get aload pop\n" + " exch 4 1 roll\n" + " sub abs 5 gt\n" + " 3 1 roll\n" + " sub abs 5 gt\n" + " or\n" + " } {\n" + " true\n" + " } ifelse\n" + " {\n" + " 2 array astore\n" + " 2 dict begin\n" + " /PageSize exch def\n" + " /ImagingBBox null def\n" + " currentdict end\n" + " setpagedevice\n" + " } {\n" + " pop pop\n" + " } ifelse\n" + " } {\n" + " pop\n" + " } ifelse\n" + "} def\n"); + } + if (surface->contains_eps) { + _cairo_output_stream_printf (surface->final_stream, + "/cairo_eps_begin {\n" + " /cairo_save_state save def\n" + " /dict_count countdictstack def\n" + " /op_count count 1 sub def\n" + " userdict begin\n" + " /showpage { } def\n" + " 0 g 0 J 1 w 0 j 10 M [ ] 0 d n\n" + "} bind def\n" + "/cairo_eps_end {\n" + " count op_count sub { pop } repeat\n" + " countdictstack dict_count sub { end } repeat\n" + " cairo_save_state restore\n" + "} bind def\n"); + } _cairo_output_stream_printf (surface->final_stream, "%%%%EndProlog\n"); num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); if (num_comments) { - _cairo_output_stream_printf (surface->final_stream, - "%%%%BeginSetup\n"); - comments = _cairo_array_index (&surface->dsc_setup_comments, 0); for (i = 0; i < num_comments; i++) { _cairo_output_stream_printf (surface->final_stream, @@ -322,13 +507,9 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) free (comments[i]); comments[i] = NULL; } - - _cairo_output_stream_printf (surface->final_stream, - "%%%%EndSetup\n"); } } -#if CAIRO_HAS_FT_FONT static cairo_status_t _cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, cairo_scaled_font_subset_t *font_subset) @@ -353,14 +534,19 @@ _cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, "%% _cairo_ps_surface_emit_type1_font_subset\n"); #endif + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginResource: font %s\n", + subset.base_font); length = subset.header_length + subset.data_length + subset.trailer_length; _cairo_output_stream_write (surface->final_stream, subset.data, length); + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndResource\n"); _cairo_type1_subset_fini (&subset); return CAIRO_STATUS_SUCCESS; } -#endif + static cairo_status_t _cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, @@ -377,15 +563,18 @@ _cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, if (unlikely (status)) return status; - /* FIXME: Figure out document structure convention for fonts */ - #if DEBUG_PS _cairo_output_stream_printf (surface->final_stream, "%% _cairo_ps_surface_emit_type1_font_fallback\n"); #endif + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginResource: font %s\n", + subset.base_font); length = subset.header_length + subset.data_length + subset.trailer_length; _cairo_output_stream_write (surface->final_stream, subset.data, length); + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndResource\n"); _cairo_type1_fallback_fini (&subset); @@ -402,7 +591,7 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, cairo_status_t status; unsigned int i, begin, end; - status = _cairo_truetype_subset_init (&subset, font_subset); + status = _cairo_truetype_subset_init_ps (&subset, font_subset); if (unlikely (status)) return status; @@ -413,6 +602,9 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, "%% _cairo_ps_surface_emit_truetype_font_subset\n"); #endif + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginResource: font %s\n", + subset.ps_name); _cairo_output_stream_printf (surface->final_stream, "11 dict begin\n" "/FontType 42 def\n" @@ -426,14 +618,29 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ - for (i = 1; i < font_subset->num_glyphs; i++) { - if (font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (surface->final_stream, - "Encoding %d /%s put\n", - i, font_subset->glyph_names[i]); - } else { - _cairo_output_stream_printf (surface->final_stream, - "Encoding %d /g%d put\n", i, i); + if (font_subset->is_latin) { + for (i = 1; i < 256; i++) { + if (font_subset->latin_to_subset_glyph_index[i] > 0) { + if (font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /%s put\n", + i, font_subset->glyph_names[font_subset->latin_to_subset_glyph_index[i]]); + } else { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /g%ld put\n", i, font_subset->latin_to_subset_glyph_index[i]); + } + } + } + } else { + for (i = 1; i < font_subset->num_glyphs; i++) { + if (font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /%s put\n", + i, font_subset->glyph_names[i]); + } else { + _cairo_output_stream_printf (surface->final_stream, + "Encoding %d /g%d put\n", i, i); + } } } @@ -480,13 +687,15 @@ _cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, "/f-%d-%d currentdict end definefont pop\n", font_subset->font_id, font_subset->subset_id); - + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndResource\n"); _cairo_truetype_subset_fini (&subset); + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_ps_emit_imagemask (cairo_image_surface_t *image, cairo_output_stream_t *stream) { @@ -528,7 +737,7 @@ _cairo_ps_emit_imagemask (cairo_image_surface_t *image, return _cairo_output_stream_get_status (stream); } -static cairo_status_t +static cairo_int_status_t _cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { @@ -540,7 +749,8 @@ _cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_sub type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_ps_emit_imagemask, - surface->font_subsets); + surface->font_subsets, + TRUE); for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, @@ -576,17 +786,20 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, "%% _cairo_ps_surface_emit_type3_font_subset\n"); #endif + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginResource: font\n"); _cairo_output_stream_printf (surface->final_stream, "8 dict begin\n" "/FontType 3 def\n" - "/FontMatrix [1 0 0 1 0 0] def\n" + "/FontMatrix [1 0 0 -1 0 0] def\n" "/Encoding 256 array def\n" "0 1 255 { Encoding exch /.notdef put } for\n"); type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, NULL, _cairo_ps_emit_imagemask, - surface->font_subsets); + surface->font_subsets, + TRUE); status = type3_surface->status; if (unlikely (status)) return status; @@ -656,27 +869,26 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, - _cairo_fixed_to_double (font_bbox.p1.y), font_subset->font_id, font_subset->subset_id); + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndResource\n"); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_ps_surface_t *surface = closure; - cairo_status_t status; - + cairo_int_status_t status; status = _cairo_scaled_font_subset_create_glyph_names (font_subset); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; -#if CAIRO_HAS_FT_FONT status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; -#endif status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset); if (status != CAIRO_INT_STATUS_UNSUPPORTED) @@ -690,15 +902,15 @@ _cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_su return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_ps_surface_t *surface = closure; - cairo_status_t status; + cairo_int_status_t status; status = _cairo_scaled_font_subset_create_glyph_names (font_subset); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset); @@ -706,7 +918,7 @@ _cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subs return status; ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } static cairo_status_t @@ -742,6 +954,16 @@ _cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) surface); } + +static cairo_int_status_t +_cairo_ps_surface_emit_forms (cairo_ps_surface_t *surface) +{ + _cairo_hash_table_foreach (surface->forms, + _cairo_ps_form_emit, + surface); + return surface->base.status; +} + static cairo_status_t _cairo_ps_surface_emit_body (cairo_ps_surface_t *surface) { @@ -769,9 +991,7 @@ _cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) if (surface->eps) { _cairo_output_stream_printf (surface->final_stream, - "count op_count sub {pop} repeat\n" - "countdictstack dict_count sub {end} repeat\n" - "cairo_eps_state restore\n"); + "end\n"); } _cairo_output_stream_printf (surface->final_stream, @@ -790,11 +1010,11 @@ _path_covers_bbox (cairo_ps_surface_t *surface, _cairo_box_round_to_rectangle (&box, &rect); /* skip trivial whole-page clips */ - if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) { - if (rect.x == surface->page_bbox.x && - rect.width == surface->page_bbox.width && - rect.y == surface->page_bbox.y && - rect.height == surface->page_bbox.height) + if (_cairo_rectangle_intersect (&rect, &surface->surface_extents)) { + if (rect.x == surface->surface_extents.x && + rect.width == surface->surface_extents.width && + rect.y == surface->surface_extents.y && + rect.height == surface->surface_extents.height) { return TRUE; } @@ -883,7 +1103,7 @@ _cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) } } - page = malloc (sizeof (cairo_page_media_t)); + page = _cairo_malloc (sizeof (cairo_page_media_t)); if (unlikely (page == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; @@ -919,7 +1139,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, cairo_status_t status, status_ignored; cairo_ps_surface_t *surface; - surface = malloc (sizeof (cairo_ps_surface_t)); + surface = _cairo_malloc (sizeof (cairo_ps_surface_t)); if (unlikely (surface == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; @@ -928,7 +1148,8 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, _cairo_surface_init (&surface->base, &cairo_ps_surface_backend, NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ surface->final_stream = stream; @@ -956,23 +1177,30 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, goto CLEANUP_OUTPUT_STREAM; } + _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE); surface->has_creation_date = FALSE; surface->eps = FALSE; surface->ps_level = CAIRO_PS_LEVEL_3; surface->ps_level_used = CAIRO_PS_LEVEL_2; surface->width = width; surface->height = height; - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); + surface->surface_bounded = TRUE; surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; surface->force_fallbacks = FALSE; surface->content = CAIRO_CONTENT_COLOR_ALPHA; - surface->use_string_datasource = FALSE; surface->current_pattern_is_solid_color = FALSE; - - surface->page_bbox.x = 0; - surface->page_bbox.y = 0; - surface->page_bbox.width = width; - surface->page_bbox.height = height; + surface->document_bbox_p1.x = 0; + surface->document_bbox_p1.y = 0; + surface->document_bbox_p2.x = 0; + surface->document_bbox_p2.y = 0; + surface->total_form_size = 0; + surface->contains_eps = FALSE; + surface->paint_proc = FALSE; _cairo_surface_clipper_init (&surface->clipper, _cairo_ps_surface_clipper_intersect_clip_path); @@ -980,13 +1208,22 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, _cairo_pdf_operators_init (&surface->pdf_operators, surface->stream, &surface->cairo_to_ps, - surface->font_subsets); + surface->font_subsets, + TRUE); surface->num_pages = 0; cairo_list_init (&surface->document_media); _cairo_array_init (&surface->dsc_header_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *)); _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *)); + _cairo_array_init (&surface->recording_surf_stack, sizeof (unsigned int)); + + surface->num_forms = 0; + surface->forms = _cairo_hash_table_create (_cairo_ps_form_equal); + if (unlikely (surface->forms == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_FONT_SUBSETS; + } surface->dsc_comment_target = &surface->dsc_header_comments; @@ -1001,6 +1238,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, return surface->paginated_surface; } + CLEANUP_FONT_SUBSETS: _cairo_scaled_font_subsets_destroy (surface->font_subsets); CLEANUP_OUTPUT_STREAM: status_ignored = _cairo_output_stream_destroy (surface->stream); @@ -1084,7 +1322,7 @@ cairo_ps_surface_create (const char *filename, * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 - */ + **/ cairo_surface_t * cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, @@ -1118,41 +1356,40 @@ _extract_ps_surface (cairo_surface_t *surface, cairo_ps_surface_t **ps_surface) { cairo_surface_t *target; - cairo_status_t status_ignored; if (surface->status) return FALSE; if (surface->finished) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_paginated (surface)) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } target = _cairo_paginated_surface_get_target (surface); if (target->status) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, target->status); + _cairo_surface_set_error (surface, target->status); return FALSE; } if (target->finished) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_ps (target)) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } @@ -1303,15 +1540,25 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, double height_in_points) { cairo_ps_surface_t *ps_surface = NULL; + cairo_status_t status; if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return; ps_surface->width = width_in_points; ps_surface->height = height_in_points; - cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points); + cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); + ps_surface->surface_extents.x = 0; + ps_surface->surface_extents.y = 0; + ps_surface->surface_extents.width = ceil (ps_surface->width); + ps_surface->surface_extents.height = ceil (ps_surface->height); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators, &ps_surface->cairo_to_ps); + status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface, + width_in_points, + height_in_points); + if (status) + status = _cairo_surface_set_error (surface, status); } /** @@ -1324,12 +1571,12 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, * The comment is expected to conform to the PostScript Language * Document Structuring Conventions (DSC). Please see that manual for * details on the available comments and their meanings. In - * particular, the %%IncludeFeature comment allows a + * particular, the \%\%IncludeFeature comment allows a * device-independent means of controlling printer device features. So * the PostScript Printer Description Files Specification will also be * a useful reference. * - * The comment string must begin with a percent character (%) and the + * The comment string must begin with a percent character (\%) and the * total length of the string (including any initial percent * characters) must not exceed 255 characters. Violating either of * these conditions will place @surface into an error state. But @@ -1347,40 +1594,41 @@ cairo_ps_surface_set_size (cairo_surface_t *surface, * * For comments to appear in the header section, this function should * be called after the surface is created, but before a call to - * cairo_ps_surface_begin_setup(). + * cairo_ps_surface_dsc_begin_setup(). * * For comments to appear in the Setup section, this function should - * be called after a call to cairo_ps_surface_begin_setup() but before - * a call to cairo_ps_surface_begin_page_setup(). + * be called after a call to cairo_ps_surface_dsc_begin_setup() but + * before a call to cairo_ps_surface_dsc_begin_page_setup(). * * For comments to appear in the PageSetup section, this function - * should be called after a call to cairo_ps_surface_begin_page_setup(). + * should be called after a call to + * cairo_ps_surface_dsc_begin_page_setup(). * - * Note that it is only necessary to call cairo_ps_surface_begin_page_setup() - * for the first page of any surface. After a call to - * cairo_show_page() or cairo_copy_page() comments are unambiguously - * directed to the PageSetup section of the current page. But it - * doesn't hurt to call this function at the beginning of every page - * as that consistency may make the calling code simpler. + * Note that it is only necessary to call + * cairo_ps_surface_dsc_begin_page_setup() for the first page of any + * surface. After a call to cairo_show_page() or cairo_copy_page() + * comments are unambiguously directed to the PageSetup section of the + * current page. But it doesn't hurt to call this function at the + * beginning of every page as that consistency may make the calling + * code simpler. * * As a final note, cairo automatically generates several comments on * its own. As such, applications must not manually generate any of * the following comments: * - * Header section: %!PS-Adobe-3.0, %%Creator, %%CreationDate, %%Pages, - * %%BoundingBox, %%DocumentData, %%LanguageLevel, %%EndComments. + * Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages, + * \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments. * - * Setup section: %%BeginSetup, %%EndSetup + * Setup section: \%\%BeginSetup, \%\%EndSetup * - * PageSetup section: %%BeginPageSetup, %%PageBoundingBox, - * %%EndPageSetup. + * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup. * - * Other sections: %%BeginProlog, %%EndProlog, %%Page, %%Trailer, %%EOF + * Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF * * Here is an example sequence showing how this function might be used: * * - * #cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height); + * cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height); * ... * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document"); * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover") @@ -1513,10 +1761,20 @@ _cairo_ps_surface_finish (void *abstract_surface) _cairo_ps_surface_emit_header (surface); + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginSetup\n"); + status = _cairo_ps_surface_emit_font_subsets (surface); if (unlikely (status)) goto CLEANUP; + status = _cairo_ps_surface_emit_forms (surface); + if (unlikely (status)) + goto CLEANUP; + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndSetup\n"); + status = _cairo_ps_surface_emit_body (surface); if (unlikely (status)) goto CLEANUP; @@ -1524,6 +1782,10 @@ _cairo_ps_surface_finish (void *abstract_surface) _cairo_ps_surface_emit_footer (surface); CLEANUP: + _cairo_hash_table_foreach (surface->forms, + _cairo_ps_form_pluck, + surface->forms); + _cairo_hash_table_destroy (surface->forms); _cairo_scaled_font_subsets_destroy (surface->font_subsets); status2 = _cairo_output_stream_destroy (surface->stream); @@ -1565,6 +1827,8 @@ CLEANUP: free (comments[i]); _cairo_array_fini (&surface->dsc_page_setup_comments); + _cairo_array_fini (&surface->recording_surf_stack); + _cairo_surface_clipper_reset (&surface->clipper); return status; @@ -1581,35 +1845,22 @@ _cairo_ps_surface_start_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_ps_surface_end_page (cairo_ps_surface_t *surface) -{ - cairo_int_status_t status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (surface->clipper.clip.path != NULL) { - _cairo_output_stream_printf (surface->stream, "Q Q\n"); - _cairo_surface_clipper_reset (&surface->clipper); - } else - _cairo_output_stream_printf (surface->stream, "Q\n"); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_int_status_t _cairo_ps_surface_show_page (void *abstract_surface) { cairo_ps_surface_t *surface = abstract_surface; cairo_int_status_t status; - status = _cairo_ps_surface_end_page (surface); + if (surface->clipper.clip != NULL) + _cairo_surface_clipper_reset (&surface->clipper); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; - _cairo_output_stream_printf (surface->stream, "showpage\n"); + _cairo_output_stream_printf (surface->stream, + "Q Q\n" + "showpage\n"); return CAIRO_STATUS_SUCCESS; } @@ -1623,18 +1874,194 @@ color_is_gray (double red, double green, double blue) fabs (red - blue) < epsilon); } -static cairo_int_status_t -_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern) +/** + * _cairo_ps_surface_acquire_source_surface_from_pattern: + * @surface: [in] the ps surface + * @pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use + * as the source + * @extents: [in] extents of the operation that is using this source + * @src_surface_extents: [out] return source surface extents + * @src_surface_bounded: [out] return TRUE if source surface is bounded + * @src_op_extents: [out] return operation extents in source space + * @source_surface: [out] returns surface of type image surface or recording surface + * @x_offset: [out] return x offset of surface + * @y_offset: [out] return y offset of surface + * + * Acquire source surface or raster source pattern. + **/ +static cairo_status_t +_cairo_ps_surface_acquire_source_surface_from_pattern ( + cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *src_surface_extents, + cairo_bool_t *src_surface_bounded, + cairo_rectangle_int_t *src_op_extents, + cairo_surface_t **source_surface, + double *x_offset, + double *y_offset) { - cairo_image_surface_t *image; - void *image_extra; + cairo_status_t status; + cairo_box_t bbox; + + *x_offset = 0; + *y_offset = 0; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&bbox, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &bbox, NULL); + _cairo_box_round_to_rectangle (&bbox, src_op_extents); + + if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_t *surf; + + surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, src_op_extents); + if (!surf) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents); + cairo_surface_get_device_offset (surf, x_offset, y_offset); + *source_surface = surf; + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_t *surf = NULL; + + *source_surface = ((cairo_surface_pattern_t *) pattern)->surface; + surf = *source_surface; + *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents); + if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (_cairo_surface_is_snapshot (surf)) + surf = _cairo_surface_snapshot_get_target (surf); + + if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf; + + *src_surface_extents = sub->extents; + *src_surface_bounded = TRUE; + *x_offset = -sub->extents.x; + *y_offset = -sub->extents.y; + } + + cairo_surface_destroy (surf); + } else if (surf->type != CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (surf, &image, &image_extra); + if (unlikely (status)) + return status; + + *src_surface_bounded = _cairo_surface_get_extents (&image->base, src_surface_extents); + _cairo_surface_release_source_image (surf, image, image_extra); + } + } else { + ASSERT_NOT_REACHED; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_surface_t *source_surface) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + _cairo_raster_source_pattern_release (pattern, source_surface); +} + +/** + * _cairo_ps_surface_create_padded_image_from_image: + * @surface: the ps surface + * @source: The source image + * @extents: extents of the operation that is using this source + * @image: returns the padded image or NULL if padding not required to fill @extents + * @image_extents: returns extents of padded image. These extents in are in source image space. + * + * Creates a padded image if the source image does not fill the extents. + **/ +static cairo_status_t +_cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t *surface, + cairo_image_surface_t *source, + const cairo_matrix_t *source_matrix, + const cairo_rectangle_int_t *extents, + cairo_image_surface_t **image, + cairo_rectangle_int_t *image_extents) +{ + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_surface_t *pad_image; + cairo_surface_pattern_t pad_pattern; + int w, h; cairo_int_status_t status; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (source_matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + + /* Check if image needs padding to fill extents. */ + w = source->width; + h = source->height; + if (_cairo_fixed_integer_ceil(box.p1.x) < 0 || + _cairo_fixed_integer_ceil(box.p1.y) < 0 || + _cairo_fixed_integer_floor(box.p2.y) > w || + _cairo_fixed_integer_floor(box.p2.y) > h) + { + pad_image = _cairo_image_surface_create_with_content (source->base.content, + rect.width, + rect.height); + if (pad_image->status) + return pad_image->status; + + _cairo_pattern_init_for_surface (&pad_pattern, &source->base); + cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y); + pad_pattern.base.extend = CAIRO_EXTEND_PAD; + status = _cairo_surface_paint (pad_image, + CAIRO_OPERATOR_SOURCE, + &pad_pattern.base, + NULL); + _cairo_pattern_fini (&pad_pattern.base); + *image = (cairo_image_surface_t *) pad_image; + image_extents->x = rect.x; + image_extents->y = rect.y; + image_extents->width = rect.width; + image_extents->height = rect.height; + } else { + *image = NULL; + status = CAIRO_STATUS_SUCCESS; + } + + return status; +} + +static cairo_int_status_t +_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_rectangle_int_t src_op_extents; + cairo_surface_t *source_surface; + double x_offset, y_offset; + cairo_image_surface_t *image; + void *image_extra; + cairo_int_status_t status; cairo_image_transparency_t transparency; - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &image_extra); + status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, + pattern, + extents, + &src_surface_extents, + &src_surface_bounded, + &src_op_extents, + &source_surface, + &x_offset, + &y_offset); + if (unlikely (status)) + return status; + + status = _cairo_surface_acquire_source_image (source_surface, &image, &image_extra); if (unlikely (status)) return status; @@ -1664,7 +2091,8 @@ _cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t ASSERT_NOT_REACHED; } - _cairo_surface_release_source_image (pattern->surface, image, image_extra); + _cairo_surface_release_source_image (source_surface, image, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); return status; } @@ -1694,50 +2122,16 @@ static cairo_bool_t _gradient_pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) { - const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *) pattern; - uint16_t alpha; - cairo_extend_t extend; - unsigned int i; + double min_alpha, max_alpha; if (surface->ps_level == CAIRO_PS_LEVEL_2) return FALSE; - if (gradient->n_stops == 0) - return TRUE; - /* Alpha gradients are only supported (by flattening the alpha) * if there is no variation in the alpha across the gradient. */ - alpha = gradient->stops[0].color.alpha_short; - for (i = 0; i < gradient->n_stops; i++) { - if (gradient->stops[i].color.alpha_short != alpha) - return FALSE; - } - - extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern); - - /* Radial gradients are currently only supported when one circle - * is inside the other. */ - if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { - double x1, y1, x2, y2, r1, r2, d; - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - - if (extend == CAIRO_EXTEND_REPEAT || - extend == CAIRO_EXTEND_REFLECT) { - return FALSE; - } - - x1 = _cairo_fixed_to_double (radial->c1.x); - y1 = _cairo_fixed_to_double (radial->c1.y); - r1 = _cairo_fixed_to_double (radial->r1); - x2 = _cairo_fixed_to_double (radial->c2.x); - y2 = _cairo_fixed_to_double (radial->c2.y); - r2 = _cairo_fixed_to_double (radial->r2); - - d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); - if (d > fabs(r2 - r1)) { - return FALSE; - } - } + _cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha); + if (min_alpha != max_alpha) + return FALSE; surface->ps_level_used = CAIRO_PS_LEVEL_3; @@ -1747,16 +2141,46 @@ _gradient_pattern_supported (cairo_ps_surface_t *surface, static cairo_bool_t pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) { - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: return TRUE; - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || - pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: return _gradient_pattern_supported (surface, pattern); - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + case CAIRO_PATTERN_TYPE_SURFACE: return surface_pattern_supported ((cairo_surface_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return TRUE; + + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +static cairo_bool_t +mask_supported (cairo_ps_surface_t *surface, + const cairo_pattern_t *mask, + const cairo_rectangle_int_t *extents) +{ + if (surface->ps_level == CAIRO_PS_LEVEL_2) + return FALSE; + + 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_IMAGE) { + /* check if mask if opaque or bilevel alpha */ + if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, mask, extents) == CAIRO_INT_STATUS_SUCCESS) { + surface->ps_level_used = CAIRO_PS_LEVEL_3; + return TRUE; + } + } + } + return FALSE; } @@ -1764,8 +2188,11 @@ static cairo_int_status_t _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, + const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { + double min_alpha; + if (surface->force_fallbacks && surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { @@ -1773,26 +2200,50 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, } if (! pattern_supported (surface, pattern)) - { return CAIRO_INT_STATUS_UNSUPPORTED; - } if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) return CAIRO_INT_STATUS_UNSUPPORTED; + /* Mask is only supported when the mask is an image with opaque or bilevel alpha. */ + if (mask && !mask_supported (surface, mask, extents)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->extend == CAIRO_EXTEND_PAD) - return CAIRO_INT_STATUS_UNSUPPORTED; - else - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + if (pattern->extend == CAIRO_EXTEND_PAD) { + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_rectangle_int_t rec_extents; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + + /* Check if surface needs padding to fill extents */ + 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.y) > rec_extents.y + rec_extents.height) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + } + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } } - if (op == CAIRO_OPERATOR_SOURCE) - return CAIRO_STATUS_SUCCESS; + if (op == CAIRO_OPERATOR_SOURCE) { + if (mask) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return CAIRO_STATUS_SUCCESS; + } /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If * the pattern contains transparency, we return @@ -1803,14 +2254,14 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface, * render stage and we blend the transparency into the white * background to convert the pattern to opaque. */ - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE || pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, pattern, extents); - return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, - surface_pattern); - } - - if (_cairo_pattern_is_opaque (pattern, extents)) + /* Patterns whose drawn part is opaque are directly supported; + those whose drawn part is partially transparent can be + supported by flattening the alpha. */ + _cairo_pattern_alpha_range (pattern, &min_alpha, NULL); + if (CAIRO_ALPHA_IS_OPAQUE (min_alpha)) return CAIRO_STATUS_SUCCESS; return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; @@ -1820,21 +2271,17 @@ static cairo_bool_t _cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *pattern, + const cairo_pattern_t *mask, const cairo_rectangle_int_t *extents) { - return _cairo_ps_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; + return _cairo_ps_surface_analyze_operation (surface, op, pattern, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED; } /* The "standard" implementation limit for PostScript string sizes is * 65535 characters (see PostScript Language Reference, Appendix - * B). We go one short of that because we sometimes need two - * characters in a string to represent a single ASCII85 byte, (for the - * escape sequences "\\", "\(", and "\)") and we must not split these - * across two strings. So we'd be in trouble if we went right to the - * limit and one of these escape sequences just happened to land at - * the end. + * B). */ -#define STRING_ARRAY_MAX_STRING_SIZE (65535-1) +#define STRING_ARRAY_MAX_STRING_SIZE 65535 #define STRING_ARRAY_MAX_COLUMN 72 typedef struct _string_array_stream { @@ -1842,64 +2289,62 @@ typedef struct _string_array_stream { cairo_output_stream_t *output; int column; int string_size; + int tuple_count; cairo_bool_t use_strings; } string_array_stream_t; static cairo_status_t -_string_array_stream_write (cairo_output_stream_t *base, - const unsigned char *data, - unsigned int length) +_base85_string_wrap_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) { string_array_stream_t *stream = (string_array_stream_t *) base; unsigned char c; - const unsigned char backslash = '\\'; if (length == 0) return CAIRO_STATUS_SUCCESS; while (length--) { - if (stream->string_size == 0 && stream->use_strings) { - _cairo_output_stream_printf (stream->output, "("); - stream->column++; + if (stream->column == 0) { + if (stream->use_strings) { + _cairo_output_stream_printf (stream->output, "<~"); + stream->column = 2; + } else { + _cairo_output_stream_printf (stream->output, " "); + stream->column = 1; + } } c = *data++; - if (stream->use_strings) { - switch (c) { - case '\\': - case '(': - case ')': - _cairo_output_stream_write (stream->output, &backslash, 1); - stream->column++; - stream->string_size++; - break; - } - } - /* Have to be careful to never split the final ~> sequence. */ - if (c == '~') { - _cairo_output_stream_write (stream->output, &c, 1); - stream->column++; - stream->string_size++; - - if (length-- == 0) - break; - - c = *data++; - } _cairo_output_stream_write (stream->output, &c, 1); stream->column++; - stream->string_size++; + /* Base85 encodes each 4 byte tuple with a 5 ASCII character + * tuple, except for 'z' with represents 4 zero bytes. We need + * to keep track of the string length after decoding. + */ + if (c == 'z') { + stream->string_size += 4; + stream->tuple_count = 0; + } else { + if (++stream->tuple_count == 5) { + stream->string_size += 4; + stream->tuple_count = 0; + } + } + + /* Split string at tuple boundary when there is not enough + * space for another tuple */ if (stream->use_strings && - stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE) + stream->tuple_count == 0 && + stream->string_size > STRING_ARRAY_MAX_STRING_SIZE - 4) { - _cairo_output_stream_printf (stream->output, ")\n"); + _cairo_output_stream_printf (stream->output, "~>\n"); stream->string_size = 0; stream->column = 0; } if (stream->column >= STRING_ARRAY_MAX_COLUMN) { _cairo_output_stream_printf (stream->output, "\n "); - stream->string_size += 2; stream->column = 1; } } @@ -1908,78 +2353,75 @@ _string_array_stream_write (cairo_output_stream_t *base, } static cairo_status_t -_string_array_stream_close (cairo_output_stream_t *base) +_base85_string_wrap_stream_close (cairo_output_stream_t *base) { - cairo_status_t status; string_array_stream_t *stream = (string_array_stream_t *) base; - if (stream->use_strings) - _cairo_output_stream_printf (stream->output, ")\n"); + if (!stream->use_strings || stream->string_size != 0) + _cairo_output_stream_printf (stream->output, "~>"); - status = _cairo_output_stream_get_status (stream->output); - - return status; + return _cairo_output_stream_get_status (stream->output); } -/* A string_array_stream wraps an existing output stream. It takes the - * data provided to it and output one or more consecutive string - * objects, each within the standard PostScript implementation limit - * of 65k characters. - * - * The strings are each separated by a space character for easy - * inclusion within an array object, (but the array delimiters are not - * added by the string_array_stream). - * +/* A _base85_strings_stream wraps an existing output stream. It takes + * base85 encoded data and splits it into strings each limited to + * STRING_ARRAY_MAX_STRING_SIZE bytes when decoded. Each string is + * enclosed in "<~" and "~>". + * The string array stream is also careful to wrap the output within - * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds - * necessary escaping for special characters within a string, - * (specifically '\', '(', and ')'). + * STRING_ARRAY_MAX_COLUMN columns. Wrapped lines start with a space + * in case an encoded line starts with %% which could be interpreted + * as a DSC comment. */ static cairo_output_stream_t * -_string_array_stream_create (cairo_output_stream_t *output) +_base85_strings_stream_create (cairo_output_stream_t *output) { string_array_stream_t *stream; - stream = malloc (sizeof (string_array_stream_t)); + stream = _cairo_malloc (sizeof (string_array_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, - _string_array_stream_write, + _base85_string_wrap_stream_write, NULL, - _string_array_stream_close); + _base85_string_wrap_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; + stream->tuple_count = 0; stream->use_strings = TRUE; return &stream->base; } -/* A base85_array_stream wraps an existing output stream. It wraps the - * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output - * is not enclosed in strings like string_array_stream. +/* A base85_wrap_stream wraps an existing output stream. It wraps the + * output within STRING_ARRAY_MAX_COLUMN columns. A base85 EOD "~>" is + * appended to the end. Wrapped lines start with a space in case an + * encoded line starts with %% which could be interpreted as a DSC + * comment. */ static cairo_output_stream_t * -_base85_array_stream_create (cairo_output_stream_t *output) +_base85_wrap_stream_create (cairo_output_stream_t *output) { string_array_stream_t *stream; - stream = malloc (sizeof (string_array_stream_t)); + stream = _cairo_malloc (sizeof (string_array_stream_t)); if (unlikely (stream == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, - _string_array_stream_write, + _base85_string_wrap_stream_write, NULL, - _string_array_stream_close); + _base85_string_wrap_stream_close); stream->output = output; stream->column = 0; stream->string_size = 0; + stream->tuple_count = 0; stream->use_strings = FALSE; return &stream->base; @@ -2032,15 +2474,18 @@ static cairo_status_t _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, const unsigned char *data, unsigned long length, + cairo_ps_compress_t compress, cairo_bool_t use_strings) { - cairo_output_stream_t *base85_stream, *string_array_stream; + cairo_output_stream_t *base85_stream, *string_array_stream, *deflate_stream; + unsigned char *data_compressed; + unsigned long data_compressed_size; cairo_status_t status, status2; if (use_strings) - string_array_stream = _string_array_stream_create (surface->stream); + string_array_stream = _base85_strings_stream_create (surface->stream); else - string_array_stream = _base85_array_stream_create (surface->stream); + string_array_stream = _base85_wrap_stream_create (surface->stream); status = _cairo_output_stream_get_status (string_array_stream); if (unlikely (status)) @@ -2053,287 +2498,466 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface, return _cairo_output_stream_destroy (base85_stream); } - _cairo_output_stream_write (base85_stream, data, length); + switch (compress) { + case CAIRO_PS_COMPRESS_NONE: + _cairo_output_stream_write (base85_stream, data, length); + break; + case CAIRO_PS_COMPRESS_LZW: + /* XXX: Should fix cairo-lzw to provide a stream-based interface + * instead. */ + data_compressed_size = length; + data_compressed = _cairo_lzw_compress ((unsigned char*)data, &data_compressed_size); + if (unlikely (data_compressed == NULL)) { + status = _cairo_output_stream_destroy (string_array_stream); + status = _cairo_output_stream_destroy (base85_stream); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + _cairo_output_stream_write (base85_stream, data_compressed, data_compressed_size); + free (data_compressed); + break; + + case CAIRO_PS_COMPRESS_DEFLATE: + deflate_stream = _cairo_deflate_stream_create (base85_stream); + if (_cairo_output_stream_get_status (deflate_stream)) { + return _cairo_output_stream_destroy (deflate_stream); + } + _cairo_output_stream_write (deflate_stream, data, length); + status = _cairo_output_stream_destroy (deflate_stream); + if (unlikely (status)) { + status2 = _cairo_output_stream_destroy (string_array_stream); + status2 = _cairo_output_stream_destroy (base85_stream); + return _cairo_output_stream_destroy (deflate_stream); + } + break; + } status = _cairo_output_stream_destroy (base85_stream); - - /* Mark end of base85 data */ - _cairo_output_stream_printf (string_array_stream, "~>"); status2 = _cairo_output_stream_destroy (string_array_stream); if (status == CAIRO_STATUS_SUCCESS) status = status2; - return status; } -static cairo_status_t -_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, - cairo_image_surface_t *image, - cairo_operator_t op, - cairo_filter_t filter) +static const char * +get_interpolate (cairo_filter_t filter) { - cairo_status_t status; - unsigned char *data, *data_compressed; - unsigned long data_size, data_compressed_size; - cairo_image_surface_t *opaque_image = NULL; - int x, y, i; - cairo_image_transparency_t transparency; - cairo_bool_t use_mask; - uint32_t *pixel; - int bit; const char *interpolate; - if (image->base.status) - return image->base.status; - switch (filter) { - default: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - interpolate = "true"; - break; - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_GAUSSIAN: - interpolate = "false"; + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; break; } - transparency = _cairo_image_analyze_transparency (image); + return interpolate; +} - /* PostScript can not represent the alpha channel, so we blend the - current image over a white (or black for CONTENT_COLOR - surfaces) RGB surface to eliminate it. */ +static cairo_status_t +_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) +{ + cairo_status_t status; + unsigned char *data; + unsigned long data_size; + cairo_image_surface_t *ps_image; + int x, y, i, a; + cairo_image_transparency_t transparency; + cairo_bool_t use_mask; + uint32_t *pixel32; + uint8_t *pixel8; + int bit; + cairo_image_color_t color; + const char *interpolate; + cairo_ps_compress_t compress; + const char *compress_filter; + cairo_image_surface_t *image_surf; + cairo_image_surface_t *image; + void *image_extra; - if (op == CAIRO_OPERATOR_SOURCE || - transparency == CAIRO_IMAGE_HAS_ALPHA || - (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA && - surface->ps_level == CAIRO_PS_LEVEL_2)) + if (params->src_surface->status) + return params->src_surface->status; + + status = _cairo_surface_acquire_source_image (params->src_surface, &image_surf, &image_extra); + if (unlikely (status)) + return status; + + image = image_surf; + if (image->format != CAIRO_FORMAT_RGB24 && + image->format != CAIRO_FORMAT_ARGB32 && + image->format != CAIRO_FORMAT_A8 && + image->format != CAIRO_FORMAT_A1) { - status = _cairo_ps_surface_flatten_image_transparency (surface, - image, - &opaque_image); - if (unlikely (status)) - return status; + cairo_surface_t *surf; + cairo_surface_pattern_t pattern; + surf = _cairo_image_surface_create_with_content (image->base.content, + image->width, + image->height); + image = (cairo_image_surface_t *) surf; + if (surf->status) { + status = surf->status; + goto bail0; + } + + _cairo_pattern_init_for_surface (&pattern, &image->base); + status = _cairo_surface_paint (surf, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto bail0; + } + ps_image = image; + interpolate = get_interpolate (params->filter); + + if (params->stencil_mask) { use_mask = FALSE; - } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) { - opaque_image = image; - use_mask = FALSE; + color = CAIRO_IMAGE_IS_MONOCHROME; + transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; } else { - use_mask = TRUE; + transparency = _cairo_image_analyze_transparency (image); + + /* PostScript can not represent the alpha channel, so we blend the + current image over a white (or black for CONTENT_COLOR + surfaces) RGB surface to eliminate it. */ + + if (params->op == CAIRO_OPERATOR_SOURCE || + transparency == CAIRO_IMAGE_HAS_ALPHA || + (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA && + surface->ps_level == CAIRO_PS_LEVEL_2)) + { + status = _cairo_ps_surface_flatten_image_transparency (surface, + image, + &ps_image); + if (unlikely (status)) + return status; + + use_mask = FALSE; + } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) { + use_mask = FALSE; + } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */ + use_mask = TRUE; + } + + color = _cairo_image_analyze_color (ps_image); } - if (use_mask) { - /* Type 2 (mask and image interleaved) has the mask and image - * samples interleaved by row. The mask row is first, one bit - * per pixel with (bit 7 first). The row is padded to byte - * boundaries. The image data is 3 bytes per pixel RGB - * format. */ - data_size = image->height * ((image->width + 7)/8 + 3*image->width); - } else { - data_size = image->height * image->width * 3; + /* Type 2 (mask and image interleaved) has the mask and image + * samples interleaved by row. The mask row is first, one bit per + * pixel with (bit 7 first). The row is padded to byte + * boundaries. The image data is 3 bytes per pixel RGB format. */ + switch (color) { + default: + case CAIRO_IMAGE_UNKNOWN_COLOR: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_COLOR: + data_size = ps_image->width * 3; + break; + case CAIRO_IMAGE_IS_GRAYSCALE: + data_size = ps_image->width; + break; + case CAIRO_IMAGE_IS_MONOCHROME: + data_size = (ps_image->width + 7)/8; + break; } - data = malloc (data_size); + if (use_mask) + data_size += (ps_image->width + 7)/8; + data_size *= ps_image->height; + data = _cairo_malloc (data_size); if (unlikely (data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail1; } - if (use_mask) { - i = 0; - for (y = 0; y < image->height; y++) { + i = 0; + for (y = 0; y < ps_image->height; y++) { + if (params->stencil_mask || use_mask) { /* mask row */ - pixel = (uint32_t *) (image->data + y * image->stride); - bit = 7; - for (x = 0; x < image->width; x++, pixel++) { - if (bit == 7) - data[i] = 0; - if (((*pixel & 0xff000000) >> 24) > 0x80) - data[i] |= (1 << bit); - bit--; - if (bit < 0) { - bit = 7; - i++; + if (ps_image->format == CAIRO_FORMAT_A1) { + pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride); + + for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) { + a = *pixel8; + a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); + data[i++] = a; } - } - if (bit != 7) - i++; + } else { + pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride); + pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride); + bit = 7; + for (x = 0; x < ps_image->width; x++) { + if (ps_image->format == CAIRO_FORMAT_ARGB32) { + a = (*pixel32 & 0xff000000) >> 24; + pixel32++; + } else { + a = *pixel8; + pixel8++; + } - /* image row*/ - pixel = (uint32_t *) (image->data + y * image->stride); - for (x = 0; x < image->width; x++, pixel++) { - data[i++] = (*pixel & 0x00ff0000) >> 16; - data[i++] = (*pixel & 0x0000ff00) >> 8; - data[i++] = (*pixel & 0x000000ff) >> 0; + if (transparency == CAIRO_IMAGE_HAS_ALPHA) { + data[i++] = a; + } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */ + if (bit == 7) + data[i] = 0; + if (a != 0) + data[i] |= (1 << bit); + bit--; + if (bit < 0) { + bit = 7; + i++; + } + } + } + if (bit != 7) + i++; } } + if (params->stencil_mask) + continue; + + /* image row*/ + pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride); + bit = 7; + for (x = 0; x < ps_image->width; x++, pixel32++) { + int r, g, b; + + if (ps_image->format == CAIRO_FORMAT_ARGB32) { + /* At this point ARGB32 images are either opaque or + * bilevel alpha so we don't need to unpremultiply. */ + if (((*pixel32 & 0xff000000) >> 24) == 0) { + r = g = b = 0; + } else { + r = (*pixel32 & 0x00ff0000) >> 16; + g = (*pixel32 & 0x0000ff00) >> 8; + b = (*pixel32 & 0x000000ff) >> 0; + } + } else if (ps_image->format == CAIRO_FORMAT_RGB24) { + r = (*pixel32 & 0x00ff0000) >> 16; + g = (*pixel32 & 0x0000ff00) >> 8; + b = (*pixel32 & 0x000000ff) >> 0; + } else { + r = g = b = 0; + } + + switch (color) { + case CAIRO_IMAGE_IS_COLOR: + case CAIRO_IMAGE_UNKNOWN_COLOR: + data[i++] = r; + data[i++] = g; + data[i++] = b; + break; + + case CAIRO_IMAGE_IS_GRAYSCALE: + data[i++] = r; + break; + + case CAIRO_IMAGE_IS_MONOCHROME: + if (bit == 7) + data[i] = 0; + if (r != 0) + data[i] |= (1 << bit); + bit--; + if (bit < 0) { + bit = 7; + i++; + } + break; + } + } + if (bit != 7) + i++; + } + + if (surface->ps_level == CAIRO_PS_LEVEL_2) { + compress = CAIRO_PS_COMPRESS_LZW; + compress_filter = "LZWDecode"; } else { - i = 0; - for (y = 0; y < opaque_image->height; y++) { - pixel = (uint32_t *) (opaque_image->data + y * opaque_image->stride); - for (x = 0; x < opaque_image->width; x++, pixel++) { - data[i++] = (*pixel & 0x00ff0000) >> 16; - data[i++] = (*pixel & 0x0000ff00) >> 8; - data[i++] = (*pixel & 0x000000ff) >> 0; - } - } + compress = CAIRO_PS_COMPRESS_DEFLATE; + compress_filter = "FlateDecode"; + surface->ps_level_used = CAIRO_PS_LEVEL_3; } - /* XXX: Should fix cairo-lzw to provide a stream-based interface - * instead. */ - data_compressed_size = data_size; - data_compressed = _cairo_lzw_compress (data, &data_compressed_size); - if (unlikely (data_compressed == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto bail2; - } - - if (surface->use_string_datasource) { + if (surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator later. */ _cairo_output_stream_printf (surface->stream, - "/CairoImageData [\n"); + "/CairoData [\n"); status = _cairo_ps_surface_emit_base85_string (surface, - data_compressed, - data_compressed_size, + data, + data_size, + compress, TRUE); if (unlikely (status)) - goto bail3; + goto bail2; _cairo_output_stream_printf (surface->stream, "] def\n"); _cairo_output_stream_printf (surface->stream, - "/CairoImageDataIndex 0 def\n"); + "/CairoDataIndex 0 def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); } if (use_mask) { _cairo_output_stream_printf (surface->stream, - "/DeviceRGB setcolorspace\n" - "5 dict dup begin\n" - " /ImageType 3 def\n" - " /InterleaveType 2 def\n" - " /DataDict 8 dict def\n" - " DataDict begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent 8 def\n" - " /Decode [ 0 1 0 1 0 1 ] def\n", - image->width, - image->height, - interpolate); + "%s setcolorspace\n" + "<<\n" + " /ImageType 3\n" + " /InterleaveType 2\n" + " /DataDict <<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + " /Decode [ %s ]\n", + color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", + ps_image->width, + ps_image->height, + interpolate, + color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8, + color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1"); - if (surface->use_string_datasource) { + if (surface->paint_proc) { _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /LZWDecode filter def\n"); + " /DataSource { cairo_data_source } /%s filter\n", + compress_filter); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n"); + " /DataSource cairo_ascii85_file /%s filter\n", + compress_filter); } _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - " end\n" - " /MaskDict 8 dict def\n" - " MaskDict begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /Interpolate %s def\n" - " /BitsPerComponent 1 def\n" - " /Decode [ 1 0 ] def\n" - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - " end\n" - "end\n" + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + " >>\n" + " /MaskDict <<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent 1\n" + " /Decode [ 1 0 ]\n" + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + " >>\n" + ">>\n" "image\n", - image->height, - image->width, - image->height, + ps_image->width, + -ps_image->height, + ps_image->height, + ps_image->width, + ps_image->height, interpolate, - image->height); + ps_image->width, + -ps_image->height, + ps_image->height); } else { - _cairo_output_stream_printf (surface->stream, - "/DeviceRGB setcolorspace\n" - "8 dict dup begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /BitsPerComponent 8 def\n" - " /Decode [ 0 1 0 1 0 1 ] def\n", - opaque_image->width, - opaque_image->height); - if (surface->use_string_datasource) { + const char *decode; + + if (!params->stencil_mask) { _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /LZWDecode filter def\n"); + "%s setcolorspace\n", + color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray"); + } + if (params->stencil_mask) + decode = "1 0"; + else if (color == CAIRO_IMAGE_IS_COLOR) + decode = "0 1 0 1 0 1"; + else + decode ="0 1"; + + _cairo_output_stream_printf (surface->stream, + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /Interpolate %s\n" + " /BitsPerComponent %d\n" + " /Decode [ %s ]\n", + ps_image->width, + ps_image->height, + interpolate, + color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8, + decode); + if (surface->paint_proc) { + _cairo_output_stream_printf (surface->stream, + " /DataSource { cairo_data_source } /%s filter\n", + compress_filter); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n"); + " /DataSource cairo_ascii85_file /%s filter\n", + compress_filter); } _cairo_output_stream_printf (surface->stream, - " /Interpolate %s def\n" - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - "end\n" - "image\n", - interpolate, - opaque_image->height); + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + ">>\n" + "%s%s\n", + ps_image->width, + -ps_image->height, + ps_image->height, + surface->paint_proc ? "" : "cairo_", + params->stencil_mask ? "imagemask" : "image"); } - if (!surface->use_string_datasource) { + if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator. */ status = _cairo_ps_surface_emit_base85_string (surface, - data_compressed, - data_compressed_size, + data, + data_size, + compress, FALSE); _cairo_output_stream_printf (surface->stream, "\n"); } else { status = CAIRO_STATUS_SUCCESS; } -bail3: - free (data_compressed); - bail2: free (data); bail1: - if (!use_mask && opaque_image != image) - cairo_surface_destroy (&opaque_image->base); + if (!use_mask && ps_image != image) + cairo_surface_destroy (&ps_image->base); + +bail0: + if (image != image_surf) + cairo_surface_destroy (&image->base); + + _cairo_surface_release_source_image (params->src_surface, image_surf, image_extra); return status; } -static cairo_status_t -_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, - cairo_surface_t *source, - int width, - int height) +static cairo_int_status_t +_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) { cairo_status_t status; const unsigned char *mime_data; unsigned long mime_data_length; cairo_image_info_t info; + const char *colorspace; + const char *decode; - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + if (unlikely (params->src_surface->status)) + return params->src_surface->status; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); - if (unlikely (source->status)) - return source->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -2341,18 +2965,40 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, if (unlikely (status)) return status; - if (info.num_components != 1 && info.num_components != 3) - return CAIRO_INT_STATUS_UNSUPPORTED; + switch (info.num_components) { + case 1: + colorspace = "/DeviceGray"; + decode = "0 1"; + break; + case 3: + colorspace = "/DeviceRGB"; + decode = "0 1 0 1 0 1"; + break; + case 4: + colorspace = "/DeviceCMYK"; + decode = "0 1 0 1 0 1 0 1"; + break; + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } - if (surface->use_string_datasource) { + /* At this point we know emitting jpeg will succeed. */ + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + params->is_image = TRUE; + params->approx_size = mime_data_length; + return CAIRO_STATUS_SUCCESS; + } + + if (surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator later. */ _cairo_output_stream_printf (surface->stream, - "/CairoImageData [\n"); + "/CairoData [\n"); status = _cairo_ps_surface_emit_base85_string (surface, mime_data, mime_data_length, + CAIRO_PS_COMPRESS_NONE, TRUE); if (unlikely (status)) return status; @@ -2360,165 +3006,381 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, "] def\n"); _cairo_output_stream_printf (surface->stream, - "/CairoImageDataIndex 0 def\n"); - } - - _cairo_output_stream_printf (surface->stream, - "/%s setcolorspace\n" - "8 dict dup begin\n" - " /ImageType 1 def\n" - " /Width %d def\n" - " /Height %d def\n" - " /BitsPerComponent %d def\n" - " /Decode [ 0 1 0 1 0 1 ] def\n", - info.num_components == 1 ? "DeviceGray" : "DeviceRGB", - info.width, - info.height, - info.bits_per_component); - - if (surface->use_string_datasource) { - _cairo_output_stream_printf (surface->stream, - " /DataSource {\n" - " CairoImageData CairoImageDataIndex get\n" - " /CairoImageDataIndex CairoImageDataIndex 1 add def\n" - " CairoImageDataIndex CairoImageData length 1 sub gt\n" - " { /CairoImageDataIndex 0 def } if\n" - " } /ASCII85Decode filter /DCTDecode filter def\n"); + "/CairoDataIndex 0 def\n"); } else { _cairo_output_stream_printf (surface->stream, - " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n"); + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); } _cairo_output_stream_printf (surface->stream, - " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n" - "end\n" - "image\n", - info.height); + "%s setcolorspace\n" + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent %d\n" + " /Interpolate %s\n" + " /Decode [ %s ]\n", + colorspace, + info.width, + info.height, + info.bits_per_component, + get_interpolate (params->filter), + decode); - if (!surface->use_string_datasource) { + if (surface->paint_proc) { + _cairo_output_stream_printf (surface->stream, + " /DataSource { cairo_data_source } /DCTDecode filter\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource cairo_ascii85_file /DCTDecode filter\n"); + } + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + ">>\n" + "%simage\n", + info.width, + -info.height, + info.height, + surface->paint_proc ? "" : "cairo_"); + + if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will * be used as the data source for the image operator. */ status = _cairo_ps_surface_emit_base85_string (surface, mime_data, mime_data_length, + CAIRO_PS_COMPRESS_NONE, FALSE); } return status; } -static cairo_status_t -_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, - cairo_surface_t *recording_surface) +static cairo_int_status_t +_cairo_ps_surface_emit_ccitt_image (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) { - double old_width, old_height; - cairo_matrix_t old_cairo_to_ps; - cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; - cairo_box_t bbox; cairo_status_t status; + const unsigned char *ccitt_data; + unsigned long ccitt_data_len; + const unsigned char *ccitt_params_data; + unsigned long ccitt_params_data_len; + char *ccitt_params_string; + cairo_ccitt_params_t ccitt_params; - old_content = surface->content; - old_width = surface->width; - old_height = surface->height; - old_page_bbox = surface->page_bbox; - old_cairo_to_ps = surface->cairo_to_ps; + if (unlikely (params->src_surface->status)) + return params->src_surface->status; - status = - _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, - &bbox, - NULL); - if (unlikely (status)) + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_CCITT_FAX, + &ccitt_data, &ccitt_data_len); + if (ccitt_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, + &ccitt_params_data, &ccitt_params_data_len); + if (ccitt_params_data == NULL) + 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; + status = _cairo_tag_parse_ccitt_params (ccitt_params_string, &ccitt_params); + if (unlikely(status)) return status; -#if DEBUG_PS - _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n", - _cairo_fixed_to_double (bbox.p1.x), - _cairo_fixed_to_double (bbox.p1.y), - _cairo_fixed_to_double (bbox.p2.x), - _cairo_fixed_to_double (bbox.p2.y)); -#endif + free (ccitt_params_string); - surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); - surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); - _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox); + if (ccitt_params.columns <= 0 || ccitt_params.rows <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); - _cairo_output_stream_printf (surface->stream, " q\n"); - - if (recording_surface->content == CAIRO_CONTENT_COLOR) { - surface->content = CAIRO_CONTENT_COLOR; - _cairo_output_stream_printf (surface->stream, - " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + /* At this point we know emitting ccitt will succeed. */ + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + params->is_image = TRUE; + params->approx_size = ccitt_data_len; + return CAIRO_STATUS_SUCCESS; } - status = _cairo_recording_surface_replay_region (recording_surface, - NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) + if (surface->paint_proc) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator later. */ + _cairo_output_stream_printf (surface->stream, + "/CairoData [\n"); + + status = _cairo_ps_surface_emit_base85_string (surface, + ccitt_data, + ccitt_data_len, + CAIRO_PS_COMPRESS_NONE, + TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "] def\n"); + _cairo_output_stream_printf (surface->stream, + "/CairoDataIndex 0 def\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "/cairo_ascii85_file currentfile /ASCII85Decode filter def\n"); + } + + if (!params->stencil_mask) { + _cairo_output_stream_printf (surface->stream, + "/DeviceGray setcolorspace\n"); + } + + _cairo_output_stream_printf (surface->stream, + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 1\n" + " /Interpolate %s\n" + " /Decode [ 0 1 ]\n", + ccitt_params.columns, + ccitt_params.rows, + get_interpolate (params->filter)); + + if (surface->paint_proc) { + _cairo_output_stream_printf (surface->stream, + " /DataSource { cairo_data_source }\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /DataSource cairo_ascii85_file\n"); + } + + _cairo_output_stream_printf (surface->stream, + " << /Columns %d /Rows %d /K %d\n", + ccitt_params.columns, + ccitt_params.rows, + ccitt_params.k); + + if (ccitt_params.end_of_line) + _cairo_output_stream_printf (surface->stream, " /EndOfLine true\n"); + + if (ccitt_params.encoded_byte_align) + _cairo_output_stream_printf (surface->stream, " /EncodedByteAlign true\n"); + + if (!ccitt_params.end_of_block) + _cairo_output_stream_printf (surface->stream, " /EndOfBlock false\n"); + + if (ccitt_params.black_is_1) + _cairo_output_stream_printf (surface->stream, " /BlackIs1 true\n"); + + if (ccitt_params.damaged_rows_before_error > 0) { + _cairo_output_stream_printf (surface->stream, + " /DamagedRowsBeforeError %d\n", + ccitt_params.damaged_rows_before_error); + } + + _cairo_output_stream_printf (surface->stream, + " >> /CCITTFaxDecode filter\n"); + + _cairo_output_stream_printf (surface->stream, + " /ImageMatrix [ %d 0 0 %d 0 %d ]\n" + ">>\n" + "%s%s\n", + ccitt_params.columns, + -ccitt_params.rows, + ccitt_params.rows, + surface->paint_proc ? "" : "cairo_", + params->stencil_mask ? "imagemask" : "image"); + + if (!surface->paint_proc) { + /* Emit the image data as a base85-encoded string which will + * be used as the data source for the image operator. */ + status = _cairo_ps_surface_emit_base85_string (surface, + ccitt_data, + ccitt_data_len, + CAIRO_PS_COMPRESS_NONE, + FALSE); + } + + return status; +} + +/* The '|' character is not used in PS (including ASCII85). We can + * speed up the search by first searching for the first char before + * comparing strings. + */ +#define SUBFILE_FILTER_EOD "|EOD|" + +/* Count number of non overlapping occurrences of SUBFILE_FILTER_EOD in data. */ +static int +count_eod_strings (const unsigned char *data, unsigned long data_len) +{ + const unsigned char *p = data; + const unsigned char *end; + int first_char, len, count; + const char *eod_str = SUBFILE_FILTER_EOD; + + first_char = eod_str[0]; + len = strlen (eod_str); + p = data; + end = data + data_len - len + 1; + count = 0; + while (p < end) { + p = memchr (p, first_char, end - p); + if (!p) + break; + + if (memcmp (p, eod_str, len) == 0) { + count++; + p += len; + } + } + + return count; +} + +static cairo_status_t +_cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) +{ + cairo_status_t status; + const unsigned char *eps_data = NULL; + unsigned long eps_data_len; + const unsigned char *eps_params_string = NULL; + unsigned long eps_params_string_len; + char *params_string = NULL; + cairo_eps_params_t eps_params; + cairo_matrix_t mat; + double eps_width, eps_height; + + if (unlikely (params->src_surface->status)) + return params->src_surface->status; + + /* We only embed EPS with level 3 as we may use ReusableStreamDecode and we + * don't know what level the EPS file requires. */ + if (surface->ps_level == CAIRO_PS_LEVEL_2) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_EPS, + &eps_data, &eps_data_len); + if (eps_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_EPS_PARAMS, + &eps_params_string, &eps_params_string_len); + if (eps_params_string == NULL) + 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; + status = _cairo_tag_parse_eps_params (params_string, &eps_params); + if (unlikely(status)) return status; - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; + /* At this point we know emitting EPS will succeed. */ + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + params->is_image = FALSE; + params->approx_size = eps_data_len; + surface->contains_eps = TRUE; - _cairo_output_stream_printf (surface->stream, " Q\n"); - surface->content = old_content; - surface->width = old_width; - surface->height = old_height; - surface->page_bbox = old_page_bbox; - surface->current_pattern_is_solid_color = FALSE; - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->cairo_to_ps = old_cairo_to_ps; + /* Find number of occurrences of SUBFILE_FILTER_EOD in the EPS data. + * We will need it before emitting the data if a ReusableStream is used. + */ + params->eod_count = count_eod_strings (eps_data, eps_data_len); + return CAIRO_STATUS_SUCCESS; + } - _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, - &surface->cairo_to_ps); + surface->ps_level_used = CAIRO_PS_LEVEL_3; + _cairo_output_stream_printf (surface->stream, "cairo_eps_begin\n"); + + eps_width = eps_params.bbox.p2.x - eps_params.bbox.p1.x; + eps_height = eps_params.bbox.p2.y - eps_params.bbox.p1.y; + cairo_matrix_init_translate (&mat, + params->src_surface_extents->x, + params->src_surface_extents->y); + cairo_matrix_scale (&mat, + params->src_surface_extents->width/eps_width, + params->src_surface_extents->height/eps_height); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, -eps_params.bbox.p1.x, -eps_params.bbox.p2.y); + + if (! _cairo_matrix_is_identity (&mat)) { + _cairo_output_stream_printf (surface->stream, "[ "); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, " ] concat\n"); + } + + _cairo_output_stream_printf (surface->stream, + "%f %f %f %f rectclip\n", + eps_params.bbox.p1.x, + eps_params.bbox.p1.y, + eps_width, + eps_height); + + _cairo_output_stream_write (surface->stream, eps_data, eps_data_len); + _cairo_output_stream_printf (surface->stream, "\ncairo_eps_end\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, - cairo_surface_t *recording_surface, - const cairo_rectangle_int_t *extents) +_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface, + cairo_surface_t *recording_surface, + const cairo_rectangle_int_t *recording_extents, + cairo_bool_t subsurface) { double old_width, old_height; + cairo_rectangle_int_t old_surface_extents; + cairo_bool_t old_surface_bounded; cairo_matrix_t old_cairo_to_ps; cairo_content_t old_content; - cairo_rectangle_int_t old_page_bbox; - cairo_status_t status; + cairo_surface_clipper_t old_clipper; + cairo_int_status_t status; + cairo_surface_t *free_me = NULL; + unsigned int id; + int i, recording_surf_stack_size; + + /* Prevent infinite recursion if the recording_surface references a recording + * currently being emitted */ + recording_surf_stack_size = _cairo_array_num_elements (&surface->recording_surf_stack); + for (i = 0; i < recording_surf_stack_size; i++) { + _cairo_array_copy_element (&surface->recording_surf_stack, i, &id); + if (id == recording_surface->unique_id) + return CAIRO_STATUS_SUCCESS; + } + id = recording_surface->unique_id; + status = _cairo_array_append (&surface->recording_surf_stack, &id); + if (unlikely (status)) + return status; + + if (_cairo_surface_is_snapshot (recording_surface)) + free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface); old_content = surface->content; old_width = surface->width; old_height = surface->height; - old_page_bbox = surface->page_bbox; + old_surface_extents = surface->surface_extents; + old_surface_bounded = surface->surface_bounded; old_cairo_to_ps = surface->cairo_to_ps; + old_clipper = surface->clipper; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_ps_surface_clipper_intersect_clip_path); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, - "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n", - extents->x, extents->y, - extents->width, extents->height); + "%% _cairo_ps_surface_emit_recording_surface" + " x: %d, y: %d, w: %d, h: %d subsurface: %d\n", + recording_extents->x, recording_extents->y, + recording_extents->width, recording_extents->height, + subsurface); #endif - surface->page_bbox.x = surface->page_bbox.y = 0; - surface->page_bbox.width = surface->width = extents->width; - surface->page_bbox.height = surface->height = extents->height; - + surface->width = recording_extents->width; + surface->height = recording_extents->height; + surface->surface_extents = *recording_extents; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); - cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height); + cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0); _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); _cairo_output_stream_printf (surface->stream, " q\n"); @@ -2527,14 +3389,14 @@ _cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, surface->content = CAIRO_CONTENT_COLOR; _cairo_output_stream_printf (surface->stream, " 0 g %d %d %d %d rectfill\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + recording_extents->x, + recording_extents->y, + recording_extents->width, + recording_extents->height); } status = _cairo_recording_surface_replay_region (recording_surface, - extents, + subsurface ? recording_extents : NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); @@ -2546,18 +3408,25 @@ _cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface, return status; _cairo_output_stream_printf (surface->stream, " Q\n"); + + _cairo_surface_clipper_reset (&surface->clipper); + surface->clipper = old_clipper; surface->content = old_content; surface->width = old_width; surface->height = old_height; - surface->page_bbox = old_page_bbox; + surface->surface_extents = old_surface_extents; + surface->surface_bounded = old_surface_bounded; surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); surface->cairo_to_ps = old_cairo_to_ps; _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators, &surface->cairo_to_ps); + cairo_surface_destroy (free_me); - return CAIRO_STATUS_SUCCESS; + _cairo_array_truncate (&surface->recording_surf_stack, recording_surf_stack_size); + + return status; } static void @@ -2602,150 +3471,355 @@ _cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, red, green, blue); } -static cairo_status_t -_cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_rectangle_int_t *extents, - int *width, - int *height, - int *origin_x, - int *origin_y) +/* + * PS Forms are used for sources that have CAIRO_MIME_TYPE_UNIQUE_ID. They will be + * emitted once in the PS header and can be rendered with the 'execform' operator. + * + * This function tries adding the source the form hash table. If the source does not + * have CAIRO_MIME_TYPE_UNIQUE_ID, CAIRO_INT_STATUS_UNSUPPORTED is returned. + + * @source: [in] the source for the form + * @params: [in] source parameters + * @test: [in] if TRUE, test if form will be used (excludes size check) + * @ps_form [out] the new or existing entry int the hash table. + * image or recording. + */ +static cairo_int_status_t +_cairo_ps_surface_use_form (cairo_ps_surface_t *surface, + cairo_emit_surface_params_t *params, + cairo_bool_t test, + cairo_ps_form_t **ps_form) { - cairo_status_t status; - cairo_surface_t *pad_image; - int x = 0; - int y = 0; + cairo_ps_form_t source_key; + cairo_ps_form_t *source_entry; + unsigned char *unique_id = NULL; + unsigned long unique_id_length = 0; + cairo_status_t status; + long max_size; - surface->acquired_image = NULL; - surface->image = NULL; + if (params->op != CAIRO_OPERATOR_OVER || params->stencil_mask) + return CAIRO_INT_STATUS_UNSUPPORTED; - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) pattern->surface; + if (params->src_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; - *width = sub->extents.width; - *height = sub->extents.height; - } else { - cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; - cairo_box_t bbox; - cairo_rectangle_int_t extents; + cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_UNIQUE_ID, + (const unsigned char **) &source_key.unique_id, + &source_key.unique_id_length); + if (source_key.unique_id == NULL || source_key.unique_id_length == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (unlikely (status)) - return status; - - _cairo_box_round_to_rectangle (&bbox, &extents); - *width = extents.width; - *height = extents.height; - } + if (test) return CAIRO_STATUS_SUCCESS; - } else { - status = _cairo_surface_acquire_source_image (pattern->surface, - &surface->acquired_image, - &surface->image_extra); - if (unlikely (status)) - return status; - pad_image = &surface->acquired_image->base; - if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_surface_pattern_t pad_pattern; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - x = -rect.x; - y = -rect.y; - - pad_image = - _cairo_image_surface_create_with_pixman_format (NULL, - surface->acquired_image->pixman_format, - rect.width, rect.height, - 0); - if (pad_image->status) { - status = pad_image->status; - goto BAIL; - } - - _cairo_pattern_init_for_surface (&pad_pattern, &surface->acquired_image->base); - cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); - pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_paint (pad_image, - CAIRO_OPERATOR_SOURCE, - &pad_pattern.base, - NULL); - _cairo_pattern_fini (&pad_pattern.base); - if (unlikely (status)) { - if (pad_image != &surface->acquired_image->base) - cairo_surface_destroy (pad_image); - - goto BAIL; - } - } - - surface->image = (cairo_image_surface_t *) pad_image; - *width = surface->image->width; - *height = surface->image->height; - *origin_x = x; - *origin_y = y; + source_key.filter = params->filter; + _cairo_ps_form_init_key (&source_key); + source_entry = _cairo_hash_table_lookup (surface->forms, &source_key.base); + if (source_entry) { + _cairo_rectangle_union (&source_entry->required_extents, params->src_op_extents); + *ps_form = source_entry; return CAIRO_STATUS_SUCCESS; } -BAIL: - _cairo_ps_surface_release_surface (surface, pattern); + if (surface->ps_level == CAIRO_PS_LEVEL_3) + max_size = MAX_L3_FORM_DATA; + else + max_size = MAX_L3_FORM_DATA; + + /* Don't add any more Forms if we exceed the form memory limit */ + if (surface->total_form_size + params->approx_size > max_size) + return CAIRO_INT_STATUS_UNSUPPORTED; + + surface->total_form_size += params->approx_size > max_size; + unique_id = _cairo_malloc (source_key.unique_id_length); + if (unique_id == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + unique_id_length = source_key.unique_id_length; + memcpy (unique_id, source_key.unique_id, unique_id_length); + + source_entry = calloc (sizeof (cairo_ps_form_t), 1); + if (source_entry == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + source_entry->unique_id_length = unique_id_length; + source_entry->unique_id = unique_id; + source_entry->id = surface->num_forms++; + source_entry->src_surface = cairo_surface_reference (params->src_surface); + 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; + source_entry->filter = params->filter; + source_entry->is_image = params->is_image; + _cairo_ps_form_init_key (source_entry); + status = _cairo_hash_table_insert (surface->forms, &source_entry->base); + if (unlikely(status)) + goto fail; + + *ps_form = source_entry; + return CAIRO_STATUS_SUCCESS; + + fail: + free (unique_id); + free (source_entry); return status; } -static cairo_status_t -_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_operator_t op, - int width, - int height) +static cairo_int_status_t +_cairo_ps_surface_emit_form (cairo_ps_surface_t *surface, + cairo_emit_surface_params_t *params, + cairo_bool_t test) { + cairo_ps_form_t *ps_form = NULL; cairo_status_t status; - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - cairo_surface_t *source = pattern->surface; + status = _cairo_ps_surface_use_form (surface, + params, + test, + &ps_form); + if (test || status) + return status; - if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; - status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents); + /* _cairo_ps_form_emit will use Level 3 if permitted by ps_level */ + if (surface->ps_level == CAIRO_PS_LEVEL_3) + surface->ps_level_used = CAIRO_PS_LEVEL_3; + + _cairo_output_stream_printf (surface->stream, + "/cairoform-%d /Form findresource execform\n", + ps_form->id); + + return CAIRO_STATUS_SUCCESS; +} + +/* Emit a surface. This function has three modes. + * + * CAIRO_EMIT_SURFACE_ANALYZE: This will determine the surface type to + * be emitted and approximate size. is_image is set to TRUE if the + * emitted surface is an image surface (including mime images). This + * is used by the caller to setup the correct CTM. approx_size is set + * to the approximate size of the emitted surface and is used as an + * input by the emit mode. + * + * CAIRO_EMIT_SURFACE_EMIT: Emits the surface will be emitted. The + * approx_size and the surface unique id values are used to determine + * if a Form should be used. If a form is used, the exec form + * operation is emitted and the surface is added to the forms hash + * table. + * + * CAIRO_EMIT_SURFACE_EMIT_FORM: Emits the form definition for the surface. + * + * Usage is: + * 1) Setup input params and call with ANALYZE. + * 2) Setup CTM for surface and call with EMIT using same params struct. + * The EMIT_FORM mode is used when emitting the form definitions. + */ +static cairo_int_status_t +_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface, + cairo_emit_surface_mode_t mode, + cairo_emit_surface_params_t *params) +{ + cairo_int_status_t status; + cairo_output_stream_t *old_stream = NULL; + cairo_bool_t use_form; + + /* Try emitting as a form. Returns unsupported if the surface is + * deemed unsuitable for a form. */ + use_form = FALSE; + if (mode == CAIRO_EMIT_SURFACE_ANALYZE || mode == CAIRO_EMIT_SURFACE_EMIT) { + status = _cairo_ps_surface_emit_form (surface, + params, + mode == CAIRO_EMIT_SURFACE_ANALYZE); + use_form = (status == CAIRO_INT_STATUS_SUCCESS); + if (status != CAIRO_INT_STATUS_SUCCESS && status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + if (mode == CAIRO_EMIT_SURFACE_EMIT && status == CAIRO_INT_STATUS_SUCCESS) + return status; + } + + status = _cairo_ps_surface_emit_eps (surface, mode, params); + if (status == CAIRO_INT_STATUS_SUCCESS) { + params->is_image = FALSE; + goto surface_emitted; + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_jpeg_image (surface, mode, params); + if (status == CAIRO_INT_STATUS_SUCCESS) { + params->is_image = TRUE; + goto surface_emitted; + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_ccitt_image (surface, mode, params); + if (status == CAIRO_INT_STATUS_SUCCESS) { + params->is_image = TRUE; + goto surface_emitted; + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + /* Find size of image or recording surface by emitting to a memory stream */ + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + old_stream = surface->stream; + surface->stream = _cairo_memory_stream_create (); + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); + } + + if (params->src_surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + params->is_image = FALSE; + if (params->src_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) params->src_surface; + status = _cairo_ps_surface_emit_recording_surface (surface, + sub->target, + &sub->extents, + TRUE); } else { - status = _cairo_ps_surface_emit_recording_surface (surface, source); + status = _cairo_ps_surface_emit_recording_surface (surface, + params->src_surface, + params->src_op_extents, + FALSE); } } else { - if (pattern->base.extend != CAIRO_EXTEND_PAD) { - status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface, - width, height); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_ps_surface_emit_image (surface, surface->image, - op, pattern->base.filter); + params->is_image = TRUE; + status = _cairo_ps_surface_emit_image (surface, mode, params); } + if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { + unsigned char *data; + unsigned long length; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_memory_stream_destroy (surface->stream, &data, &length); + free (data); + if (unlikely (status)) + return status; + + params->approx_size = length; + surface->stream = old_stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, + surface->stream); + } + + surface_emitted: + return status; } static void -_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern) +_cairo_ps_form_emit (void *entry, void *closure) { - if (surface->image != surface->acquired_image) - cairo_surface_destroy (&surface->image->base); + cairo_ps_form_t *form = entry; + cairo_ps_surface_t *surface = closure; + cairo_emit_surface_params_t params; + cairo_int_status_t status; + cairo_output_stream_t *old_stream; - if (pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) { - _cairo_surface_release_source_image (pattern->surface, - surface->acquired_image, - surface->image_extra); + params.src_surface = form->src_surface; + params.op = CAIRO_OPERATOR_OVER; + params.src_surface_extents = &form->src_surface_extents; + params.src_surface_bounded = form->src_surface_bounded; + params.src_op_extents = &form->required_extents; + params.filter = form->filter; + params.stencil_mask = FALSE; + params.is_image = form->is_image; + params.approx_size = 0; + params.eod_count = 0; + + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginResource: form cairoform-%d\n", + form->id); + + _cairo_output_stream_printf (surface->final_stream, + "/cairo_paint_form-%d", + form->id); + if (surface->ps_level == CAIRO_PS_LEVEL_3) { + surface->paint_proc = FALSE; + _cairo_output_stream_printf (surface->final_stream, + "\n" + "currentfile\n" + "<< /Filter /SubFileDecode\n" + " /DecodeParms << /EODString (%s) /EODCount 0 >>\n" + ">> /ReusableStreamDecode filter\n", + SUBFILE_FILTER_EOD); + } else { + surface->paint_proc = TRUE; + _cairo_output_stream_printf (surface->final_stream, + " {\n"); + } + _cairo_output_stream_printf (surface->final_stream, + "5 dict begin\n"); + + old_stream = surface->stream; + surface->stream = surface->final_stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); + status = _cairo_ps_surface_emit_surface (surface, + CAIRO_EMIT_SURFACE_EMIT_FORM, + ¶ms); + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + surface->stream = old_stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->stream); + + _cairo_output_stream_printf (surface->final_stream, + "end\n"); + if (surface->ps_level == CAIRO_PS_LEVEL_3) { + _cairo_output_stream_printf (surface->final_stream, + "%s\n" + "def\n", + SUBFILE_FILTER_EOD); + } else { + _cairo_output_stream_printf (surface->final_stream, + "} bind def\n"); } - surface->acquired_image = NULL; - surface->image = NULL; + _cairo_output_stream_printf (surface->final_stream, + "\n" + "/cairoform-%d\n" + "<<\n" + " /FormType 1\n", + form->id); + + if (form->is_image) { + _cairo_output_stream_printf (surface->final_stream, + " /BBox [ 0 0 1 1 ]\n"); + } else { + _cairo_output_stream_printf (surface->final_stream, + " /BBox [ %d %d %d %d ]\n", + form->required_extents.x, + form->required_extents.y, + form->required_extents.x + form->required_extents.width, + form->required_extents.y + form->required_extents.height); + } + + _cairo_output_stream_printf (surface->final_stream, + " /Matrix [ 1 0 0 1 0 0 ]\n" + " /PaintProc { pop cairo_paint_form-%d", + form->id); + + if (surface->ps_level == CAIRO_PS_LEVEL_3) { + _cairo_output_stream_printf (surface->final_stream, + " dup 0 setfileposition cvx exec"); + } + _cairo_output_stream_printf (surface->final_stream, + " } bind\n" + ">>\n" + "/Form defineresource pop\n"); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EndResource\n"); + if (status) + surface->base.status = status; } static void @@ -2778,58 +3852,100 @@ _path_fixed_init_rectangle (cairo_path_fixed_t *path, } static cairo_status_t -_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, - cairo_rectangle_int_t *extents, - cairo_operator_t op) +_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op, + cairo_bool_t stencil_mask) { + cairo_rectangle_int_t src_surface_extents; + cairo_bool_t src_surface_bounded; + cairo_rectangle_int_t src_op_extents; + cairo_surface_t *source_surface; + double x_offset, y_offset; cairo_status_t status; - int width, height; cairo_matrix_t cairo_p2d, ps_p2d; cairo_path_fixed_t path; - int origin_x = 0; - int origin_y = 0; + cairo_emit_surface_params_t params; + cairo_image_surface_t *image = NULL; status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status; - status = _cairo_ps_surface_acquire_surface (surface, - pattern, - extents, - &width, &height, - &origin_x, &origin_y); + status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, + pattern, + extents, + &src_surface_extents, + &src_surface_bounded, + &src_op_extents, + &source_surface, + &x_offset, + &y_offset); if (unlikely (status)) return status; + if (pattern->extend == CAIRO_EXTEND_PAD && + pattern->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *img; + + img = (cairo_image_surface_t *) source_surface; + status = _cairo_ps_surface_create_padded_image_from_image (surface, + img, + &pattern->matrix, + extents, + &image, + &src_surface_extents); + if (unlikely (status)) + goto release_source; + + x_offset = src_surface_extents.x; + y_offset = src_surface_extents.y; + } + _path_fixed_init_rectangle (&path, extents); status = _cairo_pdf_operators_clip (&surface->pdf_operators, &path, CAIRO_FILL_RULE_WINDING); _cairo_path_fixed_fini (&path); if (unlikely (status)) - return status; + goto release_source; - cairo_p2d = pattern->base.matrix; + cairo_p2d = pattern->matrix; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - double scale = cairo_p2d.xx; + double x_scale = cairo_p2d.xx; + double y_scale = cairo_p2d.yy; _cairo_output_stream_printf (surface->stream, - "%% Fallback Image: x=%f, y=%f, w=%d, h=%d res=%fdpi size=%ld\n", - -cairo_p2d.x0/scale, - -cairo_p2d.y0/scale, - (int)(width/scale), - (int)(height/scale), - scale*72, - (long)width*height*3); + "%% Fallback Image: x=%f y=%f w=%d h=%d ", + -cairo_p2d.x0/x_scale, + -cairo_p2d.y0/y_scale, + (int)(src_surface_extents.width/x_scale), + (int)(src_surface_extents.height/y_scale)); + if (x_scale == y_scale) { + _cairo_output_stream_printf (surface->stream, + "res=%fppi ", + x_scale*72); + } else { + _cairo_output_stream_printf (surface->stream, + "res=%fx%fppi ", + x_scale*72, + y_scale*72); + } + _cairo_output_stream_printf (surface->stream, + "size=%ld\n", + (long)src_surface_extents.width * src_surface_extents.height * 3); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", + "%d g %d %d %d %d rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, - surface->width, - surface->height); + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); } } @@ -2839,53 +3955,104 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface, ps_p2d = surface->cairo_to_ps; cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, -origin_x, -origin_y); - cairo_matrix_translate (&ps_p2d, 0.0, height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_translate (&ps_p2d, x_offset, y_offset); - if (! _cairo_matrix_is_identity (&ps_p2d)) { - _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ] concat\n", - ps_p2d.xx, ps_p2d.yx, - ps_p2d.xy, ps_p2d.yy, - ps_p2d.x0, ps_p2d.y0); + params.src_surface = image ? &image->base : source_surface; + params.op = op; + params.src_surface_extents = &src_surface_extents; + params.src_surface_bounded = src_surface_bounded; + params.src_op_extents = &src_op_extents; + params.filter = pattern->filter; + params.stencil_mask = stencil_mask; + params.is_image = FALSE; + params.approx_size = 0; + + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_ANALYZE, ¶ms); + if (unlikely (status)) + goto release_source; + + if (params.is_image) { + cairo_matrix_translate (&ps_p2d, 0.0, src_surface_extents.height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_scale (&ps_p2d, src_surface_extents.width, src_surface_extents.height); } - status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height); - _cairo_ps_surface_release_surface (surface, pattern); + if (! _cairo_matrix_is_identity (&ps_p2d)) { + _cairo_output_stream_printf (surface->stream, "[ "); + _cairo_output_stream_print_matrix (surface->stream, &ps_p2d); + _cairo_output_stream_printf (surface->stream, " ] concat\n"); + } + + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_EMIT, ¶ms); + + release_source: + if (image) + cairo_surface_destroy (&image->base); + + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); return status; } static cairo_status_t _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, - cairo_surface_pattern_t *pattern, + cairo_pattern_t *pattern, cairo_rectangle_int_t *extents, cairo_operator_t op) { cairo_status_t status; - int pattern_width = 0; /* squelch bogus compiler warning */ - int pattern_height = 0; /* squelch bogus compiler warning */ double xstep, ystep; + cairo_rectangle_int_t pattern_extents; + cairo_bool_t bounded; cairo_matrix_t cairo_p2d, ps_p2d; - cairo_bool_t old_use_string_datasource; - int origin_x = 0; - int origin_y = 0; + cairo_bool_t old_paint_proc; + double x_offset, y_offset; + cairo_surface_t *source_surface; + cairo_image_surface_t *image = NULL; + cairo_rectangle_int_t src_op_extents; + cairo_emit_surface_params_t params; + cairo_extend_t extend = cairo_pattern_get_extend (pattern); - cairo_p2d = pattern->base.matrix; + cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_ps_surface_acquire_surface (surface, - pattern, - extents, - &pattern_width, &pattern_height, - &origin_x, &origin_y); + status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface, + pattern, + extents, + &pattern_extents, + &bounded, + &src_op_extents, + &source_surface, + &x_offset, &y_offset); if (unlikely (status)) return status; - switch (pattern->base.extend) { + if (extend == CAIRO_EXTEND_PAD) { + cairo_image_surface_t *img; + + assert (source_surface->type == CAIRO_SURFACE_TYPE_IMAGE); + img = (cairo_image_surface_t *) source_surface; + status = _cairo_ps_surface_create_padded_image_from_image (surface, + img, + &pattern->matrix, + extents, + &image, + &pattern_extents); + if (unlikely (status)) + goto release_source; + } + if (unlikely (status)) + goto release_source; + + if (!bounded) + { + extend = CAIRO_EXTEND_NONE; + _cairo_rectangle_intersect (&pattern_extents, &src_op_extents); + } + + switch (extend) { case CAIRO_EXTEND_PAD: case CAIRO_EXTEND_NONE: { @@ -2902,8 +4069,9 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, * repeat visibly. */ double x1 = 0.0, y1 = 0.0; - double x2 = surface->width, y2 = surface->height; - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; + _cairo_matrix_transform_bounding_box (&pattern->matrix, &x1, &y1, &x2, &y2, NULL); @@ -2912,16 +4080,16 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, * required an answer that's large enough, we don't really * care if it's not as tight as possible.*/ xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_width + pattern_height); + pattern_extents.width + pattern_extents.height); break; } case CAIRO_EXTEND_REPEAT: - xstep = pattern_width; - ystep = pattern_height; + xstep = pattern_extents.width; + ystep = pattern_extents.height; break; case CAIRO_EXTEND_REFLECT: - xstep = pattern_width*2; - ystep = pattern_height*2; + xstep = pattern_extents.width*2; + ystep = pattern_extents.height*2; break; /* All the rest (if any) should have been analyzed away, so these * cases should be unreachable. */ @@ -2932,24 +4100,49 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, } _cairo_output_stream_printf (surface->stream, - "/CairoPattern {\n"); + "/CairoPattern {\n" + "q %d %d %d %d rectclip\n", + pattern_extents.x, pattern_extents.y, + pattern_extents.width, pattern_extents.height); + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) + src_op_extents = pattern_extents; + + old_paint_proc = surface->paint_proc; + surface->paint_proc = TRUE; + params.src_surface = image ? &image->base : source_surface; + params.op = op; + params.src_surface_extents = &pattern_extents; + params.src_surface_bounded = bounded; + params.src_op_extents = &src_op_extents; + params.filter = pattern->filter; + params.stencil_mask = FALSE; + params.is_image = FALSE; + params.approx_size = 0; + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_ANALYZE, ¶ms); + if (unlikely (status)) + goto release_source; + + if (params.is_image) { + _cairo_output_stream_printf (surface->stream, + "[ %d 0 0 %d 0 0 ] concat\n", + pattern_extents.width, pattern_extents.height); + } - old_use_string_datasource = surface->use_string_datasource; - surface->use_string_datasource = TRUE; if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, - "%d g 0 0 %f %f rectfill\n", + "%d g %d %d %f %f rectfill\n", surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, + pattern_extents.x, pattern_extents.y, xstep, ystep); } - status = _cairo_ps_surface_emit_surface (surface, pattern, op, - pattern_width, pattern_height); - if (unlikely (status)) - return status; - surface->use_string_datasource = old_use_string_datasource; + status = _cairo_ps_surface_emit_surface (surface, CAIRO_EMIT_SURFACE_EMIT, ¶ms); + if (unlikely (status)) + goto release_source; + _cairo_output_stream_printf (surface->stream, - "} bind def\n"); + " Q } bind def\n"); _cairo_output_stream_printf (surface->stream, "<< /PatternType 1\n" @@ -2959,20 +4152,43 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, " /XStep %f /YStep %f\n", xstep, ystep); - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + if (extend == CAIRO_EXTEND_REFLECT) { + cairo_matrix_t mat; + _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n" + " /BBox [%d %d %d %d]\n" " /PaintProc {\n" - " CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " [ 1 0 0 -1 0 %d] concat CairoPattern\n" - " [-1 0 0 1 %d 0] concat CairoPattern\n" - " CairoPattern\n" - " } bind\n", - pattern_width*2, pattern_height*2, - pattern_width*2, - pattern_height*2, - pattern_width*2); + " pop CairoPattern\n", + pattern_extents.x, + pattern_extents.y, + pattern_extents.x + pattern_extents.width*2, + pattern_extents.y + pattern_extents.height*2); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, -1, 1); + cairo_matrix_translate (&mat, -2*pattern_extents.width, 0); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, 1, -1); + cairo_matrix_translate (&mat, 0, -2*pattern_extents.height); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + cairo_matrix_init_translate (&mat, pattern_extents.x, pattern_extents.y); + cairo_matrix_scale (&mat, -1, -1); + cairo_matrix_translate (&mat, -2*pattern_extents.width, -2*pattern_extents.height); + cairo_matrix_translate (&mat, -pattern_extents.x, -pattern_extents.y); + _cairo_output_stream_printf (surface->stream, " q ["); + _cairo_output_stream_print_matrix (surface->stream, &mat); + _cairo_output_stream_printf (surface->stream, "] concat CairoPattern Q\n"); + + _cairo_output_stream_printf (surface->stream, " } bind\n"); } else { if (op == CAIRO_OPERATOR_SOURCE) { _cairo_output_stream_printf (surface->stream, @@ -2980,37 +4196,48 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface, xstep, ystep); } else { _cairo_output_stream_printf (surface->stream, - " /BBox [0 0 %d %d]\n", - pattern_width, pattern_height); + " /BBox [%d %d %d %d]\n", + pattern_extents.x, + pattern_extents.y, + pattern_extents.x + pattern_extents.width, + pattern_extents.y + pattern_extents.height); } _cairo_output_stream_printf (surface->stream, - " /PaintProc { CairoPattern }\n"); + " /PaintProc { pop CairoPattern }\n"); } _cairo_output_stream_printf (surface->stream, ">>\n"); - cairo_p2d = pattern->base.matrix; + cairo_p2d = pattern->matrix; status = cairo_matrix_invert (&cairo_p2d); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_init_identity (&ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, surface->height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); - cairo_matrix_translate (&ps_p2d, 0.0, pattern_height); - cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + cairo_matrix_translate (&ps_p2d, x_offset, y_offset); + if (((cairo_surface_pattern_t *)pattern)->surface->type != CAIRO_SURFACE_TYPE_RECORDING) + { + cairo_matrix_translate (&ps_p2d, 0.0, pattern_extents.height); + cairo_matrix_scale (&ps_p2d, 1.0, -1.0); + } + _cairo_output_stream_printf (surface->stream, "[ "); + _cairo_output_stream_print_matrix (surface->stream, &ps_p2d); _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ]\n", - ps_p2d.xx, ps_p2d.yx, - ps_p2d.xy, ps_p2d.yy, - ps_p2d.x0, ps_p2d.y0); - _cairo_output_stream_printf (surface->stream, + " ]\n" "makepattern setpattern\n"); - return CAIRO_STATUS_SUCCESS; + surface->paint_proc = old_paint_proc; + + release_source: + if (image) + cairo_surface_destroy (&image->base); + + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); + + return status; } typedef struct _cairo_ps_color_stop { @@ -3104,7 +4331,8 @@ _cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, } if (pattern->base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.extend == CAIRO_EXTEND_REFLECT) { + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { if (stops[0].offset > COLOR_STOP_EPSILON) { if (pattern->base.extend == CAIRO_EXTEND_REFLECT) memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t)); @@ -3146,16 +4374,40 @@ _cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->stream, "/CairoFunction\n"); - if (n_stops == 1) { - /* work around single stop gradients */ - _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[0]); + if (stops[0].offset == stops[n_stops - 1].offset) { + /* + * The first and the last stops have the same offset, but we + * don't want a function with an empty domain, because that + * would provoke underdefined behaviour from rasterisers. + * This can only happen with EXTEND_PAD, because EXTEND_NONE + * is optimised into a clear pattern in cairo-gstate, and + * REFLECT/REPEAT are always transformed to have the first + * stop at t=0 and the last stop at t=1. Thus we want a step + * function going from the first color to the last one. + * + * This can be accomplished by stitching three functions: + * - a constant first color function, + * - a step from the first color to the last color (with empty domain) + * - a constant last color function + */ + cairo_ps_color_stop_t pad_stops[4]; + + assert (pattern->base.extend == CAIRO_EXTEND_PAD); + + pad_stops[0] = pad_stops[1] = stops[0]; + pad_stops[2] = pad_stops[3] = stops[n_stops - 1]; + + pad_stops[0].offset = 0; + pad_stops[3].offset = 1; + + _cairo_ps_surface_emit_stitched_colorgradient (surface, 4, pad_stops); } else if (n_stops == 2) { /* no need for stitched function */ _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]); } else { /* multiple stops: stitch. XXX possible optimization: regulary spaced * stops do not require stitching. XXX */ - _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops,stops); + _cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops, stops); } _cairo_output_stream_printf (surface->stream, "def\n"); @@ -3199,112 +4451,129 @@ _cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface, } static cairo_status_t -_cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, - cairo_linear_pattern_t *pattern) +_cairo_ps_surface_emit_gradient (cairo_ps_surface_t *surface, + cairo_gradient_pattern_t *pattern, + cairo_bool_t is_ps_pattern) { - double x1, y1, x2, y2; - double _x1, _y1, _x2, _y2; cairo_matrix_t pat_to_ps; - cairo_extend_t extend; + cairo_circle_double_t start, end; + double domain[2]; cairo_status_t status; - cairo_gradient_pattern_t *gradient = &pattern->base; - double first_stop, last_stop; - int repeat_begin = 0, repeat_end = 1; - extend = cairo_pattern_get_extend (&pattern->base.base); + assert (pattern->n_stops != 0); - pat_to_ps = pattern->base.base.matrix; - status = cairo_matrix_invert (&pat_to_ps); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); - first_stop = gradient->stops[0].offset; - last_stop = gradient->stops[gradient->n_stops - 1].offset; - - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { - double dx, dy; - int x_rep = 0, y_rep = 0; - - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - cairo_matrix_transform_point (&pat_to_ps, &x1, &y1); - - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - cairo_matrix_transform_point (&pat_to_ps, &x2, &y2); - - dx = fabs (x2 - x1); - dy = fabs (y2 - y1); - if (dx > 1e-6) - x_rep = ceil (surface->width/dx); - if (dy > 1e-6) - y_rep = ceil (surface->height/dy); - - repeat_end = MAX (x_rep, y_rep); - repeat_begin = -repeat_end; - first_stop = repeat_begin; - last_stop = repeat_end; - } - - /* PS requires the first and last stop to be the same as the line - * coordinates. For repeating patterns this moves the line - * coordinates out to the begin/end of the repeating function. For - * non repeating patterns this may move the line coordinates in if - * there are not stops at offset 0 and 1. */ - x1 = _cairo_fixed_to_double (pattern->p1.x); - y1 = _cairo_fixed_to_double (pattern->p1.y); - x2 = _cairo_fixed_to_double (pattern->p2.x); - y2 = _cairo_fixed_to_double (pattern->p2.y); - - _x1 = x1 + (x2 - x1)*first_stop; - _y1 = y1 + (y2 - y1)*first_stop; - _x2 = x1 + (x2 - x1)*last_stop; - _y2 = y1 + (y2 - y1)*last_stop; - - x1 = _x1; - x2 = _x2; - y1 = _y1; - y2 = _y2; - - /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a - * Type 2 function is used by itself without a stitching - * function. Type 2 functions always have the domain [0 1] */ - if ((pattern->base.base.extend == CAIRO_EXTEND_NONE || - pattern->base.base.extend == CAIRO_EXTEND_PAD) && - gradient->n_stops == 2) { - first_stop = 0.0; - last_stop = 1.0; - } - - status = _cairo_ps_surface_emit_pattern_stops (surface, - &pattern->base); + status = _cairo_ps_surface_emit_pattern_stops (surface, pattern); if (unlikely (status)) return status; - if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.base.extend == CAIRO_EXTEND_REFLECT) { + pat_to_ps = pattern->base.matrix; + status = cairo_matrix_invert (&pat_to_ps); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { + double bounds_x1, bounds_x2, bounds_y1, bounds_y2; + double x_scale, y_scale, tolerance; + + /* TODO: use tighter extents */ + bounds_x1 = 0; + bounds_y1 = 0; + bounds_x2 = surface->width; + bounds_y2 = surface->height; + _cairo_matrix_transform_bounding_box (&pattern->base.matrix, + &bounds_x1, &bounds_y1, + &bounds_x2, &bounds_y2, + NULL); + + x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution; + y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution; + + tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix)); + tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1); + tolerance *= MIN (x_scale, y_scale); + + _cairo_gradient_pattern_box_to_parameter (pattern, + bounds_x1, bounds_y1, + bounds_x2, bounds_y2, + tolerance, domain); + } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) { + /* + * If the first and the last stop offset are the same, then + * the color function is a step function. + * _cairo_ps_surface_emit_pattern_stops emits it as a stitched + * function no matter how many stops the pattern has. The + * domain of the stitched function will be [0 1] in this case. + * + * This is done to avoid emitting degenerate gradients for + * EXTEND_PAD patterns having a step color function. + */ + domain[0] = 0.0; + domain[1] = 1.0; + + assert (pattern->base.extend == CAIRO_EXTEND_PAD); + } else { + domain[0] = pattern->stops[0].offset; + domain[1] = pattern->stops[pattern->n_stops - 1].offset; + } + + /* PS requires the first and last stop to be the same as the + * extreme coordinates. For repeating patterns this moves the + * extreme coordinates out to the begin/end of the repeating + * function. For non repeating patterns this may move the extreme + * coordinates in if there are not stops at offset 0 and 1. */ + _cairo_gradient_pattern_interpolate (pattern, domain[0], &start); + _cairo_gradient_pattern_interpolate (pattern, domain[1], &end); + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + pattern->base.extend == CAIRO_EXTEND_REFLECT) + { + int repeat_begin, repeat_end; + + repeat_begin = floor (domain[0]); + repeat_end = ceil (domain[1]); + status = _cairo_ps_surface_emit_repeating_function (surface, - &pattern->base, + pattern, repeat_begin, repeat_end); if (unlikely (status)) return status; + } else if (pattern->n_stops <= 2) { + /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a + * Type 2 function is used by itself without a stitching + * function. Type 2 functions always have the domain [0 1] */ + domain[0] = 0.0; + domain[1] = 1.0; } - _cairo_output_stream_printf (surface->stream, - "<< /PatternType 2\n" - " /Shading\n" - " << /ShadingType 2\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f ]\n" - " /Domain [ %f %f ]\n" - " /Function CairoFunction\n", - x1, y1, x2, y2, - first_stop, last_stop); + if (is_ps_pattern) { + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 2\n" + " /Shading\n"); + } - if (extend == CAIRO_EXTEND_PAD) { + if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + _cairo_output_stream_printf (surface->stream, + " << /ShadingType 2\n" + " /ColorSpace /DeviceRGB\n" + " /Coords [ %f %f %f %f ]\n", + start.center.x, start.center.y, + end.center.x, end.center.y); + } else { + _cairo_output_stream_printf (surface->stream, + " << /ShadingType 3\n" + " /ColorSpace /DeviceRGB\n" + " /Coords [ %f %f %f %f %f %f ]\n", + start.center.x, start.center.y, + MAX (start.radius, 0), + end.center.x, end.center.y, + MAX (end.radius, 0)); + } + + if (pattern->base.extend != CAIRO_EXTEND_NONE) { _cairo_output_stream_printf (surface->stream, " /Extend [ true true ]\n"); } else { @@ -3312,76 +4581,122 @@ _cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t *surface, " /Extend [ false false ]\n"); } + if (domain[0] == 0.0 && domain[1] == 1.0) { + _cairo_output_stream_printf (surface->stream, + " /Function CairoFunction\n"); + } else { + _cairo_output_stream_printf (surface->stream, + " /Function <<\n" + " /FunctionType 3\n" + " /Domain [ 0 1 ]\n" + " /Bounds [ ]\n" + " /Encode [ %f %f ]\n" + " /Functions [ CairoFunction ]\n" + " >>\n", + domain[0], domain[1]); + } + _cairo_output_stream_printf (surface->stream, - " >>\n" - ">>\n"); - _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ]\n", - pat_to_ps.xx, pat_to_ps.yx, - pat_to_ps.xy, pat_to_ps.yy, - pat_to_ps.x0, pat_to_ps.y0); - _cairo_output_stream_printf (surface->stream, + " >>\n"); + + if (is_ps_pattern) { + _cairo_output_stream_printf (surface->stream, + ">>\n" + "[ "); + _cairo_output_stream_print_matrix (surface->stream, &pat_to_ps); + _cairo_output_stream_printf (surface->stream, " ]\n" "makepattern setpattern\n"); + } else { + _cairo_output_stream_printf (surface->stream, + "shfill\n"); + } return status; } static cairo_status_t -_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface, - cairo_radial_pattern_t *pattern) +_cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t *surface, + cairo_mesh_pattern_t *pattern, + cairo_bool_t is_ps_pattern) { - double x1, y1, x2, y2, r1, r2; cairo_matrix_t pat_to_ps; - cairo_extend_t extend; cairo_status_t status; + cairo_pdf_shading_t shading; + int i; - extend = cairo_pattern_get_extend (&pattern->base.base); + if (_cairo_array_num_elements (&pattern->patches) == 0) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - pat_to_ps = pattern->base.base.matrix; + pat_to_ps = pattern->base.matrix; status = cairo_matrix_invert (&pat_to_ps); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); - x1 = _cairo_fixed_to_double (pattern->c1.x); - y1 = _cairo_fixed_to_double (pattern->c1.y); - r1 = _cairo_fixed_to_double (pattern->r1); - x2 = _cairo_fixed_to_double (pattern->c2.x); - y2 = _cairo_fixed_to_double (pattern->c2.y); - r2 = _cairo_fixed_to_double (pattern->r2); - status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base); - if (unlikely (status)) - return status; + status = _cairo_pdf_shading_init_color (&shading, pattern); + if (unlikely (status)) + return status; - _cairo_output_stream_printf (surface->stream, - "<< /PatternType 2\n" - " /Shading\n" - " << /ShadingType 3\n" - " /ColorSpace /DeviceRGB\n" - " /Coords [ %f %f %f %f %f %f ]\n" - " /Function CairoFunction\n", - x1, y1, r1, x2, y2, r2); + _cairo_output_stream_printf (surface->stream, + "currentfile\n" + "/ASCII85Decode filter /FlateDecode filter /ReusableStreamDecode filter\n"); - if (extend == CAIRO_EXTEND_PAD) { + status = _cairo_ps_surface_emit_base85_string (surface, + shading.data, + shading.data_length, + CAIRO_PS_COMPRESS_DEFLATE, + FALSE); + if (status) + return status; + + _cairo_output_stream_printf (surface->stream, + "\n" + "/CairoData exch def\n"); + + if (is_ps_pattern) { _cairo_output_stream_printf (surface->stream, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->stream, - " /Extend [ false false ]\n"); + "<< /PatternType 2\n" + " /Shading\n"); } _cairo_output_stream_printf (surface->stream, - " >>\n" - ">>\n"); + " << /ShadingType %d\n" + " /ColorSpace /DeviceRGB\n" + " /DataSource CairoData\n" + " /BitsPerCoordinate %d\n" + " /BitsPerComponent %d\n" + " /BitsPerFlag %d\n" + " /Decode [", + shading.shading_type, + shading.bits_per_coordinate, + shading.bits_per_component, + shading.bits_per_flag); + + for (i = 0; i < shading.decode_array_length; i++) + _cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]); _cairo_output_stream_printf (surface->stream, - "[ %f %f %f %f %f %f ]\n", - pat_to_ps.xx, pat_to_ps.yx, - pat_to_ps.xy, pat_to_ps.yy, - pat_to_ps.x0, pat_to_ps.y0); + "]\n" + " >>\n"); + + if (is_ps_pattern) { + _cairo_output_stream_printf (surface->stream, + ">>\n" + "[ \n"); + _cairo_output_stream_print_matrix (surface->stream, &pat_to_ps); + _cairo_output_stream_printf (surface->stream, + " ]\n" + "makepattern\n" + "setpattern\n"); + } else { + _cairo_output_stream_printf (surface->stream, "shfill\n"); + } + _cairo_output_stream_printf (surface->stream, - "makepattern setpattern\n"); + "currentdict /CairoData undef\n"); + + _cairo_pdf_shading_fini (&shading); return status; } @@ -3394,10 +4709,6 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, { cairo_status_t status; - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; @@ -3429,8 +4740,9 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, break; case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: status = _cairo_ps_surface_emit_surface_pattern (surface, - (cairo_surface_pattern_t *) pattern, + (cairo_pattern_t *)pattern, extents, op); if (unlikely (status)) @@ -3438,15 +4750,18 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, break; case CAIRO_PATTERN_TYPE_LINEAR: - status = _cairo_ps_surface_emit_linear_pattern (surface, - (cairo_linear_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_ps_surface_emit_gradient (surface, + (cairo_gradient_pattern_t *) pattern, + TRUE); if (unlikely (status)) return status; break; - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_ps_surface_emit_radial_pattern (surface, - (cairo_radial_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_MESH: + status = _cairo_ps_surface_emit_mesh_pattern (surface, + (cairo_mesh_pattern_t *) pattern, + TRUE); if (unlikely (status)) return status; break; @@ -3455,23 +4770,106 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_cairo_ps_surface_paint_gradient (cairo_ps_surface_t *surface, + const cairo_pattern_t *source, + const cairo_rectangle_int_t *extents) +{ + cairo_matrix_t pat_to_ps; + cairo_status_t status; + + pat_to_ps = source->matrix; + status = cairo_matrix_invert (&pat_to_ps); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps); + + if (! _cairo_matrix_is_identity (&pat_to_ps)) { + _cairo_output_stream_printf (surface->stream, "["); + _cairo_output_stream_print_matrix (surface->stream, &pat_to_ps); + _cairo_output_stream_printf (surface->stream, "] concat\n"); + } + + if (source->type == CAIRO_PATTERN_TYPE_MESH) { + status = _cairo_ps_surface_emit_mesh_pattern (surface, + (cairo_mesh_pattern_t *)source, + FALSE); + if (unlikely (status)) + return status; + } else { + status = _cairo_ps_surface_emit_gradient (surface, + (cairo_gradient_pattern_t *)source, + FALSE); + if (unlikely (status)) + return status; + } + + return status; +} + +static cairo_status_t +_cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface, + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents, + cairo_operator_t op, + cairo_bool_t stencil_mask) +{ + switch (source->type) { + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _cairo_ps_surface_paint_surface (surface, + source, + extents, + op, + stencil_mask); + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + return _cairo_ps_surface_paint_gradient (surface, + source, + extents); + + case CAIRO_PATTERN_TYPE_SOLID: + default: + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; + } +} + +static cairo_bool_t +_can_paint_pattern (const cairo_pattern_t *pattern) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return FALSE; + + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return (pattern->extend == CAIRO_EXTEND_NONE || + pattern->extend == CAIRO_EXTEND_PAD); + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + return TRUE; + + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + static cairo_bool_t _cairo_ps_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { cairo_ps_surface_t *surface = abstract_surface; - rectangle->x = 0; - rectangle->y = 0; + if (surface->surface_bounded) + *rectangle = surface->surface_extents; - /* XXX: The conversion to integers here is pretty bogus, (not to - * mention the aribitray limitation of width to a short(!). We - * may need to come up with a better interface for get_extents. - */ - rectangle->width = ceil (surface->width); - rectangle->height = ceil (surface->height); - - return TRUE; + return surface->surface_bounded; } static void @@ -3483,191 +4881,258 @@ _cairo_ps_surface_get_font_options (void *abstract_surface, cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); +} + +static cairo_int_status_t +_cairo_ps_surface_set_clip (cairo_ps_surface_t *surface, + cairo_composite_rectangles_t *composite) +{ + cairo_clip_t *clip = composite->clip; + + if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) + clip = NULL; + + if (clip == NULL) { + if (_cairo_composite_rectangles_can_reduce_clip (composite, + surface->clipper.clip)) + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_surface_clipper_set_clip (&surface->clipper, clip); } static cairo_int_status_t _cairo_ps_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; cairo_composite_rectangles_t extents; cairo_status_t status; - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, + &surface->base, op, source, clip); if (unlikely (status)) return status; - if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) - return CAIRO_STATUS_SUCCESS; + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + goto cleanup_composite; + } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_paint\n"); #endif - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - (source->extend == CAIRO_EXTEND_NONE || - source->extend == CAIRO_EXTEND_PAD)) - { + if (_can_paint_pattern (source)) { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "q\n"); - status = _cairo_ps_surface_paint_surface (surface, - (cairo_surface_pattern_t *) source, - &extents.bounded, op); + status = _cairo_ps_surface_paint_pattern (surface, + source, + &extents.bounded, op, FALSE); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "Q\n"); } else { status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", - extents.bounded.x, extents.bounded.y, - extents.bounded.width, extents.bounded.height); + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); } - return CAIRO_STATUS_SUCCESS; +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; +} + +static cairo_int_status_t +_cairo_ps_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_ps_surface_t *surface = abstract_surface; + cairo_output_stream_t *stream = surface->stream; + cairo_composite_rectangles_t extents; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &surface->base, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded); + goto cleanup_composite; + } + + assert (_cairo_ps_surface_operation_supported (surface, op, source, mask, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_mask\n"); +#endif + + status = _cairo_ps_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup_composite; + + status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); + if (unlikely (status)) + goto cleanup_composite; + + _cairo_output_stream_printf (stream, "q\n"); + status = _cairo_ps_surface_paint_pattern (surface, + mask, + &extents.bounded, op, TRUE); + if (unlikely (status)) + goto cleanup_composite; + + _cairo_output_stream_printf (stream, "Q\n"); + +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t _cairo_ps_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_int_status_t status; - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, + &surface->base, op, source, path, style, ctm, clip); if (unlikely (status)) return status; - if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) - return CAIRO_STATUS_SUCCESS; - /* use the more accurate extents */ - if (extents.is_bounded) { + { + cairo_rectangle_int_t r; + cairo_box_t b; + status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, - &extents.mask); + &r); if (unlikely (status)) - return status; + goto cleanup_composite; - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&b, &r); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b); + if (unlikely (status)) + goto cleanup_composite; } + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + goto cleanup_composite; + } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); - - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_stroke\n"); #endif - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return status; + goto cleanup_composite; - return _cairo_pdf_operators_stroke (&surface->pdf_operators, - path, - style, - ctm, - ctm_inverse); + status = _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); + +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t _cairo_ps_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_int_status_t status; - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, + &surface->base, op, source, path, clip); if (unlikely (status)) return status; - if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox)) - return CAIRO_STATUS_SUCCESS; - /* use the more accurate extents */ - if (extents.is_bounded) { + { + cairo_rectangle_int_t r; + cairo_box_t b; + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &extents.mask); + &r); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; + _cairo_box_from_rectangle (&b, &r); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b); + if (unlikely (status)) + goto cleanup_composite; } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + goto cleanup_composite; + } - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, @@ -3676,67 +5141,71 @@ _cairo_ps_surface_fill (void *abstract_surface, status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup_composite; - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - (source->extend == CAIRO_EXTEND_NONE || - source->extend == CAIRO_EXTEND_PAD)) - { + if (_can_paint_pattern (source)) { _cairo_output_stream_printf (surface->stream, "q\n"); status = _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); if (unlikely (status)) - return status; + goto cleanup_composite; - status = _cairo_ps_surface_paint_surface (surface, - (cairo_surface_pattern_t *) source, - &extents.bounded, op); + status = _cairo_ps_surface_paint_pattern (surface, + source, + &extents.bounded, op, FALSE); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_pdf_operators_reset (&surface->pdf_operators); } else { status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); } +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); return status; } +static cairo_bool_t +_cairo_ps_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + static cairo_int_status_t -_cairo_ps_surface_show_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, - cairo_clip_t *clip, - int *remaining_glyphs) +_cairo_ps_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; cairo_bool_t overlap; cairo_status_t status; - cairo_rectangle_int_t rect; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, + &surface->base, op, source, scaled_font, glyphs, num_glyphs, @@ -3745,33 +5214,45 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, if (unlikely (status)) return status; - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + goto cleanup_composite; + } - assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded)); + assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded)); #if DEBUG_PS _cairo_output_stream_printf (surface->stream, "%% _cairo_ps_surface_show_glyphs\n"); #endif - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return status; + goto cleanup_composite; - return _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE, - scaled_font); + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font); + +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; } -static void +static const char ** +_cairo_ps_surface_get_supported_mime_types (void *abstract_surface) +{ + return _cairo_ps_supported_mime_types; +} + +static cairo_int_status_t _cairo_ps_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { @@ -3780,42 +5261,58 @@ _cairo_ps_surface_set_paginated_mode (void *abstract_surface, surface->paginated_mode = paginated_mode; - if (surface->clipper.clip.path != NULL) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { + surface->surface_extents.x = 0; + surface->surface_extents.y = 0; + surface->surface_extents.width = ceil (surface->width); + surface->surface_extents.height = ceil (surface->height); - _cairo_output_stream_printf (surface->stream, "Q q\n"); - _cairo_surface_clipper_reset (&surface->clipper); + if (surface->clipper.clip != NULL) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->stream, "Q q\n"); + _cairo_surface_clipper_reset (&surface->clipper); + } } + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_ps_surface_set_bounding_box (void *abstract_surface, - cairo_box_t *bbox) + cairo_box_t *analysis_bbox) { cairo_ps_surface_t *surface = abstract_surface; int i, num_comments; char **comments; - int x1, y1, x2, y2; - cairo_bool_t has_page_media; + cairo_bool_t has_page_media, has_page_bbox; const char *page_media; + cairo_rectangle_int_t page_bbox; + cairo_point_int_t bbox_p1, bbox_p2; /* in PS coordinates */ - if (surface->eps) { - x1 = floor (_cairo_fixed_to_double (bbox->p1.x)); - y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y)); - x2 = ceil (_cairo_fixed_to_double (bbox->p2.x)); - y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y)); + _cairo_box_round_to_rectangle (analysis_bbox, &page_bbox); + + /* convert to PS coordinates */ + bbox_p1.x = page_bbox.x; + bbox_p1.y = ceil(surface->height) - (page_bbox.y + page_bbox.height); + bbox_p2.x = page_bbox.x + page_bbox.width; + bbox_p2.y = ceil(surface->height) - page_bbox.y; + + if (surface->num_pages == 1) { + surface->document_bbox_p1 = bbox_p1; + surface->document_bbox_p2 = bbox_p2; } else { - x1 = 0; - y1 = 0; - x2 = ceil (surface->width); - y2 = ceil (surface->height); + if (bbox_p1.x < surface->document_bbox_p1.x) + surface->document_bbox_p1.x = bbox_p1.x; + if (bbox_p1.y < surface->document_bbox_p1.y) + surface->document_bbox_p1.y = bbox_p1.y; + if (bbox_p2.x < surface->document_bbox_p2.x) + surface->document_bbox_p2.x = bbox_p2.x; + if (bbox_p2.y < surface->document_bbox_p2.y) + surface->document_bbox_p2.y = bbox_p2.y; } - surface->page_bbox.x = x1; - surface->page_bbox.y = y1; - surface->page_bbox.width = x2 - x1; - surface->page_bbox.height = y2 - y1; - _cairo_output_stream_printf (surface->stream, "%%%%Page: %d %d\n", surface->num_pages, @@ -3825,6 +5322,7 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, "%%%%BeginPageSetup\n"); has_page_media = FALSE; + has_page_bbox = FALSE; num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments); comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0); for (i = 0; i < num_comments; i++) { @@ -3832,6 +5330,10 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, "%s\n", comments[i]); if (strncmp (comments[i], "%%PageMedia:", 11) == 0) has_page_media = TRUE; + + if (strncmp (comments[i], "%%PageBoundingBox:", 18) == 0) + has_page_bbox = TRUE; + free (comments[i]); comments[i] = NULL; } @@ -3847,33 +5349,32 @@ _cairo_ps_surface_set_bounding_box (void *abstract_surface, page_media); } - _cairo_output_stream_printf (surface->stream, - "%%%%PageBoundingBox: %d %d %d %d\n", - x1, y1, x2, y2); + if (!has_page_bbox) { + _cairo_output_stream_printf (surface->stream, + "%%%%PageBoundingBox: %d %d %d %d\n", + bbox_p1.x, + bbox_p1.y, + bbox_p2.x, + bbox_p2.y); + } + + if (!surface->eps) { + _cairo_output_stream_printf (surface->stream, + "%f %f cairo_set_page_size\n", + ceil(surface->width), + ceil(surface->height)); + } _cairo_output_stream_printf (surface->stream, "%%%%EndPageSetup\n" - "q %d %d %d %d rectclip q\n", - surface->page_bbox.x, - surface->page_bbox.y, - surface->page_bbox.width, - surface->page_bbox.height); + "q %d %d %d %d rectclip\n" + "1 0 0 -1 0 %f cm q\n", + bbox_p1.x, + bbox_p1.y, + bbox_p2.x - bbox_p1.x, + bbox_p2.y - bbox_p1.y, + ceil(surface->height)); - if (surface->num_pages == 1) { - surface->bbox_x1 = x1; - surface->bbox_y1 = y1; - surface->bbox_x2 = x2; - surface->bbox_y2 = y2; - } else { - if (x1 < surface->bbox_x1) - surface->bbox_x1 = x1; - if (y1 < surface->bbox_y1) - surface->bbox_y1 = y1; - if (x2 > surface->bbox_x2) - surface->bbox_x2 = x2; - if (y2 > surface->bbox_y2) - surface->bbox_y2 = y2; - } surface->current_pattern_is_solid_color = FALSE; _cairo_pdf_operators_reset (&surface->pdf_operators); @@ -3888,36 +5389,40 @@ _cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface) static const cairo_surface_backend_t cairo_ps_surface_backend = { CAIRO_SURFACE_TYPE_PS, - NULL, /* create similar: handled by wrapper */ _cairo_ps_surface_finish, + + _cairo_default_context_create, + + NULL, /* create similar: handled by wrapper */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* cairo_ps_surface_copy_page */ _cairo_ps_surface_show_page, + _cairo_ps_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_ps_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* Here are the drawing functions */ _cairo_ps_surface_paint, /* paint */ - NULL, /* mask */ + _cairo_ps_surface_mask, _cairo_ps_surface_stroke, _cairo_ps_surface_fill, - _cairo_ps_surface_show_glyphs, - NULL, /* snapshot */ + NULL, /* fill-stroke */ + NULL, /* show_glyphs */ + _cairo_ps_surface_has_show_text_glyphs, + _cairo_ps_surface_show_text_glyphs, + _cairo_ps_surface_get_supported_mime_types, }; static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = { diff --git a/gfx/cairo/cairo/src/cairo-ps.h b/gfx/cairo/cairo/src/cairo-ps.h index fd1d21deba0f..33d0e0d941bb 100644 --- a/gfx/cairo/cairo/src/cairo-ps.h +++ b/gfx/cairo/cairo/src/cairo-ps.h @@ -49,13 +49,15 @@ CAIRO_BEGIN_DECLS /** * cairo_ps_level_t: - * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification. - * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. + * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification. (Since 1.6) + * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. (Since 1.6) * * #cairo_ps_level_t is used to describe the language level of the * PostScript Language Reference that a generated PostScript file will * conform to. - */ + * + * Since: 1.6 + **/ typedef enum _cairo_ps_level { CAIRO_PS_LEVEL_2, CAIRO_PS_LEVEL_3 diff --git a/gfx/cairo/cairo/src/cairo-qt-surface.cpp b/gfx/cairo/cairo/src/cairo-qt-surface.cpp index 6311c7100f0e..d276f059e1fd 100644 --- a/gfx/cairo/cairo/src/cairo-qt-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-qt-surface.cpp @@ -13,7 +13,7 @@ * * 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 + * 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 * @@ -38,14 +38,20 @@ #define __STDC_LIMIT_MACROS #include "cairoint.h" -#include "cairo-types-private.h" + #include "cairo-clip-private.h" -#include "cairo-surface-clipper-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" #include "cairo-region-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-types-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-backend-private.h" +#include "cairo-surface-fallback-private.h" #include "cairo-ft.h" #include "cairo-qt.h" -#include "cairo-error-private.h" #include @@ -56,8 +62,13 @@ #include #include #include +#include #include +#if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0 +extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count); +#endif + #include /* Enable workaround slow regional Qt paths */ @@ -126,9 +137,9 @@ struct cairo_qt_surface_t { */ static cairo_bool_t _qpixmaps_have_no_alpha = FALSE; -/** - ** Helper methods - **/ +/* + * Helper methods + */ static QPainter::CompositionMode _qpainter_compositionmode_from_cairo_op (cairo_operator_t op) @@ -182,12 +193,14 @@ _qpainter_compositionmode_from_cairo_op (cairo_operator_t op) case CAIRO_OPERATOR_HSL_LUMINOSITY: ASSERT_NOT_REACHED; } - return QPainter::CompositionMode_Source; } static bool _op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op) { + if (qs->p == NULL) + return false; + if (qs->supports_porter_duff) { switch (op) { case CAIRO_OPERATOR_CLEAR: @@ -277,10 +290,14 @@ static QImage::Format _qimage_format_from_cairo_format (cairo_format_t fmt) { switch (fmt) { + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: return QImage::Format_ARGB32_Premultiplied; case CAIRO_FORMAT_RGB24: return QImage::Format_RGB32; + case CAIRO_FORMAT_RGB16_565: + return QImage::Format_RGB16; case CAIRO_FORMAT_A8: return QImage::Format_Indexed8; // XXX not quite case CAIRO_FORMAT_A1: @@ -289,6 +306,8 @@ _qimage_format_from_cairo_format (cairo_format_t fmt) #else return QImage::Format_MonoLSB; #endif + case CAIRO_FORMAT_RGB30: + return QImage::Format_Mono; } return QImage::Format_Mono; @@ -300,7 +319,7 @@ _qmatrix_from_cairo_matrix (const cairo_matrix_t& m) return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); } -/** Path conversion **/ +/* Path conversion */ typedef struct _qpainter_path_transform { QPainterPath path; const cairo_matrix_t *ctm_inverse; @@ -369,8 +388,8 @@ _cairo_path_to_qpainterpath_close_path (void *closure) return CAIRO_STATUS_SUCCESS; } -static inline QPainterPath -path_to_qt (cairo_path_fixed_t *path, +static QPainterPath +path_to_qt (const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse = NULL) { qpainter_path_data data; @@ -381,7 +400,6 @@ path_to_qt (cairo_path_fixed_t *path, data.ctm_inverse = ctm_inverse; status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_path_to_qpainterpath_move_to, _cairo_path_to_qpainterpath_line_to, _cairo_path_to_qpainterpath_curve_to, @@ -393,7 +411,7 @@ path_to_qt (cairo_path_fixed_t *path, } static inline QPainterPath -path_to_qt (cairo_path_fixed_t *path, +path_to_qt (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_matrix_t *ctm_inverse = NULL) { @@ -406,9 +424,9 @@ path_to_qt (cairo_path_fixed_t *path, return qpath; } -/** - ** Surface backend methods - **/ +/* + * Surface backend methods + */ static cairo_surface_t * _cairo_qt_surface_create_similar (void *abstract_surface, cairo_content_t content, @@ -463,7 +481,7 @@ _cairo_qt_surface_finish (void *abstract_surface) /* Only delete p if we created it */ if (qs->image || qs->pixmap) delete qs->p; - else if (qs->p) + else qs->p->restore (); if (qs->image_equiv) @@ -543,31 +561,104 @@ _cairo_qt_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } -static cairo_status_t -_cairo_qt_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, - void **image_extra) +struct _qimage_surface { + cairo_image_surface_t image; + QImage *qimg; +}; + +static cairo_surface_t * +map_qimage_to_image (QImage *qimg, const cairo_rectangle_int_t *extents) +{ + struct _qimage_surface *surface; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + uint8_t *data; + + if (qimg == NULL) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + switch (qimg->format()) { + case QImage::Format_ARGB32_Premultiplied: + pixman_format = PIXMAN_a8r8g8b8; + break; + case QImage::Format_RGB32: + pixman_format = PIXMAN_x8r8g8b8; + break; + case QImage::Format_Indexed8: // XXX not quite + pixman_format = PIXMAN_a8; + break; +#ifdef WORDS_BIGENDIAN + case QImage::Format_Mono: +#else + case QImage::Format_MonoLSB: +#endif + pixman_format = PIXMAN_a1; + break; + + case QImage::Format_Invalid: +#ifdef WORDS_BIGENDIAN + case QImage::Format_MonoLSB: +#else + case QImage::Format_Mono: +#endif + case QImage::Format_ARGB32: + case QImage::Format_RGB16: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_RGB666: + case QImage::Format_ARGB6666_Premultiplied: + case QImage::Format_RGB555: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_RGB888: + case QImage::Format_RGB444: + case QImage::Format_ARGB4444_Premultiplied: + case QImage::NImageFormats: + default: + delete qimg; + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT); + } + + data = qimg->bits(); + data += extents->y * qimg->bytesPerLine(); + data += extents->x * PIXMAN_FORMAT_BPP (pixman_format) / 8; + + pixman_image = pixman_image_create_bits (pixman_format, + extents->width, + extents->height, + (uint32_t *)data, + qimg->bytesPerLine()); + if (pixman_image == NULL) { + delete qimg; + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + surface = (struct _qimage_surface *) _cairo_malloc (sizeof(*surface)); + if (unlikely (surface == NULL)) { + pixman_image_unref (pixman_image); + delete qimg; + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_image_surface_init (&surface->image, pixman_image, pixman_format); + surface->qimg = qimg; + + cairo_surface_set_device_offset (&surface->image.base, + -extents->x, -extents->y); + + return &surface->image.base; +} + +static cairo_image_surface_t * +_cairo_qt_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; QImage *qimg = NULL; D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface)); - *image_extra = NULL; - - if (qs->image_equiv) { - *image_out = (cairo_image_surface_t*) - cairo_surface_reference (qs->image_equiv); - - image_rect->x = qs->window.x(); - image_rect->y = qs->window.y(); - image_rect->width = qs->window.width(); - image_rect->height = qs->window.height(); - - return CAIRO_STATUS_SUCCESS; - } + if (qs->image_equiv) + return _cairo_image_surface_map_to_image (qs->image_equiv, + extents); QPoint offset; @@ -578,7 +669,7 @@ _cairo_qt_surface_acquire_dest_image (void *abstract_surface, // how we can grab an image from it QPaintDevice *pd = qs->p->device(); if (!pd) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); QPaintDevice *rpd = QPainter::redirected(pd, &offset); if (rpd) @@ -588,76 +679,47 @@ _cairo_qt_surface_acquire_dest_image (void *abstract_surface, qimg = new QImage(((QImage*) pd)->copy()); } else if (pd->devType() == QInternal::Pixmap) { qimg = new QImage(((QPixmap*) pd)->toImage()); + } else if (pd->devType() == QInternal::Widget) { + qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage()); } } - if (qimg == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - *image_out = (cairo_image_surface_t*) - cairo_image_surface_create_for_data (qimg->bits(), - _cairo_format_from_qimage_format (qimg->format()), - qimg->width(), qimg->height(), - qimg->bytesPerLine()); - *image_extra = qimg; - - image_rect->x = qs->window.x() + offset.x(); - image_rect->y = qs->window.y() + offset.y(); - image_rect->width = qs->window.width() - offset.x(); - image_rect->height = qs->window.height() - offset.y(); - - return CAIRO_STATUS_SUCCESS; + return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents); } -static void -_cairo_qt_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) +static cairo_int_status_t +_cairo_qt_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface)); - cairo_surface_destroy (&image->base); - - if (image_extra) { - QImage *qimg = (QImage*) image_extra; + if (!qs->image_equiv) { + struct _qimage_surface *qimage = (struct _qimage_surface *)image; // XXX should I be using setBackgroundMode here instead of setCompositionMode? if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_Source); - qs->p->drawImage (image_rect->x, image_rect->y, *qimg); + qs->p->drawImage ((int)qimage->image.base.device_transform.x0, + (int)qimage->image.base.device_transform.y0, + *qimage->qimg, + (int)qimage->image.base.device_transform.x0, + (int)qimage->image.base.device_transform.y0, + (int)qimage->image.width, + (int)qimage->image.height); if (qs->supports_porter_duff) qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - delete qimg; - } -} - -static cairo_status_t -_cairo_qt_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_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - if (src->backend == qs->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - return CAIRO_STATUS_SUCCESS; + delete qimage->qimg; } - return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED; + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return CAIRO_INT_STATUS_SUCCESS; } static cairo_bool_t @@ -668,7 +730,7 @@ _cairo_qt_surface_get_extents (void *abstract_surface, extents->x = qs->window.x(); extents->y = qs->window.y(); - extents->width = qs->window.width(); + extents->width = qs->window.width(); extents->height = qs->window.height(); return TRUE; @@ -703,7 +765,7 @@ _cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, static void _cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, - cairo_region_t *clip_region) + const cairo_region_t *clip_region) { _cairo_surface_clipper_reset (&qs->clipper); @@ -725,7 +787,7 @@ _cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, cairo_region_get_rectangle (clip_region, i, &rect); QRect r(rect.x, rect.y, rect.width, rect.height); - qr = qr.united(r); + qr = qr.unite(r); } qs->p->setClipRegion (qr, Qt::IntersectClip); @@ -734,11 +796,11 @@ _cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, static cairo_int_status_t _cairo_qt_surface_set_clip (cairo_qt_surface_t *qs, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_int_status_t status; - D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", qs, clip ? "(path)" : "(clear)")); + D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)")); if (clip == NULL) { _cairo_surface_clipper_reset (&qs->clipper); @@ -784,12 +846,13 @@ _cairo_qt_surface_set_clip (cairo_qt_surface_t *qs, return status; } -/** - ** Brush conversion - **/ +/* + * Brush conversion + */ struct PatternToBrushConverter { - PatternToBrushConverter (const cairo_pattern_t *pattern) : + PatternToBrushConverter (const cairo_pattern_t *pattern) + __attribute__ ((noinline)) : mAcquiredImageParent(0), mAcquiredImage(0), mAcquiredImageExtra(0) @@ -859,40 +922,34 @@ struct PatternToBrushConverter { if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern; - grad = new QLinearGradient (_cairo_fixed_to_double (lpat->p1.x), - _cairo_fixed_to_double (lpat->p1.y), - _cairo_fixed_to_double (lpat->p2.x), - _cairo_fixed_to_double (lpat->p2.y)); + grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y, + lpat->pd2.x, lpat->pd2.y); } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern; /* Based on the SVG surface code */ - cairo_point_t *c0, *c1; - cairo_fixed_t radius0, radius1; + cairo_circle_double_t *c0, *c1; + double x0, y0, r0, x1, y1, r1; - if (rpat->r1 < rpat->r2) { - c0 = &rpat->c1; - c1 = &rpat->c2; - radius0 = rpat->r1; - radius1 = rpat->r2; + if (rpat->cd1.radius < rpat->cd2.radius) { + c0 = &rpat->cd1; + c1 = &rpat->cd2; reverse_stops = FALSE; } else { - c0 = &rpat->c2; - c1 = &rpat->c1; - radius0 = rpat->r2; - radius1 = rpat->r1; + c0 = &rpat->cd2; + c1 = &rpat->cd1; reverse_stops = TRUE; } - double x0 = _cairo_fixed_to_double (c0->x); - double y0 = _cairo_fixed_to_double (c0->y); - double r0 = _cairo_fixed_to_double (radius0); - double x1 = _cairo_fixed_to_double (c1->x); - double y1 = _cairo_fixed_to_double (c1->y); - double r1 = _cairo_fixed_to_double (radius1); + x0 = c0->center.x; + y0 = c0->center.y; + r0 = c0->radius; + x1 = c1->center.x; + y1 = c1->center.y; + r1 = c1->radius; - if (rpat->r1 == rpat->r2) { + if (r0 == r1) { grad = new QRadialGradient (x1, y1, r1, x1, y1); } else { double fx = (r1 * x0 - r0 * x1) / (r1 - r0); @@ -994,7 +1051,7 @@ struct PatternToBrushConverter { } } - ~PatternToBrushConverter () { + ~PatternToBrushConverter () __attribute__ ((noinline)){ if (mAcquiredImageParent) _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra); } @@ -1097,14 +1154,14 @@ struct PatternToPenConverter { PatternToBrushConverter mBrushConverter; }; -/** - ** Core drawing operations - **/ +/* + * Core drawing operations + */ static bool _cairo_qt_fast_fill (cairo_qt_surface_t *qs, const cairo_pattern_t *source, - cairo_path_fixed_t *path = NULL, + const cairo_path_fixed_t *path = NULL, cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING, double tolerance = 0.0, cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE) @@ -1159,7 +1216,7 @@ _cairo_qt_fast_fill (cairo_qt_surface_t *qs, cairo_clip_t clip, old_clip = qs->clipper.clip; - _cairo_clip_init_copy (&clip, &qs->clipper.clip); + qs->clipper.clip = _cairo_clip_copy (&clip); status = (cairo_int_status_t) _cairo_clip_clip (&clip, path, fill_rule, @@ -1220,18 +1277,15 @@ static cairo_int_status_t _cairo_qt_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; cairo_int_status_t status; D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op))); - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return _cairo_surface_fallback_paint (abstract_surface, op, source, clip); status = _cairo_qt_surface_set_clip (qs, clip); if (unlikely (status)) @@ -1255,21 +1309,20 @@ static cairo_int_status_t _cairo_qt_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op))); - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return _cairo_surface_fallback_fill (abstract_surface, op, + source, path, fill_rule, + tolerance, antialias, clip); cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip); if (unlikely (status)) @@ -1300,29 +1353,28 @@ static cairo_int_status_t _cairo_qt_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op))); - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return _cairo_surface_fallback_stroke (abstract_surface, op, + source, path, style, ctm, + ctm_inverse, tolerance, + antialias, clip); cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip); if (unlikely (int_status)) return int_status; - QMatrix savedMatrix = qs->p->worldMatrix(); if (qs->supports_porter_duff) @@ -1355,10 +1407,44 @@ _cairo_qt_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { - return CAIRO_INT_STATUS_UNSUPPORTED; +#if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0 + cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + // pick out the colour to use from the cairo source + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source; + cairo_scaled_glyph_t* glyph; + // documentation says you have to freeze the cache, but I don't believe it + _cairo_scaled_font_freeze_cache(scaled_font); + + QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255); + QVarLengthArray positions(num_glyphs); + QVarLengthArray glyphss(num_glyphs); + FT_Face face = cairo_ft_scaled_font_lock_face (scaled_font); + const FT_Size_Metrics& ftMetrics = face->size->metrics; + QFont font(face->family_name); + font.setStyleStrategy(QFont::NoFontMerging); + font.setBold(face->style_flags & FT_STYLE_FLAG_BOLD); + font.setItalic(face->style_flags & FT_STYLE_FLAG_ITALIC); + font.setKerning(face->face_flags & FT_FACE_FLAG_KERNING); + font.setPixelSize(ftMetrics.y_ppem); + cairo_ft_scaled_font_unlock_face(scaled_font); + qs->p->setFont(font); + qs->p->setPen(tempColour); + for (int currentGlyph = 0; currentGlyph < num_glyphs; currentGlyph++) { + positions[currentGlyph].setX(glyphs[currentGlyph].x); + positions[currentGlyph].setY(glyphs[currentGlyph].y); + glyphss[currentGlyph] = glyphs[currentGlyph].index; + } + qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs); + _cairo_scaled_font_thaw_cache(scaled_font); + return CAIRO_INT_STATUS_SUCCESS; +#else + return _cairo_surface_fallback_glyphs (abstract_surface, op, + source, glyphs, num_glyphs, + scaled_font, clip); +#endif } static cairo_int_status_t @@ -1366,16 +1452,13 @@ _cairo_qt_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op))); - if (!qs->p) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + if (qs->p && mask->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; cairo_int_status_t result; @@ -1389,109 +1472,7 @@ _cairo_qt_surface_mask (void *abstract_surface, } // otherwise skip for now - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_qt_surface_composite (cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_surface, - 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_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; - - if (mask_pattern) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _op_is_supported (qs, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_qt_surface_set_clip_region (qs, clip_region); - - D(fprintf(stderr, "q[%p] composite op:%s src:%p [%d %d] dst [%d %d] dim [%d %d]\n", - abstract_surface, _opstr(op), (void*)pattern, - src_x, src_y, dst_x, dst_y, width, height)); - - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; - - QColor color; - color.setRgbF(solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - qs->p->fillRect (dst_x, dst_y, width, height, color); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; - cairo_surface_t *surface = spattern->surface; - - QImage *qimg = NULL; - QPixmap *qpixmap = NULL; - std::auto_ptr qimg_d; - - if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { - cairo_image_surface_t *isurf = (cairo_image_surface_t*) surface; - qimg = new QImage ((const uchar *) isurf->data, - isurf->width, - isurf->height, - isurf->stride, - _qimage_format_from_cairo_format (isurf->format)); - qimg_d.reset(qimg); - } - - if (surface->type == CAIRO_SURFACE_TYPE_QT) { - cairo_qt_surface_t *qsrc = (cairo_qt_surface_t*) surface; - - if (qsrc->image) - qimg = qsrc->image; - else if (qsrc->pixmap) - qpixmap = qsrc->pixmap; - } - - if (!qimg && !qpixmap) - return CAIRO_INT_STATUS_UNSUPPORTED; - - QMatrix savedMatrix = qs->p->worldMatrix(); - if (! _cairo_matrix_is_identity (&pattern->matrix)) { - cairo_matrix_t pm = pattern->matrix; - cairo_status_t status; - - status = cairo_matrix_invert (&pm); - assert (status == CAIRO_STATUS_SUCCESS); - qs->p->setWorldMatrix(_qmatrix_from_cairo_matrix (pm), true); - } - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); - - if (qimg) - qs->p->drawImage (dst_x, dst_y, *qimg, src_x, src_y, width, height); - else if (qpixmap) - qs->p->drawPixmap (dst_x, dst_y, *qpixmap, src_x, src_y, width, height); - - if (qs->supports_porter_duff) - qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - return CAIRO_INT_STATUS_SUCCESS; + return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip); } static cairo_status_t @@ -1507,48 +1488,41 @@ _cairo_qt_surface_mark_dirty (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -/** - ** Backend struct - **/ +/* + * Backend struct + */ static const cairo_surface_backend_t cairo_qt_surface_backend = { CAIRO_SURFACE_TYPE_QT, - _cairo_qt_surface_create_similar, _cairo_qt_surface_finish, + + _cairo_default_context_create, /* XXX */ + + _cairo_qt_surface_create_similar, + NULL, /* similar image */ + _cairo_qt_surface_map_to_image, + _cairo_qt_surface_unmap_image, + + _cairo_surface_default_source, _cairo_qt_surface_acquire_source_image, _cairo_qt_surface_release_source_image, - _cairo_qt_surface_acquire_dest_image, - _cairo_qt_surface_release_dest_image, - _cairo_qt_surface_clone_similar, + NULL, /* snapshot */ - _cairo_qt_surface_composite, - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ NULL, /* copy_page */ NULL, /* show_page */ + _cairo_qt_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ _cairo_qt_surface_mark_dirty, - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ _cairo_qt_surface_paint, _cairo_qt_surface_mask, _cairo_qt_surface_stroke, _cairo_qt_surface_fill, - _cairo_qt_surface_show_glyphs, - - NULL, /* snapshot */ - NULL, /* is_similar */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - NULL, /* has_show_text_glyphs */ - NULL, /* show_text_glyphs */ + _cairo_qt_surface_show_glyphs }; cairo_surface_t * @@ -1556,7 +1530,7 @@ cairo_qt_surface_create (QPainter *painter) { cairo_qt_surface_t *qs; - qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); + qs = (cairo_qt_surface_t *) _cairo_malloc (sizeof(cairo_qt_surface_t)); if (qs == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -1564,8 +1538,9 @@ cairo_qt_surface_create (QPainter *painter) _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, - NULL, - CAIRO_CONTENT_COLOR_ALPHA); + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + FALSE); /* is_vector */ _cairo_surface_clipper_init (&qs->clipper, _cairo_qt_surface_clipper_intersect_clip_path); @@ -1595,7 +1570,7 @@ cairo_qt_surface_create_with_qimage (cairo_format_t format, { cairo_qt_surface_t *qs; - qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); + qs = (cairo_qt_surface_t *) _cairo_malloc (sizeof(cairo_qt_surface_t)); if (qs == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -1603,21 +1578,13 @@ cairo_qt_surface_create_with_qimage (cairo_format_t format, _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, - NULL, - _cairo_content_from_format (format)); + NULL, /* device */ + _cairo_content_from_format (format), + FALSE); /* is_vector */ _cairo_surface_clipper_init (&qs->clipper, _cairo_qt_surface_clipper_intersect_clip_path); - if (CAIRO_FORMAT_A8 == format) { - qs->image = NULL; - qs->image_equiv = cairo_image_surface_create(format, - width, height); - qs->p = NULL; - qs->supports_porter_duff = false; - qs->window = QRect(0, 0, width, height); - return &qs->base; - } QImage *image = new QImage (width, height, _qimage_format_from_cairo_format (format)); @@ -1653,7 +1620,7 @@ cairo_qt_surface_create_with_qpixmap (cairo_content_t content, if ((content & CAIRO_CONTENT_COLOR) == 0) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); - qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); + qs = (cairo_qt_surface_t *) _cairo_malloc (sizeof(cairo_qt_surface_t)); if (qs == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -1671,7 +1638,11 @@ cairo_qt_surface_create_with_qpixmap (cairo_content_t content, if (content == CAIRO_CONTENT_COLOR_ALPHA) pixmap->fill(Qt::transparent); - _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, NULL, content); + _cairo_surface_init (&qs->base, + &cairo_qt_surface_backend, + NULL, /* device */ + content, + FALSE); /* is_vector */ _cairo_surface_clipper_init (&qs->clipper, _cairo_qt_surface_clipper_intersect_clip_path); @@ -1692,13 +1663,30 @@ cairo_qt_surface_create_with_qpixmap (cairo_content_t content, return &qs->base; } +/** + * _cairo_surface_is_qt: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_qt_surface_t + * + * Return value: True if the surface is an qt surface + **/ +static inline cairo_bool_t +_cairo_surface_is_qt (cairo_surface_t *surface) +{ + return surface->backend == &cairo_qt_surface_backend; +} + QPainter * cairo_qt_surface_get_qpainter (cairo_surface_t *surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - if (surface->type != CAIRO_SURFACE_TYPE_QT) + /* Throw an error for a non-qt surface */ + if (! _cairo_surface_is_qt (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; + } return qs->p; } @@ -1708,8 +1696,11 @@ cairo_qt_surface_get_qimage (cairo_surface_t *surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - if (surface->type != CAIRO_SURFACE_TYPE_QT) + /* Throw an error for a non-qt surface */ + if (! _cairo_surface_is_qt (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return NULL; + } return qs->image; } @@ -1719,8 +1710,10 @@ cairo_qt_surface_get_image (cairo_surface_t *surface) { cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; - if (surface->type != CAIRO_SURFACE_TYPE_QT) - return NULL; + /* Throw an error for a non-qt surface */ + if (! _cairo_surface_is_qt (surface)) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + } return qs->image_equiv; } @@ -1730,9 +1723,9 @@ cairo_qt_surface_get_image (cairo_surface_t *surface) * * - Figure out why QBrush isn't working with non-repeated images * - * - Correct repeat mode; right now, every surface source is ExtendMode::REPEAT + * - Correct repeat mode; right now, every surface source is EXTEND_REPEAT * - implement EXTEND_NONE (?? probably need to clip to the extents of the source) - * - implement ExtendMode::REFLECT (create temporary and copy 4x, then ExtendMode::REPEAT that) + * - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that) * * - stroke-image failure * diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c index a6686bb2702f..8cb71434f89e 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-font.c +++ b/gfx/cairo/cairo/src/cairo-quartz-font.c @@ -38,6 +38,7 @@ #include +#include "cairo-image-surface-private.h" #include "cairo-quartz.h" #include "cairo-quartz-private.h" @@ -52,14 +53,18 @@ * The Quartz font backend is primarily used to render text on Apple * MacOS X systems. The CGFont API is used for the internal * implementation of the font backend methods. - */ + **/ /** * CAIRO_HAS_QUARTZ_FONT: * * Defined if the Quartz font backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.6 + **/ + +static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL; /* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */ static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL; @@ -76,6 +81,14 @@ static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; +/* Not public in the least bit */ +static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL; + +/* CTFontCreateWithGraphicsFont is not available until 10.5 */ +typedef const struct __CTFontDescriptor *CTFontDescriptorRef; +static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform*, CTFontDescriptorRef) = NULL; +static CGPathRef (*CTFontCreatePathForGlyphPtr) (CTFontRef, CGGlyph, CGAffineTransform *) = NULL; + /* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ typedef struct { int ascent; @@ -87,19 +100,24 @@ static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; -/* CTFontCreateWithGraphicsFont is not public until 10.5. */ -typedef const struct __CTFontDescriptor *CTFontDescriptorRef; -static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL; +/* Not public anymore in 64-bits nor in 10.7 */ +static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; +/* Defined in 10.11 */ +#define CGGLYPH_MAX ((CGGlyph) 0xFFFE) /* kCGFontIndexMax */ +#define CGGLYPH_INVALID ((CGGlyph) 0xFFFF) /* kCGFontIndexInvalid */ + static void quartz_font_ensure_symbols(void) { if (_cairo_quartz_font_symbol_lookup_done) return; + CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag"); + /* Look for the 10.5 versions first */ CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes"); if (!CGFontGetGlyphBBoxesPtr) @@ -119,6 +137,11 @@ quartz_font_ensure_symbols(void) CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); + CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph"); + if (!CTFontCreateWithGraphicsFontPtr || !CTFontCreatePathForGlyphPtr) + CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); + CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); @@ -127,13 +150,14 @@ quartz_font_ensure_symbols(void) CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); + FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && CGFontGetGlyphBBoxesPtr && CGFontGetGlyphsForUnicharsPtr && CGFontGetUnitsPerEmPtr && CGFontGetGlyphAdvancesPtr && + ((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) && (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) _cairo_quartz_font_symbols_present = TRUE; @@ -151,7 +175,6 @@ struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; - CTFontRef ctFont; }; /* @@ -173,7 +196,7 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, return _cairo_error (CAIRO_STATUS_NO_MEMORY); family = toy_face->family; - full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. + full_name = _cairo_malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. /* handle CSS-ish faces */ if (!strcmp(family, "serif") || !strcmp(family, "Times Roman")) family = "Times"; @@ -231,16 +254,13 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, return CAIRO_STATUS_SUCCESS; } -static void +static cairo_bool_t _cairo_quartz_font_face_destroy (void *abstract_face) { cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; - if (font_face->ctFont) { - CFRelease (font_face->ctFont); - } - CGFontRelease (font_face->cgFont); + return TRUE; } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; @@ -263,7 +283,7 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, if (!_cairo_quartz_font_symbols_present) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font = malloc(sizeof(cairo_quartz_scaled_font_t)); + font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t)); if (font == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -335,68 +355,8 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { _cairo_quartz_font_face_scaled_font_create }; -// Helper to create a CTFont from a CGFont, copying any variations that were -// set on the original CGFont. -static CTFontRef -CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize) -{ - // Declare helper provided by widget/cocoa/nsCocoaFeatures.mm - extern bool Gecko_OnSierraExactly(); - - // Avoid calling potentially buggy variation APIs on pre-Sierra macOS - // versions (see bug 1331683). - // - // And on HighSierra, CTFontCreateWithGraphicsFont properly carries over - // variation settings from the CGFont to CTFont, so we don't need to do - // the extra work here -- and this seems to avoid Core Text crashiness - // seen in bug 1454094. - // - // So we only need to do this "the hard way" on Sierra; on other releases, - // just let the standard CTFont function do its thing. - // - // NOTE in case this ever needs further adjustment: there is similar logic - // in four places in the tree (sadly): - // CreateCTFontFromCGFontWithVariations in gfxMacFont.cpp - // CreateCTFontFromCGFontWithVariations in ScaledFontMac.cpp - // CreateCTFontFromCGFontWithVariations in cairo-quartz-font.c - // ctfont_create_exact_copy in SkFontHost_mac.cpp - // - // XXX Does this need to behave differently for installed fonts on High - // Sierra, as in other similar places (bug 1455569)? - - CFStringRef name = CGFontCopyPostScriptName(aCGFont); - // CTFontCreateWithGraphicsFont seems to give "LastResort" - // when used on a system CGFont with variation applied - bool system_font = CFStringHasPrefix(name, CFSTR(".")); - - if (!Gecko_OnSierraExactly() && !system_font) { - return CTFontCreateWithGraphicsFont(aCGFont, aSize, NULL, NULL); - } - - CFDictionaryRef vars = CGFontCopyVariations(aCGFont); - CTFontRef ctFont; - if (vars) { - CFDictionaryRef varAttr = - CFDictionaryCreate(NULL, - (const void**)&kCTFontVariationAttribute, - (const void**)&vars, 1, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFRelease(vars); - - CTFontDescriptorRef varDesc = CTFontDescriptorCreateWithAttributes(varAttr); - CFRelease(varAttr); - - ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, NULL, varDesc); - CFRelease(varDesc); - } else { - ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, NULL, NULL); - } - return ctFont; -} - /** - * cairo_quartz_font_face_create_for_cgfont + * cairo_quartz_font_face_create_for_cgfont: * @font: a #CGFontRef obtained through a method external to cairo. * * Creates a new font for the Quartz font backend based on a @@ -407,7 +367,7 @@ CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize) * cairo_font_face_destroy() when you are done using it. * * Since: 1.6 - */ + **/ cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { @@ -415,7 +375,7 @@ cairo_quartz_font_face_create_for_cgfont (CGFontRef font) quartz_font_ensure_symbols(); - font_face = malloc (sizeof (cairo_quartz_font_face_t)); + font_face = _cairo_malloc (sizeof (cairo_quartz_font_face_t)); if (!font_face) { cairo_status_t ignore_status; ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -424,12 +384,6 @@ cairo_quartz_font_face_create_for_cgfont (CGFontRef font) font_face->cgFont = CGFontRetain (font); - if (CTFontCreateWithGraphicsFontPtr) { - font_face->ctFont = CreateCTFontFromCGFontWithVariations (font, 1.0); - } else { - font_face->ctFont = NULL; - } - _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); return &font_face->base; @@ -453,14 +407,10 @@ _cairo_quartz_scaled_font_fini(void *abstract_font) { } -#define INVALID_GLYPH 0x00 - static inline CGGlyph _cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) { unsigned long index = _cairo_scaled_glyph_index (scaled_glyph); - if (index > 0xffff) - return INVALID_GLYPH; - return (CGGlyph) index; + return index <= CGGLYPH_MAX ? index : CGGLYPH_INVALID; } static cairo_int_status_t @@ -475,10 +425,9 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, int advance; CGRect bbox; double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); - double xscale, yscale; double xmin, ymin, xmax, ymax; - if (glyph == INVALID_GLYPH) + if (unlikely (glyph == CGGLYPH_INVALID)) goto FAIL; if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || @@ -495,11 +444,6 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, bbox.size.width = bbox.size.height = 0; } - status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale, - &xscale, &yscale, 1); - if (status) - goto FAIL; - bbox = CGRectMake (bbox.origin.x / emscale, bbox.origin.y / emscale, bbox.size.width / emscale, @@ -615,10 +559,9 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; CGPathRef glyphPath; - CTFontRef ctFont; cairo_path_fixed_t *path; - if (glyph == INVALID_GLYPH) { + if (unlikely (glyph == CGGLYPH_INVALID)) { _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create()); return CAIRO_STATUS_SUCCESS; } @@ -630,9 +573,14 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - ctFont = CreateCTFontFromCGFontWithVariations (font_face->cgFont, 1.0); - glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); - CFRelease (ctFont); + if (CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) { + CTFontRef ctFont = CTFontCreateWithGraphicsFontPtr (font_face->cgFont, 1.0, NULL, NULL); + glyphPath = CTFontCreatePathForGlyphPtr (ctFont, glyph, &textMatrix); + CFRelease (ctFont); + } else { + glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); + } + if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -666,7 +614,6 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, int advance; CGRect bbox; double width, height; - double xscale, yscale; double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); CGContextRef cgContext = NULL; @@ -680,7 +627,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, * Maybe we should draw a better missing-glyph slug or something, * but this is ok for now. */ - if (glyph == INVALID_GLYPH) { + if (unlikely (glyph == CGGLYPH_INVALID)) { surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2); status = cairo_surface_status ((cairo_surface_t *) surface); if (status) @@ -698,11 +645,6 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_INT_STATUS_UNSUPPORTED; } - status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale, - &xscale, &yscale, 1); - if (status) - return status; - /* scale(1,-1) * font->base.scale * scale(1,-1) */ textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, @@ -761,6 +703,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, switch (font->base.options.antialias) { case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: CGContextSetShouldAntialias (cgContext, TRUE); CGContextSetShouldSmoothFonts (cgContext, TRUE); if (CGContextSetAllowsFontSmoothingPtr && @@ -771,6 +714,8 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, CGContextSetShouldAntialias (cgContext, FALSE); break; case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: CGContextSetShouldAntialias (cgContext, TRUE); CGContextSetShouldSmoothFonts (cgContext, FALSE); break; @@ -821,12 +766,46 @@ _cairo_quartz_ucs4_to_index (void *abstract_font, { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); - UniChar u = (UniChar) ucs4; - CGGlyph glyph; + CGGlyph glyph[2]; + UniChar utf16[2]; - CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1); + int len = _cairo_ucs4_to_utf16 (ucs4, utf16); + CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len); - return glyph; + return glyph[0]; +} + +static cairo_int_status_t +_cairo_quartz_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font); + CFDataRef data = NULL; + + if (likely (CGFontCopyTableForTagPtr)) + data = CGFontCopyTableForTagPtr (font_face->cgFont, tag); + + if (!data) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (buffer == NULL) { + *length = CFDataGetLength (data); + CFRelease (data); + return CAIRO_STATUS_SUCCESS; + } + + if (CFDataGetLength (data) < offset + (long) *length) { + CFRelease (data); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + CFDataGetBytes (data, CFRangeMake (offset, *length), buffer); + CFRelease (data); + + return CAIRO_STATUS_SUCCESS; } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { @@ -835,8 +814,7 @@ static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { _cairo_quartz_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_quartz_ucs4_to_index, - NULL, /* show_glyphs */ - NULL, /* load_truetype_table */ + _cairo_quartz_load_truetype_table, NULL, /* map_glyphs_to_unicode */ }; @@ -852,21 +830,12 @@ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) return ffont->cgFont; } -CTFontRef -_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font) -{ - cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); - - return ffont->ctFont; -} - -#if !defined(__LP64__) && !TARGET_OS_IPHONE /* * compat with old ATSUI backend */ /** - * cairo_quartz_font_face_create_for_atsu_font_id + * cairo_quartz_font_face_create_for_atsu_font_id: * @font_id: an ATSUFontID for the font. * * Creates a new font for the Quartz font backend based on an @@ -881,15 +850,22 @@ _cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font) cairo_font_face_t * cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id) { - ATSFontRef atsFont = FMGetATSFontRefFromFont (font_id); - CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont); - cairo_font_face_t *ff; + quartz_font_ensure_symbols(); - ff = cairo_quartz_font_face_create_for_cgfont (cgFont); + if (FMGetATSFontRefFromFontPtr != NULL) { + ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id); + CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont); + cairo_font_face_t *ff; - CGFontRelease (cgFont); + ff = cairo_quartz_font_face_create_for_cgfont (cgFont); - return ff; + CGFontRelease (cgFont); + + return ff; + } else { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } } /* This is the old name for the above function, exported for compat purposes */ @@ -900,4 +876,3 @@ cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id) { return cairo_quartz_font_face_create_for_atsu_font_id (font_id); } -#endif diff --git a/gfx/cairo/cairo/src/cairo-quartz-image-surface.c b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c index 9a18dd46e5d2..30d92d6beda9 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-image-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c @@ -36,10 +36,13 @@ #include "cairoint.h" +#include "cairo-image-surface-inline.h" #include "cairo-quartz-image.h" #include "cairo-quartz-private.h" +#include "cairo-surface-backend-private.h" #include "cairo-error-private.h" +#include "cairo-default-context-private.h" #define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY))) #define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH))) @@ -47,10 +50,9 @@ #define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT))) static void -DataProviderReleaseCallback (void *info, const void *data, size_t size) +DataProviderReleaseCallback (void *image_info, const void *data, size_t size) { - cairo_surface_t *surface = (cairo_surface_t *) info; - cairo_surface_destroy (surface); + free (image_info); } static cairo_surface_t * @@ -59,13 +61,22 @@ _cairo_quartz_image_surface_create_similar (void *asurface, int width, int height) { - cairo_surface_t *result; cairo_surface_t *isurf = _cairo_image_surface_create_with_content (content, width, height); - if (cairo_surface_status(isurf)) - return isurf; + cairo_surface_t *result = cairo_quartz_image_surface_create (isurf); + cairo_surface_destroy (isurf); - result = cairo_quartz_image_surface_create (isurf); + return result; +} + +static cairo_surface_t * +_cairo_quartz_image_surface_create_similar_image (void *asurface, + cairo_format_t format, + int width, + int height) +{ + cairo_surface_t *isurf = cairo_image_surface_create (format, width, height); + cairo_surface_t *result = cairo_quartz_image_surface_create (isurf); cairo_surface_destroy (isurf); return result; @@ -76,9 +87,8 @@ _cairo_quartz_image_surface_finish (void *asurface) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - /* the imageSurface will be destroyed by the data provider's release callback */ CGImageRelease (surface->image); - + cairo_surface_destroy ( (cairo_surface_t*) surface->imageSurface); return CAIRO_STATUS_SUCCESS; } @@ -95,20 +105,20 @@ _cairo_quartz_image_surface_acquire_source_image (void *asurface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_quartz_image_surface_acquire_dest_image (void *asurface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) +static cairo_image_surface_t * +_cairo_quartz_image_surface_map_to_image (void *asurface, + const cairo_rectangle_int_t *extents) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + return _cairo_surface_map_to_image (&surface->imageSurface->base, extents); +} - *image_out = surface->imageSurface; - *image_rect = surface->extents; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; +static cairo_int_status_t +_cairo_quartz_image_surface_unmap_image (void *asurface, + cairo_image_surface_t *image) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + return _cairo_surface_unmap_image (&surface->imageSurface->base, image); } static cairo_bool_t @@ -117,7 +127,10 @@ _cairo_quartz_image_surface_get_extents (void *asurface, { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; - *extents = surface->extents; + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; return TRUE; } @@ -126,71 +139,156 @@ _cairo_quartz_image_surface_get_extents (void *asurface, */ static cairo_status_t -_cairo_quartz_image_surface_flush (void *asurface) +_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; - /* To be released by the ReleaseCallback */ - cairo_surface_reference ((cairo_surface_t*) surface->imageSurface); + /* XXX only flush if the image has been modified. */ - newImage = _cairo_quartz_create_cgimage (surface->imageSurface->format, - surface->imageSurface->width, - surface->imageSurface->height, - surface->imageSurface->stride, - surface->imageSurface->data, - TRUE, - NULL, - DataProviderReleaseCallback, - surface->imageSurface); + 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); - surface->base.is_clear = surface->imageSurface->base.is_clear; - return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_quartz_image_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_quartz_image_surface_t *surface = abstract_surface; + return _cairo_surface_paint (&surface->imageSurface->base, + op, source, clip); +} + +static cairo_int_status_t +_cairo_quartz_image_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_quartz_image_surface_t *surface = abstract_surface; + return _cairo_surface_mask (&surface->imageSurface->base, + op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_quartz_image_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_quartz_image_surface_t *surface = abstract_surface; + return _cairo_surface_stroke (&surface->imageSurface->base, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, clip); +} + +static cairo_int_status_t +_cairo_quartz_image_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_quartz_image_surface_t *surface = abstract_surface; + return _cairo_surface_fill (&surface->imageSurface->base, + op, source, path, + fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_quartz_image_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_quartz_image_surface_t *surface = abstract_surface; + return _cairo_surface_show_text_glyphs (&surface->imageSurface->base, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, clip); +} + + static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, - _cairo_quartz_image_surface_create_similar, _cairo_quartz_image_surface_finish, + + _cairo_default_context_create, + + _cairo_quartz_image_surface_create_similar, + _cairo_quartz_image_surface_create_similar_image, + _cairo_quartz_image_surface_map_to_image, + _cairo_quartz_image_surface_unmap_image, + + _cairo_surface_default_source, _cairo_quartz_image_surface_acquire_source_image, NULL, /* release_source_image */ - _cairo_quartz_image_surface_acquire_dest_image, - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_quartz_image_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + _cairo_quartz_image_surface_flush, NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* surface_show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL /* fill_stroke */ + _cairo_quartz_image_surface_paint, + _cairo_quartz_image_surface_mask, + _cairo_quartz_image_surface_stroke, + _cairo_quartz_image_surface_fill, + NULL, /* fill-stroke */ + _cairo_quartz_image_surface_glyphs, }; /** - * cairo_quartz_image_surface_create - * @surface: a cairo image surface to wrap with a quartz image surface + * 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 * given image surface. The resulting surface can be rendered quickly @@ -202,7 +300,7 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { * Return value: the newly created surface. * * Since: 1.6 - */ + **/ cairo_surface_t * cairo_quartz_image_surface_create (cairo_surface_t *surface) { @@ -213,9 +311,12 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) cairo_image_surface_t *image_surface; int width, height, stride; cairo_format_t format; - unsigned char *data; + void *image_data; - if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) + if (surface->status) + return surface; + + if (! _cairo_surface_is_image (surface)) return SURFACE_ERROR_TYPE_MISMATCH; image_surface = (cairo_image_surface_t*) surface; @@ -223,7 +324,6 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) height = image_surface->height; stride = image_surface->stride; format = image_surface->format; - data = image_surface->data; if (!_cairo_quartz_verify_surface_size(width, height)) return SURFACE_ERROR_INVALID_SIZE; @@ -234,26 +334,27 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24) return SURFACE_ERROR_INVALID_FORMAT; - qisurf = malloc(sizeof(cairo_quartz_image_surface_t)); + qisurf = _cairo_malloc (sizeof(cairo_quartz_image_surface_t)); if (qisurf == NULL) return SURFACE_ERROR_NO_MEMORY; memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t)); - /* In case the create_cgimage fails, this ref will - * be released via the callback (which will be called in - * case of failure.) - */ - cairo_surface_reference (surface); + image_data = _cairo_malloc_ab (height, stride); + if (unlikely (!image_data)) { + free(qisurf); + return SURFACE_ERROR_NO_MEMORY; + } - image = _cairo_quartz_create_cgimage (format, - width, height, - stride, - data, - TRUE, - NULL, - DataProviderReleaseCallback, - image_surface); + 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); @@ -263,16 +364,14 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) _cairo_surface_init (&qisurf->base, &cairo_quartz_image_surface_backend, NULL, /* device */ - _cairo_content_from_format (format)); + _cairo_content_from_format (format), + FALSE); /* is_vector */ - qisurf->extents.x = qisurf->extents.y = 0; - qisurf->extents.width = width; - qisurf->extents.height = height; + qisurf->width = width; + qisurf->height = height; qisurf->image = image; - qisurf->imageSurface = image_surface; - - qisurf->base.is_clear = image_surface->base.is_clear; + qisurf->imageSurface = (cairo_image_surface_t*) cairo_surface_reference(surface); return &qisurf->base; } @@ -283,8 +382,10 @@ cairo_quartz_image_surface_get_image (cairo_surface_t *asurface) { cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface; - if (cairo_surface_get_type(asurface) != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) - return NULL; + /* Throw an error for a non-quartz surface */ + if (! _cairo_surface_is_quartz (asurface)) { + return SURFACE_ERROR_TYPE_MISMATCH; + } return (cairo_surface_t*) surface->imageSurface; } diff --git a/gfx/cairo/cairo/src/cairo-quartz-image.h b/gfx/cairo/cairo/src/cairo-quartz-image.h index 2d6e8fb52725..0dd5abb4fd2d 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-image.h +++ b/gfx/cairo/cairo/src/cairo-quartz-image.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright 2008 Mozilla Corporation + * Copyright © 2008 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -39,13 +39,8 @@ #include "cairo.h" #if CAIRO_HAS_QUARTZ_IMAGE_SURFACE -#include "TargetConditionals.h" -#if !TARGET_OS_IPHONE #include -#else -#include -#endif CAIRO_BEGIN_DECLS diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h index 1ca47a9f5cd6..42e1f9e91616 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-private.h +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h @@ -44,12 +44,20 @@ #include "cairo-quartz.h" #include "cairo-surface-clipper-private.h" -#ifdef CGFLOAT_DEFINED -typedef CGFloat cairo_quartz_float_t; -#else -typedef float cairo_quartz_float_t; +#ifndef CGFLOAT_DEFINED +/* On 10.4, Quartz APIs used float instead of CGFloat */ +typedef float CGFloat; #endif +typedef CGFloat cairo_quartz_float_t; + +typedef enum { + DO_DIRECT, + DO_SHADING, + DO_IMAGE, + DO_TILED_IMAGE +} cairo_quartz_action_t; + /* define CTFontRef for pre-10.5 SDKs */ typedef const struct __CTFont *CTFontRef; @@ -63,33 +71,27 @@ typedef struct cairo_quartz_surface { cairo_surface_t *imageSurfaceEquiv; cairo_surface_clipper_t clipper; - - /** - * If non-null, this is a CGImage representing the contents of the surface. - * We clear this out before any painting into the surface, so that we - * don't force a copy to be created. - */ - CGImageRef bitmapContextImage; - cairo_rectangle_int_t extents; - - cairo_bool_t ownsData; + cairo_rectangle_int_t virtual_extents; } cairo_quartz_surface_t; typedef struct cairo_quartz_image_surface { cairo_surface_t base; - cairo_rectangle_int_t extents; + int width, height; CGImageRef image; cairo_image_surface_t *imageSurface; } cairo_quartz_image_surface_t; -cairo_bool_t +cairo_private cairo_bool_t _cairo_quartz_verify_surface_size(int width, int height); -CGImageRef -_cairo_quartz_create_cgimage (cairo_format_t format, +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, @@ -99,12 +101,9 @@ _cairo_quartz_create_cgimage (cairo_format_t format, CGDataProviderReleaseDataCallback releaseCallback, void *releaseInfo); -CGFontRef +cairo_private CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); -CTFontRef -_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont); - #else # error Cairo was not compiled with support for the quartz backend diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c index fa7dd7ff4711..7a9f52401bdb 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -34,17 +34,20 @@ * Vladimir Vukicevic */ -#ifndef _GNU_SOURCE #define _GNU_SOURCE /* required for RTLD_DEFAULT */ -#endif #include "cairoint.h" #include "cairo-quartz-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-pattern-private.h" +#include "cairo-surface-backend-private.h" #include "cairo-surface-clipper-private.h" -#include "cairo-gstate-private.h" -#include "cairo-private.h" +#include "cairo-recording-surface-private.h" #include @@ -72,20 +75,18 @@ * * The Quartz surface is used to render cairo graphics targeting the * Apple OS X Quartz rendering system. - */ + **/ /** * CAIRO_HAS_QUARTZ_SURFACE: * * Defined if the Quartz surface backend is available. * This macro can be used to conditionally compile backend-specific code. - */ - -/* Here are some of the differences between cairo and CoreGraphics - - cairo has only a single source active at once vs. CoreGraphics having - separate sources for stroke and fill -*/ + * + * Since: 1.6 + **/ +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 /* This method is private, but it exists. Its params are are exposed * as args to the NS* method, but not as CG. */ @@ -106,39 +107,21 @@ enum PrivateCGCompositeMode { }; typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); -CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform); - -/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9 - * has all the stuff we care about, just some of it isn't exported in the SDK. - */ -#ifndef kCGBitmapByteOrder32Host -#define USE_10_3_WORKAROUNDS -#define kCGBitmapAlphaInfoMask 0x1F -#define kCGBitmapByteOrderMask 0x7000 -#define kCGBitmapByteOrder32Host 0 - -typedef uint32_t CGBitmapInfo; - -/* public in 10.4, present in 10.3.9 */ -CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef); -CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef); #endif /* Some of these are present in earlier versions of the OS than where - * they are public; others are not public at all (CGContextCopyPath, - * CGContextReplacePathWithClipPath, many of the getters, etc.) + * they are public; other are not public at all */ -static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL; +/* public since 10.5 */ static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; -static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; -static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; -static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; -static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; -static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; -/* CTFontDrawGlyphs is not available until 10.7 */ -static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL; +/* public since 10.6 */ +static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; +static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; + +/* not yet public */ +static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; +static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; @@ -157,39 +140,31 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, unsigned int width, unsigned int height); -static cairo_bool_t -_cairo_surface_is_quartz (const cairo_surface_t *surface); - /* Load all extra symbols */ -static void quartz_ensure_symbols(void) +static void quartz_ensure_symbols (void) { - if (_cairo_quartz_symbol_lookup_done) + if (likely (_cairo_quartz_symbol_lookup_done)) return; - CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask"); - CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage"); - CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType"); - CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts"); - CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath"); - CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); - - CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); + CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage"); + CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType"); + CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath"); + CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); _cairo_quartz_symbol_lookup_done = TRUE; } CGImageRef -_cairo_quartz_create_cgimage (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) +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) { CGImageRef image = NULL; CGDataProviderRef dataProvider = NULL; @@ -200,7 +175,7 @@ _cairo_quartz_create_cgimage (cairo_format_t format, switch (format) { case CAIRO_FORMAT_ARGB32: if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceRGB(); + colorSpace = CGColorSpaceCreateDeviceRGB (); bitinfo |= kCGImageAlphaPremultipliedFirst; bitsPerComponent = 8; bitsPerPixel = 32; @@ -208,7 +183,7 @@ _cairo_quartz_create_cgimage (cairo_format_t format, case CAIRO_FORMAT_RGB24: if (colorSpace == NULL) - colorSpace = CGColorSpaceCreateDeviceRGB(); + colorSpace = CGColorSpaceCreateDeviceRGB (); bitinfo |= kCGImageAlphaNoneSkipFirst; bitsPerComponent = 8; bitsPerPixel = 32; @@ -226,24 +201,19 @@ _cairo_quartz_create_cgimage (cairo_format_t format, break; #endif - case CAIRO_FORMAT_RGB16_565: - case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_INVALID: default: return NULL; } - // We don't use height * stride because we may have < stride bytes - // in the last row. CGDataProviderCreateWithData checks that the last - // byte is accessible with newer SDKs and it seems like PDF contexts - // will also read the entire buffer. Instead compute the minimum required - // bytes for the last row using cairo_format_stride_for_width. - size_t size = (height - 1) * stride + cairo_format_stride_for_width (format, width); dataProvider = CGDataProviderCreateWithData (releaseInfo, data, - size, + height * stride, releaseCallback); - if (!dataProvider) { + if (unlikely (!dataProvider)) { // manually release if (releaseCallback) releaseCallback (releaseInfo, data, height * stride); @@ -282,19 +252,18 @@ FINISH: } static inline cairo_bool_t -_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) { - if (cgc == NULL) +_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) +{ + if (unlikely (cgc == NULL)) return FALSE; - if (CGContextGetTypePtr) { + if (likely (CGContextGetTypePtr)) { /* 4 is the type value of a bitmap context */ - if (CGContextGetTypePtr(cgc) == 4) - return TRUE; - return FALSE; + return CGContextGetTypePtr (cgc) == 4; } /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */ - return CGBitmapContextGetBitsPerPixel(cgc) != 0; + return CGBitmapContextGetBitsPerPixel (cgc) != 0; } /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */ @@ -304,16 +273,14 @@ _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) { /* is the desired size of the surface within bounds? */ cairo_bool_t -_cairo_quartz_verify_surface_size(int width, int height) +_cairo_quartz_verify_surface_size (int width, int height) { /* hmmm, allow width, height == 0 ? */ - if (width < 0 || height < 0) { + if (width < 0 || height < 0) return FALSE; - } - if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) { + if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) return FALSE; - } return TRUE; } @@ -327,7 +294,7 @@ static cairo_status_t _cairo_path_to_quartz_context_move_to (void *closure, const cairo_point_t *point) { - //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); + //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y))); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); @@ -339,7 +306,7 @@ static cairo_status_t _cairo_path_to_quartz_context_line_to (void *closure, const cairo_point_t *point) { - //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y))); + //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y))); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); @@ -353,10 +320,10 @@ _cairo_path_to_quartz_context_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2) { - //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n", - // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y), - // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y), - // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y))); + //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n", + // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y), + // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), + // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y))); double x0 = _cairo_fixed_to_double (p0->x); double y0 = _cairo_fixed_to_double (p0->y); double x1 = _cairo_fixed_to_double (p1->x); @@ -364,28 +331,26 @@ _cairo_path_to_quartz_context_curve_to (void *closure, double x2 = _cairo_fixed_to_double (p2->x); double y2 = _cairo_fixed_to_double (p2->y); - CGContextAddCurveToPoint (closure, - x0, y0, x1, y1, x2, y2); + CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_path_to_quartz_context_close_path (void *closure) { - //ND((stderr, "closepath\n")); + //ND ((stderr, "closepath\n")); CGContextClosePath (closure); return CAIRO_STATUS_SUCCESS; } static void -_cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path, +_cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path, CGContextRef closure) { cairo_status_t status; CGContextBeginPath (closure); status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_path_to_quartz_context_move_to, _cairo_path_to_quartz_context_line_to, _cairo_path_to_quartz_context_curve_to, @@ -399,6 +364,7 @@ _cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path, * Misc helpers/callbacks */ +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 static PrivateCGCompositeMode _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) { @@ -446,159 +412,229 @@ _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) case CAIRO_OPERATOR_HSL_COLOR: case CAIRO_OPERATOR_HSL_LUMINOSITY: default: - assert (0); - return kPrivateCGCompositeClear; + ASSERT_NOT_REACHED; } } +#endif + +static CGBlendMode +_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_MULTIPLY: + return kCGBlendModeMultiply; + case CAIRO_OPERATOR_SCREEN: + return kCGBlendModeScreen; + case CAIRO_OPERATOR_OVERLAY: + return kCGBlendModeOverlay; + case CAIRO_OPERATOR_DARKEN: + return kCGBlendModeDarken; + case CAIRO_OPERATOR_LIGHTEN: + return kCGBlendModeLighten; + case CAIRO_OPERATOR_COLOR_DODGE: + return kCGBlendModeColorDodge; + case CAIRO_OPERATOR_COLOR_BURN: + return kCGBlendModeColorBurn; + case CAIRO_OPERATOR_HARD_LIGHT: + return kCGBlendModeHardLight; + case CAIRO_OPERATOR_SOFT_LIGHT: + return kCGBlendModeSoftLight; + case CAIRO_OPERATOR_DIFFERENCE: + return kCGBlendModeDifference; + case CAIRO_OPERATOR_EXCLUSION: + return kCGBlendModeExclusion; + case CAIRO_OPERATOR_HSL_HUE: + return kCGBlendModeHue; + case CAIRO_OPERATOR_HSL_SATURATION: + return kCGBlendModeSaturation; + case CAIRO_OPERATOR_HSL_COLOR: + return kCGBlendModeColor; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return kCGBlendModeLuminosity; + +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + case CAIRO_OPERATOR_CLEAR: + return kCGBlendModeClear; + case CAIRO_OPERATOR_SOURCE: + return kCGBlendModeCopy; + case CAIRO_OPERATOR_OVER: + return kCGBlendModeNormal; + case CAIRO_OPERATOR_IN: + return kCGBlendModeSourceIn; + case CAIRO_OPERATOR_OUT: + return kCGBlendModeSourceOut; + case CAIRO_OPERATOR_ATOP: + return kCGBlendModeSourceAtop; + case CAIRO_OPERATOR_DEST_OVER: + return kCGBlendModeDestinationOver; + case CAIRO_OPERATOR_DEST_IN: + return kCGBlendModeDestinationIn; + case CAIRO_OPERATOR_DEST_OUT: + return kCGBlendModeDestinationOut; + case CAIRO_OPERATOR_DEST_ATOP: + return kCGBlendModeDestinationAtop; + case CAIRO_OPERATOR_XOR: + return kCGBlendModeXOR; + case CAIRO_OPERATOR_ADD: + return kCGBlendModePlusLighter; +#else + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: +#endif + + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_SATURATE: + default: + ASSERT_NOT_REACHED; + } +} + +static cairo_int_status_t +_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op) +{ + CGBlendMode blendmode; + + assert (op != CAIRO_OPERATOR_DEST); + + /* Quartz doesn't support SATURATE at all. COLOR_DODGE and + * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo + * uses the definition from the Adobe Supplement. Also fallback + * on SOFT_LIGHT and HSL_HUE, because their results are + * significantly different from those provided by pixman. + */ + if (op == CAIRO_OPERATOR_SATURATE || + op == CAIRO_OPERATOR_SOFT_LIGHT || + op == CAIRO_OPERATOR_HSL_HUE || + op == CAIRO_OPERATOR_COLOR_DODGE || + op == CAIRO_OPERATOR_COLOR_BURN) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 + if (op <= CAIRO_OPERATOR_ADD) { + PrivateCGCompositeMode compmode; + + compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op); + CGContextSetCompositeOperation (context, compmode); + return CAIRO_STATUS_SUCCESS; + } +#endif + + blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op); + CGContextSetBlendMode (context, blendmode); + return CAIRO_STATUS_SUCCESS; +} static cairo_int_status_t _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op) { ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op)); + /* When the destination has no color components, we can avoid some + * fallbacks, but we have to workaround operators which behave + * differently in Quartz. */ if (surface->base.content == CAIRO_CONTENT_ALPHA) { - /* For some weird reason, some compositing operators are - swapped when operating on masks */ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_ADD: - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op)); - return CAIRO_STATUS_SUCCESS; + assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */ - case CAIRO_OPERATOR_IN: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationAtop); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceOver); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_DEST_ATOP: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceIn); - return CAIRO_STATUS_SUCCESS; - - case CAIRO_OPERATOR_SATURATE: - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositePlusLighter); - return CAIRO_STATUS_SUCCESS; - - - case CAIRO_OPERATOR_ATOP: - /* - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationOver); - return CAIRO_STATUS_SUCCESS; - */ - case CAIRO_OPERATOR_DEST: - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_XOR: - default: - return CAIRO_INT_STATUS_UNSUPPORTED; + if (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_IN || + op == CAIRO_OPERATOR_OUT || + op == CAIRO_OPERATOR_DEST_IN || + op == CAIRO_OPERATOR_DEST_ATOP || + op == CAIRO_OPERATOR_XOR) + { + return CAIRO_INT_STATUS_UNSUPPORTED; } - } else { - switch (op) { - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op)); - return CAIRO_STATUS_SUCCESS; - case CAIRO_OPERATOR_DEST: - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - case CAIRO_OPERATOR_SATURATE: - /* TODO: the following are mostly supported by CGContextSetBlendMode*/ - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } + if (op == CAIRO_OPERATOR_DEST_OVER) + op = CAIRO_OPERATOR_OVER; + else if (op == CAIRO_OPERATOR_SATURATE) + op = CAIRO_OPERATOR_ADD; + else if (op == CAIRO_OPERATOR_COLOR_DODGE) + op = CAIRO_OPERATOR_OVER; + else if (op == CAIRO_OPERATOR_COLOR_BURN) + op = CAIRO_OPERATOR_OVER; } + + return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op); } static inline CGLineCap _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap) { switch (ccap) { - case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break; - case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break; - case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break; - } + default: + ASSERT_NOT_REACHED; - return kCGLineCapButt; + case CAIRO_LINE_CAP_BUTT: + return kCGLineCapButt; + + case CAIRO_LINE_CAP_ROUND: + return kCGLineCapRound; + + case CAIRO_LINE_CAP_SQUARE: + return kCGLineCapSquare; + } } static inline CGLineJoin _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin) { switch (cjoin) { - case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break; - case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break; - case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break; - } + default: + ASSERT_NOT_REACHED; - return kCGLineJoinMiter; + case CAIRO_LINE_JOIN_MITER: + return kCGLineJoinMiter; + + case CAIRO_LINE_JOIN_ROUND: + return kCGLineJoinRound; + + case CAIRO_LINE_JOIN_BEVEL: + return kCGLineJoinBevel; + } } static inline CGInterpolationQuality _cairo_quartz_filter_to_quartz (cairo_filter_t filter) { + /* The CGInterpolationQuality enumeration values seem to have the + * following meaning: + * - kCGInterpolationNone: nearest neighbor + * - kCGInterpolationLow: bilinear + * - kCGInterpolationHigh: bicubic? Lanczos? + */ + switch (filter) { - case CAIRO_FILTER_NEAREST: - return kCGInterpolationNone; + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_FAST: + return kCGInterpolationNone; - case CAIRO_FILTER_FAST: - return kCGInterpolationLow; + case CAIRO_FILTER_BEST: + return kCGInterpolationHigh; - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BILINEAR: - case CAIRO_FILTER_GAUSSIAN: - return kCGInterpolationDefault; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BILINEAR: + return kCGInterpolationLow; + + case CAIRO_FILTER_GAUSSIAN: + return kCGInterpolationDefault; + + default: + ASSERT_NOT_REACHED; + return kCGInterpolationDefault; } - - return kCGInterpolationDefault; } static inline void @@ -613,162 +649,6 @@ _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, dst->ty = src->y0; } -typedef struct { - bool isClipping; - CGGlyph *cg_glyphs; - union { - CGSize *cg_advances; - CGPoint *cg_positions; - } u; - size_t nglyphs; - CGAffineTransform textTransform; - cairo_scaled_font_t *scaled_font; - CGPoint origin; -} unbounded_show_glyphs_t; - -typedef struct { - CGPathRef cgPath; - cairo_fill_rule_t fill_rule; -} unbounded_stroke_fill_t; - -typedef struct { - CGImageRef mask; - CGAffineTransform maskTransform; -} unbounded_mask_t; - -typedef enum { - UNBOUNDED_STROKE_FILL, - UNBOUNDED_SHOW_GLYPHS, - UNBOUNDED_MASK -} unbounded_op_t; - -typedef struct { - unbounded_op_t op; - union { - unbounded_stroke_fill_t stroke_fill; - unbounded_show_glyphs_t show_glyphs; - unbounded_mask_t mask; - } u; -} unbounded_op_data_t; - -static void -_cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface, - unbounded_op_data_t *op, - cairo_antialias_t antialias) -{ - CGRect clipBox, clipBoxRound; - CGContextRef cgc; - CGImageRef maskImage; - - /* TODO: handle failure */ - if (!CGContextClipToMaskPtr) - return; - - clipBox = CGContextGetClipBoundingBox (surface->cgContext); - clipBoxRound = CGRectIntegral (clipBox); - - cgc = CGBitmapContextCreate (NULL, - clipBoxRound.size.width, - clipBoxRound.size.height, - 8, - (((size_t) clipBoxRound.size.width) + 15) & (~15), - NULL, - kCGImageAlphaOnly); - - if (!cgc) - return; - - CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy); - /* We want to mask out whatever we just rendered, so we fill the - * surface opaque, and then we'll render transparent. - */ - CGContextSetAlpha (cgc, 1.0f); - CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height)); - - CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear); - CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE)); - - CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y); - - /* We need to either render the path that was given to us, or the glyph op */ - if (op->op == UNBOUNDED_STROKE_FILL) { - CGContextBeginPath (cgc); - CGContextAddPath (cgc, op->u.stroke_fill.cgPath); - - if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextFillPath (cgc); - else - CGContextEOFillPath (cgc); - } else if (op->op == UNBOUNDED_SHOW_GLYPHS) { - if (op->u.show_glyphs.isClipping) { - /* Note that the comment in show_glyphs about kCGTextClip - * and the text transform still applies here; however, the - * cg_advances we have were already transformed, so we - * don't have to do anything. */ - CGContextSetTextDrawingMode (cgc, kCGTextClip); - CGContextSaveGState (cgc); - } - CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y); - CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform); - if (CTFontDrawGlyphsPtr) { - CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font), - op->u.show_glyphs.cg_glyphs, - op->u.show_glyphs.u.cg_positions, - op->u.show_glyphs.nglyphs, - cgc); - } else { - CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font)); - CGContextSetFontSize (cgc, 1.0); - CGContextSetTextMatrix (cgc, CGAffineTransformIdentity); - - CGContextShowGlyphsWithAdvances (cgc, - op->u.show_glyphs.cg_glyphs, - op->u.show_glyphs.u.cg_advances, - op->u.show_glyphs.nglyphs); - - } - if (op->u.show_glyphs.isClipping) { - CGContextClearRect (cgc, clipBoxRound); - CGContextRestoreGState (cgc); - } - } else if (op->op == UNBOUNDED_MASK) { - CGAffineTransform ctm = CGContextGetCTM (cgc); - CGContextSaveGState (cgc); - CGContextConcatCTM (cgc, op->u.mask.maskTransform); - CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f, - CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)), - op->u.mask.mask); - CGContextSetCTM (cgc, ctm); - CGContextClearRect (cgc, clipBoxRound); - CGContextRestoreGState (cgc); - } - - /* Also mask out the portion of the clipbox that we rounded out, if any */ - if (!CGRectEqualToRect (clipBox, clipBoxRound)) { - CGContextBeginPath (cgc); - CGContextAddRect (cgc, clipBoxRound); - CGContextAddRect (cgc, clipBox); - CGContextEOFillPath (cgc); - } - - maskImage = CGBitmapContextCreateImage (cgc); - CGContextRelease (cgc); - - if (!maskImage) - return; - - /* Then render with the mask */ - CGContextSaveGState (surface->cgContext); - - CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy); - CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage); - CGImageRelease (maskImage); - - /* Finally, clear out the entire clipping region through our mask */ - CGContextClearRect (surface->cgContext, clipBoxRound); - - CGContextRestoreGState (surface->cgContext); -} /* * Source -> Quartz setup and finish functions @@ -787,18 +667,16 @@ ComputeGradientValue (void *info, * REPEAT/REFLECT */ if (grad->base.extend == CAIRO_EXTEND_REPEAT) { - fdist = fdist - floor(fdist); + fdist = fdist - floor (fdist); } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) { - fdist = fmod(fabs(fdist), 2.0); - if (fdist > 1.0) { + fdist = fmod (fabs (fdist), 2.0); + if (fdist > 1.0) fdist = 2.0 - fdist; - } } - for (i = 0; i < grad->n_stops; i++) { + for (i = 0; i < grad->n_stops; i++) if (grad->stops[i].offset > fdist) break; - } if (i == 0 || i == grad->n_stops) { if (i == grad->n_stops) @@ -834,134 +712,62 @@ static const cairo_quartz_float_t gradient_output_value_ranges[8] = { static const CGFunctionCallbacks gradient_callbacks = { 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy }; -/* Quartz will clamp input values to the input range. - Our stops are all in the range 0.0 to 1.0. However, the color before the - beginning of the gradient line is obtained by Quartz computing a negative - position on the gradient line, clamping it to the input range we specified - for our color function, and then calling our color function (actually it - pre-samples the color function into an array, but that doesn't matter just - here). Therefore if we set the lower bound to 0.0, a negative position - on the gradient line will pass 0.0 to ComputeGradientValue, which will - select the last color stop with position 0, although it should select - the first color stop (this matters when there are multiple color stops with - position 0). - - Therefore we pass a small negative number as the lower bound of the input - range, so this value gets passed into ComputeGradientValue, which will - return the color of the first stop. The number should be small because - as far as I can tell, Quartz pre-samples the entire input range of the color - function into an array of fixed size, so if the input range is larger - than needed, the resolution of the gradient will be unnecessarily low. -*/ -static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f }; +/* Quartz computes a small number of samples of the gradient color + * function. On MacOS X 10.5 it apparently computes only 1024 + * samples. */ +#define MAX_GRADIENT_RANGE 1024 static CGFunctionRef -CreateGradientFunction (const cairo_gradient_pattern_t *gpat) -{ - cairo_pattern_t *pat; - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ - return NULL; - - return CGFunctionCreate (pat, - 1, - nonrepeating_gradient_input_value_range, - 4, - gradient_output_value_ranges, - &gradient_callbacks); -} - -static void -UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start, - double dx, double dy, - double x, double y) -{ - /* Compute a parameter t such that a line perpendicular to the (dx,dy) - vector, passing through (start->x + dx*t, start->y + dy*t), also - passes through (x,y). - - Let px = x - start->x, py = y - start->y. - t is given by - (px - dx*t)*dx + (py - dy*t)*dy = 0 - - Solving for t we get - numerator = dx*px + dy*py - denominator = dx^2 + dy^2 - t = numerator/denominator - - In CreateRepeatingLinearGradientFunction we know the length of (dx,dy) - is not zero. (This is checked in _cairo_quartz_setup_linear_source.) - */ - double px = x - start->x; - double py = y - start->y; - double numerator = dx*px + dy*py; - double denominator = dx*dx + dy*dy; - double t = numerator/denominator; - - if (*min_t > t) { - *min_t = t; - } - if (*max_t < t) { - *max_t = t; - } -} - -static CGFunctionRef -CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface, - const cairo_gradient_pattern_t *gpat, - CGPoint *start, CGPoint *end, - cairo_rectangle_int_t *extents) +CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents, + cairo_circle_double_t *start, + cairo_circle_double_t *end) { cairo_pattern_t *pat; cairo_quartz_float_t input_value_range[2]; - double t_min = 0.; - double t_max = 0.; - double dx = end->x - start->x; - double dy = end->y - start->y; - double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - if (!extents) { - extents = &surface->extents; + if (gradient->base.extend != CAIRO_EXTEND_NONE) { + double bounds_x1, bounds_x2, bounds_y1, bounds_y2; + double t[2], tolerance; + + tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix)); + tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1); + + bounds_x1 = extents->x; + bounds_y1 = extents->y; + bounds_x2 = extents->x + extents->width; + bounds_y2 = extents->y + extents->height; + _cairo_matrix_transform_bounding_box (&gradient->base.matrix, + &bounds_x1, &bounds_y1, + &bounds_x2, &bounds_y2, + NULL); + + _cairo_gradient_pattern_box_to_parameter (gradient, + bounds_x1, bounds_y1, + bounds_x2, bounds_y2, + tolerance, + t); + + if (gradient->base.extend == CAIRO_EXTEND_PAD) { + t[0] = MAX (t[0], -0.5); + t[1] = MIN (t[1], 1.5); + } else if (t[1] - t[0] > MAX_GRADIENT_RANGE) + return NULL; + + /* set the input range for the function -- the function knows how + to map values outside of 0.0 .. 1.0 to the correct color */ + input_value_range[0] = t[0]; + input_value_range[1] = t[1]; + } else { + input_value_range[0] = 0; + input_value_range[1] = 1; } - bounds_x1 = extents->x; - bounds_y1 = extents->y; - bounds_x2 = extents->x + extents->width; - bounds_y2 = extents->y + extents->height; - _cairo_matrix_transform_bounding_box (&gpat->base.matrix, - &bounds_x1, &bounds_y1, - &bounds_x2, &bounds_y2, - NULL); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x1, bounds_y1); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x2, bounds_y1); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x2, bounds_y2); - UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy, - bounds_x1, bounds_y2); + _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start); + _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end); - /* Move t_min and t_max to the nearest usable integer to try to avoid - subtle variations due to numerical instability, especially accidentally - cutting off a pixel. Extending the gradient repetitions is always safe. */ - t_min = floor (t_min); - t_max = ceil (t_max); - end->x = start->x + dx*t_max; - end->y = start->y + dy*t_max; - start->x = start->x + dx*t_min; - start->y = start->y + dy*t_min; - - // set the input range for the function -- the function knows how to - // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. - input_value_range[0] = t_min; - input_value_range[1] = t_max; - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ + if (_cairo_pattern_create_copy (&pat, &gradient->base)) return NULL; return CGFunctionCreate (pat, @@ -972,174 +778,24 @@ CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface, &gradient_callbacks); } -static void -UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center, - double dr, double dx, double dy, - double x, double y) -{ - /* Compute a parameter t such that a circle centered at - (center->x + dx*t, center->y + dy*t) with radius dr*t contains the - point (x,y). - - Let px = x - center->x, py = y - center->y. - Parameter values for which t is on the circle are given by - (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2 - - Solving for t using the quadratic formula, and simplifying, we get - numerator = dx*px + dy*py +- - sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 ) - denominator = dx^2 + dy^2 - dr^2 - t = numerator/denominator - - In CreateRepeatingRadialGradientFunction we know the outer circle - contains the inner circle. Therefore the distance between the circle - centers plus the radius of the inner circle is less than the radius of - the outer circle. (This is checked in _cairo_quartz_setup_radial_source.) - Therefore - dx^2 + dy^2 < dr^2 - So the denominator is negative and the larger solution for t is given by - numerator = dx*px + dy*py - - sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 ) - denominator = dx^2 + dy^2 - dr^2 - t = numerator/denominator - dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive. - */ - double px = x - center->x; - double py = y - center->y; - double dx_py_minus_dy_px = dx*py - dy*px; - double numerator = dx*px + dy*py - - sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px); - double denominator = dx*dx + dy*dy - dr*dr; - double t = numerator/denominator; - - if (*max_t < t) { - *max_t = t; - } -} - -/* This must only be called when one of the circles properly contains the other */ -static CGFunctionRef -CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface, - const cairo_gradient_pattern_t *gpat, - CGPoint *start, double *start_radius, - CGPoint *end, double *end_radius, - cairo_rectangle_int_t *extents) -{ - cairo_pattern_t *pat; - cairo_quartz_float_t input_value_range[2]; - CGPoint *inner; - double *inner_radius; - CGPoint *outer; - double *outer_radius; - /* minimum and maximum t-parameter values that will make our gradient - cover the clipBox */ - double t_min, t_max, t_temp; - /* outer minus inner */ - double dr, dx, dy; - double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - - if (!extents) { - extents = &surface->extents; - } - bounds_x1 = extents->x; - bounds_y1 = extents->y; - bounds_x2 = extents->x + extents->width; - bounds_y2 = extents->y + extents->height; - _cairo_matrix_transform_bounding_box (&gpat->base.matrix, - &bounds_x1, &bounds_y1, - &bounds_x2, &bounds_y2, - NULL); - - if (*start_radius < *end_radius) { - /* end circle contains start circle */ - inner = start; - outer = end; - inner_radius = start_radius; - outer_radius = end_radius; - } else { - /* start circle contains end circle */ - inner = end; - outer = start; - inner_radius = end_radius; - outer_radius = start_radius; - } - - dr = *outer_radius - *inner_radius; - dx = outer->x - inner->x; - dy = outer->y - inner->y; - - /* We can't round or fudge t_min here, it has to be as accurate as possible. */ - t_min = -(*inner_radius/dr); - inner->x += t_min*dx; - inner->y += t_min*dy; - *inner_radius = 0.; - - t_temp = 0.; - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x1, bounds_y1); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x2, bounds_y1); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x2, bounds_y2); - UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy, - bounds_x1, bounds_y2); - /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0. - But for the parameter values we use with Quartz, t_min means radius 0. - Since the circles are alway expanding and contain the earlier circles, - it's safe to extend t_max/t_temp as much as we want, so round t_temp up - to the nearest integer. This may help us give stable results. */ - t_temp = ceil (t_temp); - t_max = t_min + t_temp; - outer->x = inner->x + t_temp*dx; - outer->y = inner->y + t_temp*dy; - *outer_radius = t_temp*dr; - - /* set the input range for the function -- the function knows how to - map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */ - if (*start_radius < *end_radius) { - input_value_range[0] = t_min; - input_value_range[1] = t_max; - } else { - input_value_range[0] = 1 - t_max; - input_value_range[1] = 1 - t_min; - } - - if (_cairo_pattern_create_copy (&pat, &gpat->base)) - /* quartz doesn't deal very well with malloc failing, so there's - * not much point in us trying either */ - return NULL; - - return CGFunctionCreate (pat, - 1, - input_value_range, - 4, - gradient_output_value_ranges, - &gradient_callbacks); -} - -/* Obtain a CGImageRef from a #cairo_surface_t * */ - -typedef struct { - cairo_surface_t *surface; - cairo_image_surface_t *image_out; - void *image_extra; -} quartz_source_image_t; - static void DataProviderReleaseCallback (void *info, const void *data, size_t size) { - quartz_source_image_t *source_img = info; - _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra); - cairo_surface_destroy (source_img->surface); - free (source_img); + free (info); } static cairo_status_t -_cairo_surface_to_cgimage (cairo_surface_t *source, - CGImageRef *image_out) +_cairo_surface_to_cgimage (cairo_surface_t *source, + cairo_rectangle_int_t *extents, + cairo_format_t format, + cairo_matrix_t *matrix, + const cairo_clip_t *clip, + CGImageRef *image_out) { cairo_status_t status; - quartz_source_image_t *source_img; + cairo_image_surface_t *image_surface; + void *image_data, *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; @@ -1149,57 +805,88 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, if (_cairo_surface_is_quartz (source)) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; - if (IS_EMPTY(surface)) { + if (IS_EMPTY (surface)) { *image_out = NULL; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { - if (!surface->bitmapContextImage) { - surface->bitmapContextImage = - CGBitmapContextCreateImage (surface->cgContext); - } - if (surface->bitmapContextImage) { - *image_out = CGImageRetain (surface->bitmapContextImage); - return CAIRO_STATUS_SUCCESS; - } + *image_out = CGBitmapContextCreateImage (surface->cgContext); + if (*image_out) + return CAIRO_STATUS_SUCCESS; } } - source_img = malloc (sizeof (quartz_source_image_t)); - if (source_img == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + 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); + return status; + } - source_img->surface = cairo_surface_reference(source); + status = _cairo_recording_surface_replay_with_clip (source, + matrix, + &image_surface->base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&image_surface->base); + return status; + } + + cairo_matrix_init_identity (matrix); + } + else { + status = _cairo_surface_acquire_source_image (source, &image_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); + else + cairo_surface_destroy (&image_surface->base); - status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra); - if (status) { - cairo_surface_destroy (source_img->surface); - free (source_img); return status; } - if (source_img->image_out->width == 0 || source_img->image_out->height == 0) { - *image_out = NULL; - DataProviderReleaseCallback (source_img, - source_img->image_out->data, - source_img->image_out->height * source_img->image_out->stride); - } else { - *image_out = _cairo_quartz_create_cgimage (source_img->image_out->format, - source_img->image_out->width, - source_img->image_out->height, - source_img->image_out->stride, - source_img->image_out->data, - TRUE, - NULL, - DataProviderReleaseCallback, - source_img); + 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); - /* TODO: differentiate memory error and unsupported surface type */ - if (*image_out == NULL) - status = CAIRO_INT_STATUS_UNSUPPORTED; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); } + memcpy (image_data, image_surface->data, + image_surface->height * image_surface->stride); + *image_out = CairoQuartzCreateCGImage (image_surface->format, + image_surface->width, + image_surface->height, + image_surface->stride, + image_data, + TRUE, + NULL, + DataProviderReleaseCallback, + image_data); + + /* 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; } @@ -1256,11 +943,13 @@ SurfacePatternReleaseInfoFunc (void *ainfo) static cairo_int_status_t _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest, const cairo_pattern_t *apattern, + const cairo_clip_t *clip, CGPatternRef *cgpat) { cairo_surface_pattern_t *spattern; cairo_surface_t *pat_surf; cairo_rectangle_int_t extents; + cairo_format_t format = _cairo_format_from_content (dest->base.content); CGImageRef image; CGRect pbounds; @@ -1276,23 +965,26 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t cairo_matrix_t m; /* SURFACE is the only type we'll handle here */ - if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; + assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE); spattern = (cairo_surface_pattern_t *) apattern; pat_surf = spattern->surface; - is_bounded = _cairo_surface_get_extents (pat_surf, &extents); - assert (is_bounded); + if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) { + is_bounded = _cairo_surface_get_extents (pat_surf, &extents); + assert (is_bounded); + } + else + _cairo_surface_get_extents (&dest->base, &extents); - status = _cairo_surface_to_cgimage (pat_surf, &image); - if (status) + m = spattern->base.matrix; + status = _cairo_surface_to_cgimage (pat_surf, &extents, format, + &m, clip, &image); + if (unlikely (status)) return status; - if (image == NULL) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - info = malloc(sizeof(SurfacePatternDrawInfo)); - if (!info) + info = _cairo_malloc (sizeof (SurfacePatternDrawInfo)); + if (unlikely (!info)) return CAIRO_STATUS_NO_MEMORY; /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure @@ -1322,8 +1014,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t rw = pbounds.size.width; rh = pbounds.size.height; - m = spattern->base.matrix; - cairo_matrix_invert(&m); + cairo_matrix_invert (&m); _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform); /* The pattern matrix is relative to the bottom left, again; the @@ -1331,13 +1022,13 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t * So we take the pattern matrix and the original context matrix, * which gives us the correct base translation/y flip. */ - ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM); + ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM); #ifdef QUARTZ_DEBUG - ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height)); - ND((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d)); - CGAffineTransform xform = CGContextGetCTM(dest->cgContext); - ND((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d)); + ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height)); + ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d)); + CGAffineTransform xform = CGContextGetCTM (dest->cgContext); + ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d)); #endif *cgpat = CGPatternCreate (info, @@ -1351,119 +1042,37 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t return CAIRO_STATUS_SUCCESS; } -typedef enum { - DO_SOLID, - DO_SHADING, - DO_PATTERN, - DO_IMAGE, - DO_TILED_IMAGE, - DO_LAYER, - DO_UNSUPPORTED, - DO_NOTHING -} cairo_quartz_action_t; - /* State used during a drawing operation. */ typedef struct { - CGContextRef context; + /* The destination of the mask */ + CGContextRef cgMaskContext; + + /* The destination of the drawing of the source */ + CGContextRef cgDrawContext; + + /* The filter to be used when drawing the source */ + CGInterpolationQuality filter; + + /* Action type */ cairo_quartz_action_t action; - // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER + /* Destination rect */ + CGRect rect; + + /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */ CGAffineTransform transform; - // Used with DO_IMAGE and DO_TILED_IMAGE + /* Used with DO_IMAGE and DO_TILED_IMAGE */ CGImageRef image; - cairo_surface_t *imageSurface; - // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER - CGRect imageRect; - - // Used with DO_LAYER - CGLayerRef layer; - - // Used with DO_SHADING + /* Used with DO_SHADING */ CGShadingRef shading; - // Used with DO_PATTERN - CGPatternRef pattern; + /* Temporary destination for unbounded operations */ + CGLayerRef layer; + CGRect clipRect; } cairo_quartz_drawing_state_t; -static void -_cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, - const cairo_pattern_t *source, - cairo_quartz_drawing_state_t *state) -{ - CGRect clipBox = CGContextGetClipBoundingBox (state->context); - double x0, y0, w, h; - - cairo_surface_t *fallback; - CGImageRef img; - - cairo_status_t status; - - if (clipBox.size.width == 0.0f || - clipBox.size.height == 0.0f) { - state->action = DO_NOTHING; - return; - } - - x0 = floor(clipBox.origin.x); - y0 = floor(clipBox.origin.y); - w = ceil(clipBox.origin.x + clipBox.size.width) - x0; - h = ceil(clipBox.origin.y + clipBox.size.height) - y0; - - /* Create a temporary the size of the clip surface, and position - * it so that the device origin coincides with the original surface */ - fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h); - cairo_surface_set_device_offset (fallback, -x0, -y0); - -#if 0 - { - cairo_t *fallback_cr; - cairo_pattern_t *source_copy; - - /* Paint the source onto our temporary */ - fallback_cr = cairo_create (fallback); - cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE); - - /* Use a copy of the pattern because it is const and could be allocated - * on the stack */ - status = _cairo_pattern_create_copy (&source_copy, source); - cairo_set_source (fallback_cr, source_copy); - cairo_pattern_destroy (source_copy); - - cairo_paint (fallback_cr); - cairo_destroy (fallback_cr); - } -#else - { - cairo_pattern_union_t pattern; - - _cairo_pattern_init_static_copy (&pattern.base, source); - _cairo_pattern_transform (&pattern.base, - &fallback->device_transform_inverse); - status = _cairo_surface_paint (fallback, - CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL); - } -#endif - - status = _cairo_surface_to_cgimage (fallback, &img); - if (status) { - state->action = DO_UNSUPPORTED; - return; - } - if (img == NULL) { - state->action = DO_NOTHING; - return; - } - - state->imageRect = CGRectMake (0.0, 0.0, w, h); - state->image = img; - state->imageSurface = fallback; - state->transform = CGAffineTransformMakeTranslation (x0, y0); - state->action = DO_IMAGE; -} - /* Quartz does not support repeating radients. We handle repeating gradients by manually extending the gradient and repeating color stops. We need to @@ -1471,164 +1080,176 @@ minimize the number of repetitions since Quartz seems to sample our color function across the entire range, even if part of that range is not needed for the visible area of the gradient, and it samples with some fixed resolution, so if the gradient range is too large it samples with very low resolution and -the gradient is very coarse. CreateRepeatingLinearGradientFunction and -CreateRepeatingRadialGradientFunction compute the number of repetitions needed -based on the extents of the object (the clip region cannot be used here since -we don't want the rasterization of the entire gradient to depend on the -clip region). +the gradient is very coarse. _cairo_quartz_create_gradient_function computes +the number of repetitions needed based on the extents. */ -static void -_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, - const cairo_linear_pattern_t *lpat, - cairo_rectangle_int_t *extents, - cairo_quartz_drawing_state_t *state) +static cairo_int_status_t +_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state, + const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents) { - const cairo_pattern_t *abspat = &lpat->base.base; cairo_matrix_t mat; - CGPoint start, end; + cairo_circle_double_t start, end; CGFunctionRef gradFunc; CGColorSpaceRef rgb; - bool extend = abspat->extend == CAIRO_EXTEND_PAD; + bool extend = gradient->base.extend != CAIRO_EXTEND_NONE; - if (lpat->base.n_stops == 0) { - CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.); - CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.); - state->action = DO_SOLID; - return; - } + assert (gradient->n_stops > 0); - if (lpat->p1.x == lpat->p2.x && - lpat->p1.y == lpat->p2.y) { - /* Quartz handles cases where the vector has no length very - * differently from pixman. - * Whatever the correct behaviour is, let's at least have only pixman's - * implementation to worry about. - */ - _cairo_quartz_setup_fallback_source (surface, abspat, state); - return; - } - - mat = abspat->matrix; + mat = gradient->base.matrix; cairo_matrix_invert (&mat); _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); - rgb = CGColorSpaceCreateDeviceRGB(); + gradFunc = CairoQuartzCreateGradientFunction (gradient, extents, + &start, &end); - start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x), - _cairo_fixed_to_double (lpat->p1.y)); - end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x), - _cairo_fixed_to_double (lpat->p2.y)); + if (unlikely (gradFunc == NULL)) + return CAIRO_INT_STATUS_UNSUPPORTED; - if (abspat->extend == CAIRO_EXTEND_NONE || - abspat->extend == CAIRO_EXTEND_PAD) - { - gradFunc = CreateGradientFunction (&lpat->base); + rgb = CGColorSpaceCreateDeviceRGB (); + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + state->shading = CGShadingCreateAxial (rgb, + CGPointMake (start.center.x, + start.center.y), + CGPointMake (end.center.x, + end.center.y), + gradFunc, + extend, extend); } else { - gradFunc = CreateRepeatingLinearGradientFunction (surface, - &lpat->base, - &start, &end, - extents); + state->shading = CGShadingCreateRadial (rgb, + CGPointMake (start.center.x, + start.center.y), + MAX (start.radius, 0), + CGPointMake (end.center.x, + end.center.y), + MAX (end.radius, 0), + gradFunc, + extend, extend); } - state->shading = CGShadingCreateAxial (rgb, - start, end, - gradFunc, - extend, extend); - - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); + CGColorSpaceRelease (rgb); + CGFunctionRelease (gradFunc); state->action = DO_SHADING; + return CAIRO_STATUS_SUCCESS; } -static void -_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, - const cairo_radial_pattern_t *rpat, - cairo_rectangle_int_t *extents, - cairo_quartz_drawing_state_t *state) +static cairo_int_status_t +_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, + cairo_composite_rectangles_t *composite) { - const cairo_pattern_t *abspat = &rpat->base.base; - cairo_matrix_t mat; - CGPoint start, end; - CGFunctionRef gradFunc; - CGColorSpaceRef rgb; - bool extend = abspat->extend == CAIRO_EXTEND_PAD; - double c1x = _cairo_fixed_to_double (rpat->c1.x); - double c1y = _cairo_fixed_to_double (rpat->c1.y); - double c2x = _cairo_fixed_to_double (rpat->c2.x); - double c2y = _cairo_fixed_to_double (rpat->c2.y); - double r1 = _cairo_fixed_to_double (rpat->r1); - double r2 = _cairo_fixed_to_double (rpat->r2); - double dx = c1x - c2x; - double dy = c1y - c2y; - double centerDistance = sqrt (dx*dx + dy*dy); + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface; + cairo_operator_t op = composite->op; + const cairo_pattern_t *source = &composite->source_pattern.base; + const cairo_clip_t *clip = composite->clip; + cairo_bool_t needs_temp; + cairo_status_t status; + cairo_format_t format = _cairo_format_from_content (composite->surface->content); - if (rpat->base.n_stops == 0) { - CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.); - CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.); - state->action = DO_SOLID; - return; + state->layer = NULL; + state->image = NULL; + state->shading = NULL; + state->cgDrawContext = NULL; + state->cgMaskContext = NULL; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_quartz_surface_set_cairo_operator (surface, op); + if (unlikely (status)) + return status; + + /* Save before we change the pattern, colorspace, etc. so that + * we can restore and make sure that quartz releases our + * pattern (which may be stack allocated) + */ + + CGContextSaveGState (surface->cgContext); + state->clipRect = CGContextGetClipBoundingBox (surface->cgContext); + state->clipRect = CGRectIntegral (state->clipRect); + state->rect = state->clipRect; + + state->cgMaskContext = surface->cgContext; + state->cgDrawContext = state->cgMaskContext; + + state->filter = _cairo_quartz_filter_to_quartz (source->filter); + + if (op == CAIRO_OPERATOR_CLEAR) { + CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); + + state->action = DO_DIRECT; + return CAIRO_STATUS_SUCCESS; } - if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */ - r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */ - /* Quartz handles cases where neither circle contains the other very - * differently from pixman. - * Whatever the correct behaviour is, let's at least have only pixman's - * implementation to worry about. - * Note that this also catches the cases where r1 == r2. - */ - _cairo_quartz_setup_fallback_source (surface, abspat, state); - return; + /* + * To implement mask unbounded operations Quartz needs a temporary + * surface which will be composited entirely (ignoring the mask). + * To implement source unbounded operations Quartz needs a + * temporary surface which allows extending the source to a size + * covering the whole mask, but there are some optimization + * opportunities: + * + * - CLEAR completely ignores the source, thus we can just use a + * solid color fill. + * + * - SOURCE can be implemented by drawing the source and clearing + * outside of the source as long as the two regions have no + * intersection. This happens when the source is a pixel-aligned + * rectangle. If the source is at least as big as the + * intersection between the clip rectangle and the mask + * rectangle, no clear operation is needed. + */ + needs_temp = ! _cairo_operator_bounded_by_mask (op); + + if (needs_temp) { + state->layer = CGLayerCreateWithContext (surface->cgContext, + state->clipRect.size, + NULL); + state->cgDrawContext = CGLayerGetContext (state->layer); + state->cgMaskContext = state->cgDrawContext; + CGContextTranslateCTM (state->cgDrawContext, + -state->clipRect.origin.x, + -state->clipRect.origin.y); } - mat = abspat->matrix; - cairo_matrix_invert (&mat); - _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - rgb = CGColorSpaceCreateDeviceRGB(); + 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); - start = CGPointMake (c1x, c1y); - end = CGPointMake (c2x, c2y); + state->action = DO_DIRECT; + return CAIRO_STATUS_SUCCESS; + } - if (abspat->extend == CAIRO_EXTEND_NONE || - abspat->extend == CAIRO_EXTEND_PAD) + if (source->type == CAIRO_PATTERN_TYPE_LINEAR || + source->type == CAIRO_PATTERN_TYPE_RADIAL) { - gradFunc = CreateGradientFunction (&rpat->base); - } else { - gradFunc = CreateRepeatingRadialGradientFunction (surface, - &rpat->base, - &start, &r1, - &end, &r2, - extents); + const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source; + cairo_rectangle_int_t extents; + + extents = surface->virtual_extents; + extents.x -= surface->base.device_transform.x0; + extents.y -= surface->base.device_transform.y0; + _cairo_rectangle_union (&extents, &surface->extents); + + return _cairo_quartz_setup_gradient_source (state, gpat, &extents); } - state->shading = CGShadingCreateRadial (rgb, - start, - r1, - end, - r2, - gradFunc, - extend, extend); - - CGColorSpaceRelease(rgb); - CGFunctionRelease(gradFunc); - - state->action = DO_SHADING; -} - -static void -_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface, - const cairo_surface_pattern_t *spat, - cairo_rectangle_int_t *extents, - cairo_quartz_drawing_state_t *state) -{ - const cairo_pattern_t *source = &spat->base; - CGContextRef context = state->context; - - if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD || - (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)) + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) { + const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; cairo_surface_t *pat_surf = spat->surface; CGImageRef img; cairo_matrix_t m = spat->base.matrix; @@ -1637,36 +1258,55 @@ _cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface, CGRect srcRect; cairo_fixed_t fw, fh; cairo_bool_t is_bounded; - cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT; - cairo_status_t status; - cairo_matrix_invert(&m); - _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform); - - status = _cairo_surface_to_cgimage (pat_surf, &img); - if (status) { - state->action = DO_UNSUPPORTED; - return; - } - if (img == NULL) { - state->action = DO_NOTHING; - return; - } - - /* XXXroc what is this for? */ - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); + _cairo_surface_get_extents (composite->surface, &extents); + status = _cairo_surface_to_cgimage (pat_surf, &extents, format, + &m, clip, &img); + if (unlikely (status)) + return status; state->image = img; - is_bounded = _cairo_surface_get_extents (pat_surf, &extents); - assert (is_bounded); - - if (!repeat) { - state->imageRect = CGRectMake (0, 0, extents.width, extents.height); - state->action = DO_IMAGE; - return; + if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) { + m.x0 = -ceil (m.x0 - 0.5); + m.y0 = -ceil (m.y0 - 0.5); + } else { + cairo_matrix_invert (&m); } + _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform); + + if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) { + is_bounded = _cairo_surface_get_extents (pat_surf, &extents); + assert (is_bounded); + } + + srcRect = CGRectMake (0, 0, extents.width, extents.height); + + if (source->extend == CAIRO_EXTEND_NONE) { + int x, y; + if (op == CAIRO_OPERATOR_SOURCE && + (pat_surf->content == CAIRO_CONTENT_ALPHA || + ! _cairo_matrix_is_integer_translation (&m, &x, &y))) + { + state->layer = CGLayerCreateWithContext (surface->cgContext, + state->clipRect.size, + NULL); + state->cgDrawContext = CGLayerGetContext (state->layer); + CGContextTranslateCTM (state->cgDrawContext, + -state->clipRect.origin.x, + -state->clipRect.origin.y); + } + + CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); + + state->rect = srcRect; + state->action = DO_IMAGE; + return CAIRO_STATUS_SUCCESS; + } + + CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); + /* Quartz seems to tile images at pixel-aligned regions only -- this * leads to seams if the image doesn't end up scaling to fill the * space exactly. The CGPattern tiling approach doesn't have this @@ -1674,10 +1314,9 @@ _cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface, * epsilon), and if not, fall back to the CGPattern type. */ - xform = CGAffineTransformConcat (CGContextGetCTM (context), + xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext), state->transform); - srcRect = CGRectMake (0, 0, extents.width, extents.height); srcRect = CGRectApplyAffineTransform (srcRect, xform); fw = _cairo_fixed_from_double (srcRect.size.width); @@ -1689,323 +1328,197 @@ _cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface, /* We're good to use DrawTiledImage, but ensure that * the math works out */ - srcRect.size.width = round(srcRect.size.width); - srcRect.size.height = round(srcRect.size.height); + srcRect.size.width = round (srcRect.size.width); + srcRect.size.height = round (srcRect.size.height); xform = CGAffineTransformInvert (xform); srcRect = CGRectApplyAffineTransform (srcRect, xform); - state->imageRect = srcRect; - state->action = DO_TILED_IMAGE; - return; + state->rect = srcRect; + state->action = DO_TILED_IMAGE; + return CAIRO_STATUS_SUCCESS; } /* Fall through to generic SURFACE case */ } - CGFloat patternAlpha = 1.0f; - CGColorSpaceRef patternSpace; - CGPatternRef pattern; - cairo_int_status_t status; - - status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - state->action = DO_NOTHING; - return; - } - if (status) { - state->action = DO_UNSUPPORTED; - return; - } - - patternSpace = CGColorSpaceCreatePattern (NULL); - CGContextSetFillColorSpace (context, patternSpace); - CGContextSetFillPattern (context, pattern, &patternAlpha); - CGContextSetStrokeColorSpace (context, patternSpace); - CGContextSetStrokePattern (context, pattern, &patternAlpha); - CGColorSpaceRelease (patternSpace); - - /* Quartz likes to munge the pattern phase (as yet unexplained - * why); force it to 0,0 as we've already baked in the correct - * pattern translation into the pattern matrix - */ - CGContextSetPatternPhase (context, CGSizeMake(0,0)); - - state->pattern = pattern; - state->action = DO_PATTERN; - return; -} - -/** - * Call this before any operation that can modify the contents of a - * cairo_quartz_surface_t. - */ -static void -_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface) -{ - if (surface->bitmapContextImage) { - CGImageRelease (surface->bitmapContextImage); - surface->bitmapContextImage = NULL; - } -} - -/** - * Sets up internal state to be used to draw the source mask, stored in - * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on - * surface->cgContext. - */ -static cairo_quartz_drawing_state_t -_cairo_quartz_setup_state (cairo_quartz_surface_t *surface, - const cairo_pattern_t *source, - cairo_operator_t op, - cairo_rectangle_int_t *extents) -{ - CGContextRef context = surface->cgContext; - cairo_quartz_drawing_state_t state; - cairo_status_t status; - - state.context = context; - state.image = NULL; - state.imageSurface = NULL; - state.layer = NULL; - state.shading = NULL; - state.pattern = NULL; - - _cairo_quartz_surface_will_change (surface); - - // Save before we change the pattern, colorspace, etc. so that - // we can restore and make sure that quartz releases our - // pattern (which may be stack allocated) - CGContextSaveGState(context); - - CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter)); - - status = _cairo_quartz_surface_set_cairo_operator (surface, op); - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { - state.action = DO_NOTHING; - return state; - } - if (status) { - state.action = DO_UNSUPPORTED; - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; - - CGContextSetRGBStrokeColor (context, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - CGContextSetRGBFillColor (context, - solid->color.red, - solid->color.green, - solid->color.blue, - solid->color.alpha); - - state.action = DO_SOLID; - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_LINEAR) { - const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source; - _cairo_quartz_setup_linear_source (surface, lpat, extents, &state); - return state; - } - - if (source->type == CAIRO_PATTERN_TYPE_RADIAL) { - const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source; - _cairo_quartz_setup_radial_source (surface, rpat, extents, &state); - return state; - } - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) && - CGContextGetAlphaPtr && - CGContextGetAlphaPtr (surface->cgContext) == 1.0) { - // Quartz won't touch pixels outside the bounds of the - // source surface, so we can just go ahead and use Copy here - // to accelerate things. - // Quartz won't necessarily be able to do this optimization internally; - // for CGLayer surfaces, we can know all the pixels are opaque - // (because it's CONTENT_COLOR), but Quartz won't know. - CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy); - } + cairo_quartz_float_t patternAlpha = 1.0f; + CGColorSpaceRef patternSpace; + CGPatternRef pattern = NULL; + cairo_int_status_t status; - const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; - _cairo_quartz_setup_surface_source (surface, spat, extents, &state); - return state; + status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern); + if (unlikely (status)) + return status; + + patternSpace = CGColorSpaceCreatePattern (NULL); + CGContextSetFillColorSpace (state->cgDrawContext, patternSpace); + CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha); + CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace); + CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha); + CGColorSpaceRelease (patternSpace); + + /* Quartz likes to munge the pattern phase (as yet unexplained + * why); force it to 0,0 as we've already baked in the correct + * pattern translation into the pattern matrix + */ + CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0)); + + CGPatternRelease (pattern); + + state->action = DO_DIRECT; + return CAIRO_STATUS_SUCCESS; } - state.action = DO_UNSUPPORTED; - return state; + return CAIRO_INT_STATUS_UNSUPPORTED; } -/** - * 1) Tears down internal state used to draw the source - * 2) Does CGContextRestoreGState(state->context) - */ static void -_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state) +_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state, + cairo_composite_rectangles_t *extents) { - if (state->image) { - CGImageRelease(state->image); + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface; + + if (state->layer) { + CGContextDrawLayerInRect (surface->cgContext, + state->clipRect, + state->layer); + CGLayerRelease (state->layer); } - if (state->imageSurface) { - cairo_surface_destroy(state->imageSurface); - } + if (state->cgMaskContext) + CGContextRestoreGState (surface->cgContext); - if (state->shading) { - CGShadingRelease(state->shading); - } + if (state->image) + CGImageRelease (state->image); - if (state->pattern) { - CGPatternRelease(state->pattern); - } - - CGContextRestoreGState(state->context); + if (state->shading) + CGShadingRelease (state->shading); } - static void -_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op) +_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state, + cairo_operator_t op) { - assert (state && - ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) || - (state->layer && state->action == DO_LAYER))); + CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone); + CGContextSetInterpolationQuality(state->cgDrawContext, state->filter); - CGContextConcatCTM (state->context, state->transform); - CGContextTranslateCTM (state->context, 0, state->imageRect.size.height); - CGContextScaleCTM (state->context, 1, -1); + if (state->action == DO_DIRECT) { + CGContextFillRect (state->cgDrawContext, state->rect); + return; + } - if (state->action == DO_TILED_IMAGE) { - CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image); - /* no need to worry about unbounded operators, since tiled images - fill the entire clip region */ - } else { - if (state->action == DO_LAYER) { - /* Note that according to Apple docs it's completely legal - * to draw a CGLayer to any CGContext, even one it wasn't - * created for. - */ - CGContextDrawLayerAtPoint (state->context, state->imageRect.origin, - state->layer); - } else { - CGContextDrawImage (state->context, state->imageRect, state->image); - } + CGContextConcatCTM (state->cgDrawContext, state->transform); - /* disable this EXTEND_NONE correctness code because we use this path - * for both EXTEND_NONE and EXTEND_PAD */ - if (0 && !_cairo_operator_bounded_by_source (op)) { - CGContextBeginPath (state->context); - CGContextAddRect (state->context, state->imageRect); - CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context)); - CGContextSetRGBFillColor (state->context, 0, 0, 0, 0); - CGContextEOFillPath (state->context); + if (state->action == DO_SHADING) { + CGContextDrawShading (state->cgDrawContext, state->shading); + return; + } + + CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); + CGContextScaleCTM (state->cgDrawContext, 1, -1); + + if (state->action == DO_IMAGE) { + CGContextDrawImage (state->cgDrawContext, state->rect, state->image); + if (op == CAIRO_OPERATOR_SOURCE && + state->cgDrawContext == state->cgMaskContext) + { + CGContextBeginPath (state->cgDrawContext); + CGContextAddRect (state->cgDrawContext, state->rect); + + CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); + CGContextScaleCTM (state->cgDrawContext, 1, -1); + CGContextConcatCTM (state->cgDrawContext, + CGAffineTransformInvert (state->transform)); + + CGContextAddRect (state->cgDrawContext, state->clipRect); + + CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0); + CGContextEOFillPath (state->cgDrawContext); } + } else { + CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image); } } +static cairo_image_surface_t * +_cairo_quartz_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + unsigned int stride, bitinfo, bpp, color_comps; + CGColorSpaceRef colorspace; + void *imageData; + cairo_format_t format; -/* - * get source/dest image implementation - */ + if (surface->imageSurfaceEquiv) + return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents); + + if (IS_EMPTY (surface)) + return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); + + if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) + return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext); + bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); + + // let's hope they don't add YUV under us + colorspace = CGBitmapContextGetColorSpace (surface->cgContext); + color_comps = CGColorSpaceGetNumberOfComponents (colorspace); + + /* XXX TODO: We can handle many more data formats by + * converting to pixman_format_t */ + + if (bpp == 32 && color_comps == 3 && + (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst && + (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) + { + format = CAIRO_FORMAT_ARGB32; + } + else if (bpp == 32 && color_comps == 3 && + (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst && + (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) + { + format = CAIRO_FORMAT_RGB24; + } + else if (bpp == 8 && color_comps == 1) + { + format = CAIRO_FORMAT_A1; + } + else + { + return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + imageData = CGBitmapContextGetData (surface->cgContext); + stride = CGBitmapContextGetBytesPerRow (surface->cgContext); + + return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData, + format, + extents->width, + extents->height, + stride); +} -/* Read the image from the surface's front buffer */ static cairo_int_status_t -_cairo_quartz_get_image (cairo_quartz_surface_t *surface, - cairo_image_surface_t **image_out) +_cairo_quartz_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) { - unsigned char *imageData; - cairo_image_surface_t *isurf; + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - if (IS_EMPTY(surface)) { - *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); - return CAIRO_STATUS_SUCCESS; - } + if (surface->imageSurfaceEquiv) + return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image); - if (surface->imageSurfaceEquiv) { - CGContextFlush(surface->cgContext); - *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv); - return CAIRO_STATUS_SUCCESS; - } + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); - if (_cairo_quartz_is_cgcontext_bitmap_context(surface->cgContext)) { - unsigned int stride; - unsigned int bitinfo; - unsigned int bpc, bpp; - CGColorSpaceRef colorspace; - unsigned int color_comps; - - CGContextFlush(surface->cgContext); - imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext); - -#ifdef USE_10_3_WORKAROUNDS - bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext); -#else - bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext); -#endif - stride = CGBitmapContextGetBytesPerRow (surface->cgContext); - bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext); - bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext); - - // let's hope they don't add YUV under us - colorspace = CGBitmapContextGetColorSpace (surface->cgContext); - color_comps = CGColorSpaceGetNumberOfComponents(colorspace); - - // XXX TODO: We can handle all of these by converting to - // pixman masks, including non-native-endian masks - if (bpc != 8) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (bpp != 32 && bpp != 8) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (color_comps != 3 && color_comps != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (bpp == 32 && color_comps == 3 && - (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst && - (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_ARGB32, - surface->extents.width, - surface->extents.height, - stride); - } else if (bpp == 32 && color_comps == 3 && - (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst && - (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_RGB24, - surface->extents.width, - surface->extents.height, - stride); - } else if (bpp == 8 && color_comps == 1) - { - isurf = (cairo_image_surface_t *) - cairo_image_surface_create_for_data (imageData, - CAIRO_FORMAT_A8, - surface->extents.width, - surface->extents.height, - stride); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *image_out = isurf; return CAIRO_STATUS_SUCCESS; } + /* * Cairo surface backend implementations */ @@ -2015,9 +1528,9 @@ _cairo_quartz_surface_finish (void *abstract_surface) { cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); + ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext)); - if (IS_EMPTY(surface)) + if (IS_EMPTY (surface)) return CAIRO_STATUS_SUCCESS; /* Restore our saved gstate that we use to reset clipping */ @@ -2028,119 +1541,54 @@ _cairo_quartz_surface_finish (void *abstract_surface) surface->cgContext = NULL; - if (surface->bitmapContextImage) { - CGImageRelease (surface->bitmapContextImage); - surface->bitmapContextImage = NULL; - } - if (surface->imageSurfaceEquiv) { - if (surface->ownsData) - _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv); cairo_surface_destroy (surface->imageSurfaceEquiv); surface->imageSurfaceEquiv = NULL; - } else if (surface->imageData && surface->ownsData) { - free (surface->imageData); } + free (surface->imageData); surface->imageData = NULL; return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_quartz_surface_acquire_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) +_cairo_quartz_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - cairo_int_status_t status; cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface)); + *image_extra = NULL; - /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */ - - status = _cairo_quartz_get_image (surface, image_out); - - if (status) + *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents); + if (unlikely (cairo_surface_status(&(*image_out)->base))) { + cairo_surface_destroy (&(*image_out)->base); + *image_out = NULL; return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } return CAIRO_STATUS_SUCCESS; } static void _cairo_quartz_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) + cairo_image_surface_t *image, + void *image_extra) { - cairo_surface_destroy ((cairo_surface_t *) image); - - if (image_extra) { - cairo_surface_destroy ((cairo_surface_t *) image_extra); - } -} - - -static cairo_status_t -_cairo_quartz_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, - void **image_extra) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - - ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface)); - - *image_rect = surface->extents; - *image_extra = NULL; - - _cairo_quartz_surface_will_change (surface); - - return _cairo_quartz_surface_acquire_image (abstract_surface, - image_out, image_extra); -} - -static void -_cairo_quartz_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) -{ - /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */ - - cairo_surface_destroy ((cairo_surface_t *) image); - - if (image_extra) { - /* we need to write the data from the temp surface back to the layer */ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra; - CGImageRef img; - cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img); - if (status) { - cairo_surface_destroy (&tmp_surface->base); - return; - } - - CGContextSaveGState (surface->cgContext); - CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height); - CGContextScaleCTM (surface->cgContext, 1, -1); - CGContextDrawImage (surface->cgContext, - CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height), - img); - CGContextRestoreGState (surface->cgContext); - - cairo_surface_destroy (&tmp_surface->base); - } + _cairo_quartz_surface_unmap_image (abstract_surface, image); } static cairo_surface_t * _cairo_quartz_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) + cairo_content_t content, + int width, + int height) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + cairo_quartz_surface_t *surface, *similar_quartz; + cairo_surface_t *similar; cairo_format_t format; if (content == CAIRO_CONTENT_COLOR_ALPHA) @@ -2153,99 +1601,20 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, return NULL; // verify width and height of surface - if (!_cairo_quartz_verify_surface_size(width, height)) { + if (!_cairo_quartz_verify_surface_size (width, height)) { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } - return cairo_quartz_surface_create (format, width, height); -} + similar = cairo_quartz_surface_create (format, width, height); + if (unlikely (similar->status)) + return similar; -static cairo_status_t -_cairo_quartz_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_quartz_surface_t *new_surface = NULL; - cairo_format_t new_format; - CGImageRef quartz_image = NULL; - cairo_status_t status; + surface = (cairo_quartz_surface_t *) abstract_surface; + similar_quartz = (cairo_quartz_surface_t *) similar; + similar_quartz->virtual_extents = surface->virtual_extents; - *clone_out = NULL; - - // verify width and height of surface - if (!_cairo_quartz_verify_surface_size(width, height)) { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (width == 0 || height == 0) { - *clone_out = (cairo_surface_t*) - _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, - width, height); - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (_cairo_surface_is_quartz (src)) { - cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src; - - if (IS_EMPTY(qsurf)) { - *clone_out = (cairo_surface_t*) - _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, - qsurf->extents.width, qsurf->extents.height); - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - } - - status = _cairo_surface_to_cgimage (src, &quartz_image); - if (status) - return CAIRO_INT_STATUS_UNSUPPORTED; - - new_format = CAIRO_FORMAT_ARGB32; /* assumed */ - if (_cairo_surface_is_image (src)) { - new_format = ((cairo_image_surface_t *) src)->format; - } - - new_surface = (cairo_quartz_surface_t *) - cairo_quartz_surface_create (new_format, width, height); - - if (quartz_image == NULL) - goto FINISH; - - if (!new_surface || new_surface->base.status) { - CGImageRelease (quartz_image); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - CGContextSaveGState (new_surface->cgContext); - - CGContextSetCompositeOperation (new_surface->cgContext, - kPrivateCGCompositeCopy); - - CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y); - CGContextDrawImage (new_surface->cgContext, - CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)), - quartz_image); - - CGContextRestoreGState (new_surface->cgContext); - - CGImageRelease (quartz_image); - -FINISH: - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = (cairo_surface_t*) new_surface; - - return CAIRO_STATUS_SUCCESS; + return similar; } static cairo_bool_t @@ -2259,512 +1628,395 @@ _cairo_quartz_surface_get_extents (void *abstract_surface, } static cairo_int_status_t -_cairo_quartz_surface_paint_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) +_cairo_quartz_cg_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; cairo_quartz_drawing_state_t state; - - ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - state = _cairo_quartz_setup_state (surface, source, op, NULL); - - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - CGContextFillRect (state.context, CGRectMake(surface->extents.x, - surface->extents.y, - surface->extents.width, - surface->extents.height)); - } else if (state.action == DO_SHADING) { - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - _cairo_quartz_draw_image (&state, op); - } else if (state.action != DO_NOTHING) { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } - - _cairo_quartz_teardown_state (&state); - - ND((stderr, "-- paint\n")); - return rv; -} - -static cairo_bool_t -_cairo_quartz_source_needs_extents (const cairo_pattern_t *source) -{ - /* For repeating gradients we need to manually extend the gradient and - repeat stops, since Quartz doesn't support repeating gradients natively. - We need to minimze the number of repeated stops, and since rasterization - depends on the number of repetitions we use (even if some of the - repetitions go beyond the extents of the object or outside the clip - region), it's important to use the same number of repetitions when - rendering an object no matter what the clip region is. So the - computation of the repetition count cannot depended on the clip region, - and should only depend on the object extents, so we need to compute - the object extents for repeating gradients. */ - return (source->type == CAIRO_PATTERN_TYPE_LINEAR || - source->type == CAIRO_PATTERN_TYPE_RADIAL) && - (source->extend == CAIRO_EXTEND_REPEAT || - source->extend == CAIRO_EXTEND_REFLECT); -} - -static cairo_int_status_t -_cairo_quartz_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; cairo_int_status_t rv; - cairo_image_surface_t *image; - rv = _cairo_quartz_surface_paint_cg (abstract_surface, - op, - source, - clip); + ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", + extents->surface, extents->op, extents->source_pattern.base.type)); - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; + rv = _cairo_quartz_setup_state (&state, extents); + if (unlikely (rv)) + goto BAIL; - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_paint (&image->base, op, source, clip); - cairo_surface_destroy (&image->base); - } + _cairo_quartz_draw_source (&state, extents->op); +BAIL: + _cairo_quartz_teardown_state (&state, extents); + + ND ((stderr, "-- paint\n")); return rv; } static cairo_int_status_t -_cairo_quartz_surface_fill_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) +_cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents, + cairo_surface_t *mask_surf, + const cairo_matrix_t *mask_mat, + CGInterpolationQuality filter) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + CGRect rect; + CGImageRef img; + cairo_status_t status; + CGAffineTransform mask_matrix; cairo_quartz_drawing_state_t state; - CGPathRef path_for_unbounded = NULL; + cairo_format_t format = _cairo_format_from_content (extents->surface->content); + cairo_rectangle_int_t dest_extents; + cairo_matrix_t m = *mask_mat; - ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type)); + _cairo_surface_get_extents (extents->surface, &dest_extents); + status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format, + &m, extents->clip, &img); + if (unlikely (status)) + return status; - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; + status = _cairo_quartz_setup_state (&state, extents); + if (unlikely (status)) + goto BAIL; - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; + rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); + _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix); - if (_cairo_quartz_source_needs_extents (source)) - { - /* We don't need precise extents since these are only used to - compute the number of gradient reptitions needed to cover the - object. */ - cairo_rectangle_int_t path_extents; - _cairo_path_fixed_approximate_fill_extents (path, &path_extents); - state = _cairo_quartz_setup_state (surface, source, op, &path_extents); - } else { - state = _cairo_quartz_setup_state (surface, source, op, NULL); - } + /* ClipToMask is essentially drawing an image, so we need to flip the CTM + * to get the image to appear oriented the right way */ + CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix)); + CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height); + CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0); - CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE)); + state.filter = filter; - _cairo_quartz_cairo_path_to_quartz_context (path, state.context); + CGContextSetInterpolationQuality (state.cgMaskContext, filter); + CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone); - if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr) - path_for_unbounded = CGContextCopyPathPtr (state.context); + CGContextClipToMask (state.cgMaskContext, rect, img); - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextFillPath (state.context); - else - CGContextEOFillPath (state.context); - } else if (state.action == DO_SHADING) { + CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0); + CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height); + CGContextConcatCTM (state.cgMaskContext, mask_matrix); - // we have to clip and then paint the shading; we can't fill - // with the shading - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (state.context); - else - CGContextEOClip (state.context); + _cairo_quartz_draw_source (&state, extents->op); - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextClip (state.context); - else - CGContextEOClip (state.context); +BAIL: + _cairo_quartz_teardown_state (&state, extents); - _cairo_quartz_draw_image (&state, op); - } else if (state.action != DO_NOTHING) { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - } + CGImageRelease (img); - _cairo_quartz_teardown_state (&state); - - if (path_for_unbounded) { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_STROKE_FILL; - ub.u.stroke_fill.cgPath = path_for_unbounded; - ub.u.stroke_fill.fill_rule = fill_rule; - - _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias); - CGPathRelease (path_for_unbounded); - } - - ND((stderr, "-- fill\n")); - return rv; + return status; } static cairo_int_status_t -_cairo_quartz_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) +_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface, + cairo_composite_rectangles_t *extents) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_fill_cg (abstract_surface, - op, - source, - path, - fill_rule, - tolerance, - antialias, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_fill (&image->base, op, source, - path, fill_rule, tolerance, antialias, - clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_stroke_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; cairo_quartz_drawing_state_t state; - CGAffineTransform origCTM, strokeTransform; - CGPathRef path_for_unbounded = NULL; + double alpha = extents->mask_pattern.solid.color.alpha; + cairo_status_t status; - ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type)); + status = _cairo_quartz_setup_state (&state, extents); + if (unlikely (status)) + return status; - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; + CGContextSetAlpha (surface->cgContext, alpha); + _cairo_quartz_draw_source (&state, extents->op); - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; + _cairo_quartz_teardown_state (&state, extents); - rv = _cairo_quartz_surface_set_cairo_operator (surface, op); - if (unlikely (rv)) - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; + return CAIRO_STATUS_SUCCESS; +} - if (_cairo_quartz_source_needs_extents (source)) - { - cairo_rectangle_int_t path_extents; - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents); - state = _cairo_quartz_setup_state (surface, source, op, &path_extents); - } else { - state = _cairo_quartz_setup_state (surface, source, op, NULL); +static cairo_int_status_t +_cairo_quartz_cg_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface; + const cairo_pattern_t *source = &extents->source_pattern.base; + const cairo_pattern_t *mask = &extents->mask_pattern.base; + cairo_surface_t *mask_surf; + cairo_matrix_t matrix; + cairo_status_t status; + cairo_bool_t need_temp; + CGInterpolationQuality filter; + + ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", + extents->surface, extents->op, extents->source_pattern.base.type, + extents->mask_pattern.base.type)); + + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) + return _cairo_quartz_cg_mask_with_solid (surface, extents); + + need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE || + mask->extend != CAIRO_EXTEND_NONE); + + filter = _cairo_quartz_filter_to_quartz (source->filter); + + if (! need_temp) { + mask_surf = extents->mask_pattern.surface.surface; + + /* When an opaque surface used as a mask in Quartz, its + * luminosity is used as the alpha value, so we con only use + * surfaces with alpha without creating a temporary mask. */ + need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA); } + if (! need_temp) { + CGInterpolationQuality mask_filter; + cairo_bool_t simple_transform; + + matrix = mask->matrix; + + mask_filter = _cairo_quartz_filter_to_quartz (mask->filter); + if (mask_filter == kCGInterpolationNone) { + simple_transform = _cairo_matrix_is_translation (&matrix); + if (simple_transform) { + matrix.x0 = ceil (matrix.x0 - 0.5); + matrix.y0 = ceil (matrix.y0 - 0.5); + } + } else { + simple_transform = _cairo_matrix_is_integer_translation (&matrix, + NULL, + NULL); + } + + /* Quartz only allows one interpolation to be set for mask and + * source, so we can skip the temp surface only if the source + * filtering makes the mask look correct. */ + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) + need_temp = ! (simple_transform || filter == mask_filter); + else + filter = mask_filter; + } + + if (need_temp) { + /* Render the mask to a surface */ + mask_surf = _cairo_quartz_surface_create_similar (surface, + CAIRO_CONTENT_ALPHA, + surface->extents.width, + surface->extents.height); + status = mask_surf->status; + if (unlikely (status)) + goto BAIL; + + /* mask_surf is clear, so use OVER instead of SOURCE to avoid a + * temporary layer or fallback to cairo-image. */ + status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL); + if (unlikely (status)) + goto BAIL; + + cairo_matrix_init_identity (&matrix); + } + + status = _cairo_quartz_cg_mask_with_surface (extents, + mask_surf, &matrix, filter); + +BAIL: + + if (need_temp) + cairo_surface_destroy (mask_surf); + + return status; +} + +static cairo_int_status_t +_cairo_quartz_cg_fill (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_quartz_drawing_state_t state; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + + ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", + extents->surface, extents->op, extents->source_pattern.base.type)); + + rv = _cairo_quartz_setup_state (&state, extents); + if (unlikely (rv)) + goto BAIL; + + CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE)); + + _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext); + + if (state.action == DO_DIRECT) { + assert (state.cgDrawContext == state.cgMaskContext); + if (fill_rule == CAIRO_FILL_RULE_WINDING) + CGContextFillPath (state.cgMaskContext); + else + CGContextEOFillPath (state.cgMaskContext); + } else { + if (fill_rule == CAIRO_FILL_RULE_WINDING) + CGContextClip (state.cgMaskContext); + else + CGContextEOClip (state.cgMaskContext); + + _cairo_quartz_draw_source (&state, extents->op); + } + +BAIL: + _cairo_quartz_teardown_state (&state, extents); + + ND ((stderr, "-- fill\n")); + return rv; +} + +static cairo_int_status_t +_cairo_quartz_cg_stroke (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + 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_quartz_drawing_state_t state; + cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + CGAffineTransform strokeTransform, invStrokeTransform; + + ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", + extents->surface, extents->op, extents->source_pattern.base.type)); + + rv = _cairo_quartz_setup_state (&state, extents); + if (unlikely (rv)) + goto BAIL; + // Turning antialiasing off used to cause misrendering with // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels). // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases. - CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE)); - CGContextSetLineWidth (state.context, style->line_width); - CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); - CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); - CGContextSetMiterLimit (state.context, style->miter_limit); - - origCTM = CGContextGetCTM (state.context); + CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE)); + CGContextSetLineWidth (state.cgMaskContext, style->line_width); + CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); + CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); + CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit); if (style->dash && style->num_dashes) { -#define STATIC_DASH 32 - cairo_quartz_float_t sdash[STATIC_DASH]; + cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)]; cairo_quartz_float_t *fdash = sdash; unsigned int max_dashes = style->num_dashes; unsigned int k; - bool set_line_dash = false; - if (style->num_dashes % 2 == 0) { - for (k = 1; k < max_dashes; k++) { - if (style->dash[k]) { - set_line_dash = true; - break; - } - } - } else - set_line_dash = true; + if (style->num_dashes%2) + max_dashes *= 2; + if (max_dashes > ARRAY_LENGTH (sdash)) + fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t)); + if (unlikely (fdash == NULL)) { + rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } - if (set_line_dash) { - if (style->num_dashes%2) - max_dashes *= 2; - if (max_dashes > STATIC_DASH) - fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t)); - if (fdash == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + for (k = 0; k < max_dashes; k++) + fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes]; - for (k = 0; k < max_dashes; k++) - fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes]; - - CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes); - if (fdash != sdash) - free (fdash); - } else - CGContextSetLineDash (state.context, 0, NULL, 0); + CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes); + if (fdash != sdash) + free (fdash); } else - CGContextSetLineDash (state.context, 0, NULL, 0); + CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0); - - _cairo_quartz_cairo_path_to_quartz_context (path, state.context); + _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext); _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); - CGContextConcatCTM (state.context, strokeTransform); + CGContextConcatCTM (state.cgMaskContext, strokeTransform); - if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr) - path_for_unbounded = CGContextCopyPathPtr (state.context); + if (state.action == DO_DIRECT) { + assert (state.cgDrawContext == state.cgMaskContext); + CGContextStrokePath (state.cgMaskContext); + } else { + CGContextReplacePathWithStrokedPath (state.cgMaskContext); + CGContextClip (state.cgMaskContext); - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - CGContextStrokePath (state.context); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - CGContextReplacePathWithStrokedPath (state.context); - CGContextClip (state.context); + _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform); + CGContextConcatCTM (state.cgMaskContext, invStrokeTransform); - CGContextSetCTM (state.context, origCTM); - _cairo_quartz_draw_image (&state, op); - } else if (state.action == DO_SHADING) { - CGContextReplacePathWithStrokedPath (state.context); - CGContextClip (state.context); - - CGContextSetCTM (state.context, origCTM); - - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } else if (state.action != DO_NOTHING) { - rv = CAIRO_INT_STATUS_UNSUPPORTED; - goto BAIL; + _cairo_quartz_draw_source (&state, extents->op); } - if (path_for_unbounded) { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_STROKE_FILL; - ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING; - - CGContextBeginPath (state.context); - CGContextAddPath (state.context, path_for_unbounded); - CGPathRelease (path_for_unbounded); - - CGContextSaveGState (state.context); - CGContextConcatCTM (state.context, strokeTransform); - CGContextReplacePathWithStrokedPath (state.context); - CGContextRestoreGState (state.context); - - ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context); - - _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias); - CGPathRelease (ub.u.stroke_fill.cgPath); - } - - BAIL: - _cairo_quartz_teardown_state (&state); - - ND((stderr, "-- stroke\n")); - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_stroke_cg (abstract_surface, op, source, - path, style, ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_stroke (&image->base, op, source, - path, style, ctm, ctm_inverse, - tolerance, antialias, - clip); - cairo_surface_destroy (&image->base); - } +BAIL: + _cairo_quartz_teardown_state (&state, extents); + ND ((stderr, "-- stroke\n")); return rv; } #if CAIRO_HAS_QUARTZ_FONT static cairo_int_status_t -_cairo_quartz_surface_show_glyphs_cg (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, - cairo_clip_t *clip, - int *remaining_glyphs) +_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) { - CGAffineTransform textTransform, ctm, invTextTransform; -#define STATIC_BUF_SIZE 64 - CGGlyph glyphs_static[STATIC_BUF_SIZE]; - CGSize cg_advances_static[STATIC_BUF_SIZE]; + CGAffineTransform textTransform, invTextTransform; + CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; + CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; CGGlyph *cg_glyphs = &glyphs_static[0]; - /* We'll use the cg_advances array for either advances or positions, - depending which API we're using to actually draw. The types involved - have the same size, so this is safe. */ CGSize *cg_advances = &cg_advances_static[0]; + COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); - cairo_rectangle_int_t glyph_extents; - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; cairo_quartz_drawing_state_t state; + cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; cairo_quartz_float_t xprev, yprev; int i; CGFontRef cgfref = NULL; - cairo_bool_t isClipping = FALSE; cairo_bool_t didForceFontSmoothing = FALSE; - cairo_antialias_t effective_antialiasing; - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - if (num_glyphs <= 0) - return CAIRO_STATUS_SUCCESS; if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) return CAIRO_INT_STATUS_UNSUPPORTED; - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) - return rv; - - if (_cairo_quartz_source_needs_extents (source) && - !_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, - &glyph_extents, NULL)) - { - state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents); - } else { - state = _cairo_quartz_setup_state (surface, source, op, NULL); - } - - if (state.action == DO_SOLID || state.action == DO_PATTERN) { - CGContextSetTextDrawingMode (state.context, kCGTextFill); - } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_SHADING || state.action == DO_LAYER) { - CGContextSetTextDrawingMode (state.context, kCGTextClip); - isClipping = TRUE; - } else { - if (state.action != DO_NOTHING) - rv = CAIRO_INT_STATUS_UNSUPPORTED; goto BAIL; + + if (state.action == DO_DIRECT) { + assert (state.cgDrawContext == state.cgMaskContext); + CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill); + } else { + CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip); } /* this doesn't addref */ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); - CGContextSetFont (state.context, cgfref); - CGContextSetFontSize (state.context, 1.0); - - effective_antialiasing = scaled_font->options.antialias; - if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && - !surface->base.permit_subpixel_antialiasing) { - effective_antialiasing = CAIRO_ANTIALIAS_GRAY; - } + CGContextSetFont (state.cgMaskContext, cgfref); + CGContextSetFontSize (state.cgMaskContext, 1.0); switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_SUBPIXEL: - CGContextSetShouldAntialias (state.context, TRUE); - CGContextSetShouldSmoothFonts (state.context, TRUE); + case CAIRO_ANTIALIAS_BEST: + CGContextSetShouldAntialias (state.cgMaskContext, TRUE); + CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE); if (CGContextSetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (state.context)) + !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) { didForceFontSmoothing = TRUE; - CGContextSetAllowsFontSmoothingPtr (state.context, TRUE); + CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE); } break; case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (state.context, FALSE); + CGContextSetShouldAntialias (state.cgMaskContext, FALSE); break; case CAIRO_ANTIALIAS_GRAY: - CGContextSetShouldAntialias (state.context, TRUE); - CGContextSetShouldSmoothFonts (state.context, FALSE); + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: + CGContextSetShouldAntialias (state.cgMaskContext, TRUE); + CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE); break; case CAIRO_ANTIALIAS_DEFAULT: /* Don't do anything */ break; } - if (num_glyphs > STATIC_BUF_SIZE) { - cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph)); - if (cg_glyphs == NULL) { + if (num_glyphs > ARRAY_LENGTH (glyphs_static)) { + cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize)); + if (unlikely (cg_glyphs == NULL)) { rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } - cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize)); - if (cg_advances == NULL) { - rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } + cg_advances = (CGSize*) (cg_glyphs + num_glyphs); } /* scale(1,-1) * scaled_font->scale */ @@ -2772,7 +2024,7 @@ _cairo_quartz_surface_show_glyphs_cg (void *abstract_surface, scaled_font->scale.yx, -scaled_font->scale.xy, -scaled_font->scale.yy, - 0, 0); + 0.0, 0.0); /* scaled_font->scale_inverse * scale(1,-1) */ invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx, @@ -2781,323 +2033,136 @@ _cairo_quartz_surface_show_glyphs_cg (void *abstract_surface, -scaled_font->scale_inverse.yy, 0.0, 0.0); - CGContextSetTextMatrix (state.context, CGAffineTransformIdentity); + CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0); + CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity); + + /* Convert our glyph positions to glyph advances. We need n-1 advances, + * since the advance at index 0 is applied after glyph 0. */ + xprev = glyphs[0].x; + yprev = glyphs[0].y; + + cg_glyphs[0] = glyphs[0].index; + + for (i = 1; i < num_glyphs; i++) { + cairo_quartz_float_t xf = glyphs[i].x; + cairo_quartz_float_t yf = glyphs[i].y; + cg_glyphs[i] = glyphs[i].index; + cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform); + xprev = xf; + yprev = yf; + } /* Translate to the first glyph's position before drawing */ - ctm = CGContextGetCTM (state.context); - CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y); - CGContextConcatCTM (state.context, textTransform); + CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); + CGContextConcatCTM (state.cgMaskContext, textTransform); - if (CTFontDrawGlyphsPtr) { - /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use - * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap - * fonts like Apple Color Emoji will render properly. - * For this, we need to convert our glyph positions to Core Graphics's CGPoint. - * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */ + CGContextShowGlyphsWithAdvances (state.cgMaskContext, + cg_glyphs, + cg_advances, + num_glyphs); - CGPoint *cg_positions = (CGPoint*) cg_advances; - cairo_quartz_float_t origin_x = glyphs[0].x; - cairo_quartz_float_t origin_y = glyphs[0].y; + CGContextConcatCTM (state.cgMaskContext, invTextTransform); + CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); - for (i = 0; i < num_glyphs; i++) { - CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y); - cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform); - cg_glyphs[i] = glyphs[i].index; - } - - CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font), - cg_glyphs, cg_positions, num_glyphs, state.context); - } else { - /* Convert our glyph positions to glyph advances. We need n-1 advances, - * since the advance at index 0 is applied after glyph 0. */ - xprev = glyphs[0].x; - yprev = glyphs[0].y; - - cg_glyphs[0] = glyphs[0].index; - - for (i = 1; i < num_glyphs; i++) { - cairo_quartz_float_t xf = glyphs[i].x; - cairo_quartz_float_t yf = glyphs[i].y; - cg_glyphs[i] = glyphs[i].index; - cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform); - xprev = xf; - yprev = yf; - } - - CGContextShowGlyphsWithAdvances (state.context, - cg_glyphs, - cg_advances, - num_glyphs); - } - - CGContextSetCTM (state.context, ctm); - - if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || - state.action == DO_LAYER) { - _cairo_quartz_draw_image (&state, op); - } else if (state.action == DO_SHADING) { - CGContextConcatCTM (state.context, state.transform); - CGContextDrawShading (state.context, state.shading); - } + if (state.action != DO_DIRECT) + _cairo_quartz_draw_source (&state, extents->op); BAIL: if (didForceFontSmoothing) - CGContextSetAllowsFontSmoothingPtr (state.context, FALSE); + CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); - _cairo_quartz_teardown_state (&state); + _cairo_quartz_teardown_state (&state, extents); - if (rv == CAIRO_STATUS_SUCCESS && - cgfref && - !_cairo_operator_bounded_by_mask (op)) - { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_SHOW_GLYPHS; - - ub.u.show_glyphs.isClipping = isClipping; - ub.u.show_glyphs.cg_glyphs = cg_glyphs; - if (CTFontDrawGlyphsPtr) { - /* we're using Core Text API: the cg_advances array was - reused (above) for glyph positions */ - CGPoint *cg_positions = (CGPoint*) cg_advances; - ub.u.show_glyphs.u.cg_positions = cg_positions; - } else { - ub.u.show_glyphs.u.cg_advances = cg_advances; - } - ub.u.show_glyphs.nglyphs = num_glyphs; - ub.u.show_glyphs.textTransform = textTransform; - ub.u.show_glyphs.scaled_font = scaled_font; - ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y); - - _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias); - } - - - if (cg_advances != &cg_advances_static[0]) { - free (cg_advances); - } - - if (cg_glyphs != &glyphs_static[0]) { + if (cg_glyphs != glyphs_static) free (cg_glyphs); - } return rv; } #endif /* CAIRO_HAS_QUARTZ_FONT */ -static cairo_int_status_t -_cairo_quartz_surface_show_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, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_image_surface_t *image; +static const cairo_compositor_t _cairo_quartz_cg_compositor = { + &_cairo_fallback_compositor, + _cairo_quartz_cg_paint, + _cairo_quartz_cg_mask, + _cairo_quartz_cg_stroke, + _cairo_quartz_cg_fill, #if CAIRO_HAS_QUARTZ_FONT - rv = _cairo_quartz_surface_show_glyphs_cg (abstract_surface, op, source, - glyphs, num_glyphs, - scaled_font, clip, remaining_glyphs); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - + _cairo_quartz_cg_glyphs, +#else + NULL, #endif - - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_show_text_glyphs (&image->base, op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} +}; static cairo_int_status_t -_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_surface_pattern_t *mask, - cairo_clip_t *clip) +_cairo_quartz_surface_paint (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) { - CGRect rect; - CGImageRef img; - cairo_surface_t *pat_surf = mask->surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - CGAffineTransform ctm, mask_matrix; - - status = _cairo_surface_to_cgimage (pat_surf, &img); - if (status) - return status; - if (img == NULL) { - if (!_cairo_operator_bounded_by_mask (op)) - CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); - return CAIRO_STATUS_SUCCESS; - } - - rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img)); - - CGContextSaveGState (surface->cgContext); - - /* ClipToMask is essentially drawing an image, so we need to flip the CTM - * to get the image to appear oriented the right way */ - ctm = CGContextGetCTM (surface->cgContext); - - _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix); - mask_matrix = CGAffineTransformInvert(mask_matrix); - mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img)); - mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0); - - CGContextConcatCTM (surface->cgContext, mask_matrix); - CGContextClipToMaskPtr (surface->cgContext, rect, img); - - CGContextSetCTM (surface->cgContext, ctm); - - status = _cairo_quartz_surface_paint_cg (surface, op, source, clip); - - CGContextRestoreGState (surface->cgContext); - - if (!_cairo_operator_bounded_by_mask (op)) { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_MASK; - ub.u.mask.mask = img; - ub.u.mask.maskTransform = mask_matrix; - _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE); - } - - CGImageRelease (img); - - return status; -} - -/* This is somewhat less than ideal, but it gets the job done; - * it would be better to avoid calling back into cairo. This - * creates a temporary surface to use as the mask. - */ -static cairo_int_status_t -_cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - int width = surface->extents.width; - int height = surface->extents.height; - - cairo_surface_t *gradient_surf = NULL; - cairo_surface_pattern_t surface_pattern; - cairo_int_status_t status; - - /* Render the gradient to a surface */ - gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_A8, - width, - height); - - status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL); - if (status) - goto BAIL; - - _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf); - - status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, clip); - - _cairo_pattern_fini (&surface_pattern.base); - - BAIL: - if (gradient_surf) - cairo_surface_destroy (gradient_surf); - - return status; + return _cairo_compositor_paint (&_cairo_quartz_cg_compositor, + surface, op, source, clip); } static cairo_int_status_t -_cairo_quartz_surface_mask_cg (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - - ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type)); - - if (IS_EMPTY(surface)) - return CAIRO_STATUS_SUCCESS; - - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */ - if (mask->type == CAIRO_PATTERN_TYPE_SOLID && - op == CAIRO_OPERATOR_OVER) { - /* This is easy; we just need to paint with the alpha. */ - cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; - - CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); - rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip); - CGContextSetAlpha (surface->cgContext, 1.0); - - return rv; - } - - /* If we have CGContextClipToMask, we can do more complex masks */ - if (CGContextClipToMaskPtr) { - /* For these, we can skip creating a temporary surface, since we already have one */ - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && - mask->extend == CAIRO_EXTEND_NONE) { - return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); - } - - return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); - } - - /* So, CGContextClipToMask is not present in 10.3.9, so we're - * doomed; if we have imageData, we can do fallback, otherwise - * just pretend success. - */ - if (surface->imageData) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_quartz_surface_mask (void *abstract_surface, +_cairo_quartz_surface_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; + return _cairo_compositor_mask (&_cairo_quartz_cg_compositor, + surface, op, source, mask, + clip); +} - rv = _cairo_quartz_surface_mask_cg (abstract_surface, - op, - source, - mask, - clip); +static cairo_int_status_t +_cairo_quartz_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 (&_cairo_quartz_cg_compositor, + surface, op, source, path, + fill_rule, tolerance, antialias, + clip); +} - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; +static cairo_int_status_t +_cairo_quartz_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 (&_cairo_quartz_cg_compositor, + surface, op, source, path, + style, ctm,ctm_inverse, + tolerance, antialias, clip); +} - rv = _cairo_quartz_get_image (surface, &image); - if (rv == CAIRO_STATUS_SUCCESS) { - rv = _cairo_surface_mask (&image->base, op, source, mask, clip); - cairo_surface_destroy (&image->base); - } - - return rv; +static cairo_int_status_t +_cairo_quartz_surface_glyphs (void *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) +{ + return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor, + surface, op, source, + glyphs, num_glyphs, scaled_font, + clip); } static cairo_status_t @@ -3110,9 +2175,9 @@ _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip cairo_quartz_surface_t *surface = cairo_container_of (clipper, cairo_quartz_surface_t, clipper); - ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path)); + ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path)); - if (IS_EMPTY(surface)) + if (IS_EMPTY (surface)) return CAIRO_STATUS_SUCCESS; if (path == NULL) { @@ -3136,80 +2201,68 @@ _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip CGContextEOClip (surface->cgContext); } - ND((stderr, "-- intersect_clip_path\n")); + ND ((stderr, "-- intersect_clip_path\n")); return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface, - int x, int y, - int width, int height) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - _cairo_quartz_surface_will_change (surface); - return CAIRO_STATUS_SUCCESS; -} - - // XXXtodo implement show_page; need to figure out how to handle begin/end static const struct _cairo_surface_backend cairo_quartz_surface_backend = { CAIRO_SURFACE_TYPE_QUARTZ, - _cairo_quartz_surface_create_similar, _cairo_quartz_surface_finish, - _cairo_quartz_surface_acquire_image, + + _cairo_default_context_create, + + _cairo_quartz_surface_create_similar, + NULL, /* similar image */ + _cairo_quartz_surface_map_to_image, + _cairo_quartz_surface_unmap_image, + + _cairo_surface_default_source, + _cairo_quartz_surface_acquire_source_image, _cairo_quartz_surface_release_source_image, - _cairo_quartz_surface_acquire_dest_image, - _cairo_quartz_surface_release_dest_image, - _cairo_quartz_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_quartz_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ - _cairo_quartz_surface_mark_dirty_rectangle, - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ + NULL, /* mark_dirty_rectangle */ _cairo_quartz_surface_paint, _cairo_quartz_surface_mask, _cairo_quartz_surface_stroke, _cairo_quartz_surface_fill, - _cairo_quartz_surface_show_glyphs, - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL /* fill_stroke */ + NULL, /* fill-stroke */ + _cairo_quartz_surface_glyphs, }; cairo_quartz_surface_t * _cairo_quartz_surface_create_internal (CGContextRef cgContext, - cairo_content_t content, - unsigned int width, - unsigned int height) + cairo_content_t content, + unsigned int width, + unsigned int height) { cairo_quartz_surface_t *surface; - quartz_ensure_symbols(); + quartz_ensure_symbols (); /* Init the base surface */ - surface = malloc(sizeof(cairo_quartz_surface_t)); - if (surface == NULL) + surface = _cairo_malloc (sizeof (cairo_quartz_surface_t)); + if (unlikely (surface == NULL)) return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - memset(surface, 0, sizeof(cairo_quartz_surface_t)); + memset (surface, 0, sizeof (cairo_quartz_surface_t)); _cairo_surface_init (&surface->base, &cairo_quartz_surface_backend, NULL, /* device */ - content); + content, + FALSE); /* is_vector */ _cairo_surface_clipper_init (&surface->clipper, _cairo_quartz_surface_clipper_intersect_clip_path); @@ -3218,11 +2271,15 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, surface->extents.x = surface->extents.y = 0; surface->extents.width = width; surface->extents.height = height; + surface->virtual_extents = surface->extents; + surface->imageData = NULL; + surface->imageSurfaceEquiv = NULL; - if (IS_EMPTY(surface)) { + + if (IS_EMPTY (surface)) { surface->cgContext = NULL; surface->cgContextBaseCTM = CGAffineTransformIdentity; - surface->imageData = NULL; + surface->base.is_clear = TRUE; return surface; } @@ -3234,16 +2291,11 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, surface->cgContext = cgContext; surface->cgContextBaseCTM = CGContextGetCTM (cgContext); - surface->imageData = NULL; - surface->imageSurfaceEquiv = NULL; - surface->bitmapContextImage = NULL; - surface->ownsData = TRUE; - return surface; } /** - * cairo_quartz_surface_create_for_cg_context + * cairo_quartz_surface_create_for_cg_context: * @cgContext: the existing CGContext for which to create the surface * @width: width of the surface, in pixels * @height: height of the surface, in pixels @@ -3266,7 +2318,7 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, * * Return value: the newly created Cairo surface. * - * Since: 1.4 + * Since: 1.6 **/ cairo_surface_t * @@ -3276,21 +2328,16 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, { cairo_quartz_surface_t *surf; - CGContextRetain (cgContext); - surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA, width, height); - if (surf->base.status) { - CGContextRelease (cgContext); - // create_internal will have set an error - return (cairo_surface_t*) surf; - } + if (likely (!surf->base.status)) + CGContextRetain (cgContext); - return (cairo_surface_t *) surf; + return &surf->base; } /** - * cairo_quartz_surface_create + * cairo_quartz_surface_create: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels @@ -3302,30 +2349,45 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, * * Return value: the newly created surface. * - * Since: 1.4 + * Since: 1.6 **/ cairo_surface_t * cairo_quartz_surface_create (cairo_format_t format, unsigned int width, unsigned int height) { + cairo_quartz_surface_t *surf; + CGContextRef cgc; + CGColorSpaceRef cgColorspace; + CGBitmapInfo bitinfo; + void *imageData; int stride; - unsigned char *data; + int bitsPerComponent; - if (!_cairo_quartz_verify_surface_size(width, height)) + if (!_cairo_quartz_verify_surface_size (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); if (width == 0 || height == 0) { - return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), - width, height); + return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), + width, height)->base; } if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) { + cgColorspace = CGColorSpaceCreateDeviceRGB (); + bitinfo = kCGBitmapByteOrder32Host; + if (format == CAIRO_FORMAT_ARGB32) + bitinfo |= kCGImageAlphaPremultipliedFirst; + else + bitinfo |= kCGImageAlphaNoneSkipFirst; + bitsPerComponent = 8; stride = width * 4; } else if (format == CAIRO_FORMAT_A8) { + cgColorspace = NULL; stride = width; + bitinfo = kCGImageAlphaOnly; + bitsPerComponent = 8; } else if (format == CAIRO_FORMAT_A1) { /* I don't think we can usefully support this, as defined by * cairo_format_t -- these are 1-bit pixels stored in 32-bit @@ -3342,93 +2404,14 @@ cairo_quartz_surface_create (cairo_format_t format, */ stride = (stride + 15) & ~15; - data = _cairo_malloc_ab (height, stride); - if (!data) { + imageData = _cairo_malloc_ab (height, stride); + if (unlikely (!imageData)) { + CGColorSpaceRelease (cgColorspace); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } /* zero the memory to match the image surface behaviour */ - memset (data, 0, height * stride); - - cairo_quartz_surface_t *surf; - surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data - (data, format, width, height, stride); - if (surf->base.status) { - free (data); - return (cairo_surface_t *) surf; - } - - // We created this data, so we can delete it. - surf->ownsData = TRUE; - - return (cairo_surface_t *) surf; -} - -/** - * cairo_quartz_surface_create_for_data - * @data: a pointer to a buffer supplied by the application in which - * to write contents. This pointer must be suitably aligned for any - * kind of variable, (for example, a pointer returned by malloc). - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a Quartz surface backed by a CGBitmap. The surface is - * created using the Device RGB (or Device Gray, for A8) color space. - * All Cairo operations, including those that require software - * rendering, will succeed on this surface. - * - * Return value: the newly created surface. - * - * Since: 1.12 - **/ -cairo_surface_t * -cairo_quartz_surface_create_for_data (unsigned char *data, - cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride) -{ - cairo_quartz_surface_t *surf; - CGContextRef cgc; - CGColorSpaceRef cgColorspace; - CGBitmapInfo bitinfo; - void *imageData = data; - int bitsPerComponent; - unsigned int i; - - // verify width and height of surface - if (!_cairo_quartz_verify_surface_size(width, height)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - - if (width == 0 || height == 0) { - return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), - width, height); - } - - if (format == CAIRO_FORMAT_ARGB32 || - format == CAIRO_FORMAT_RGB24) - { - cgColorspace = CGColorSpaceCreateDeviceRGB(); - bitinfo = kCGBitmapByteOrder32Host; - if (format == CAIRO_FORMAT_ARGB32) - bitinfo |= kCGImageAlphaPremultipliedFirst; - else - bitinfo |= kCGImageAlphaNoneSkipFirst; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_A8) { - cgColorspace = NULL; - bitinfo = kCGImageAlphaOnly; - bitsPerComponent = 8; - } else if (format == CAIRO_FORMAT_A1) { - /* I don't think we can usefully support this, as defined by - * cairo_format_t -- these are 1-bit pixels stored in 32-bit - * quantities. - */ - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - } else { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - } + memset (imageData, 0, height * stride); cgc = CGBitmapContextCreate (imageData, width, @@ -3454,37 +2437,34 @@ cairo_quartz_surface_create_for_data (unsigned char *data, CGContextRelease (cgc); free (imageData); // create_internal will have set an error - return (cairo_surface_t*) surf; + return &surf->base; } + surf->base.is_clear = TRUE; + surf->imageData = imageData; + surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); - cairo_surface_t* tmpImageSurfaceEquiv = - cairo_image_surface_create_for_data (imageData, format, - width, height, stride); - - if (cairo_surface_status (tmpImageSurfaceEquiv)) { - // Tried & failed to create an imageSurfaceEquiv! - cairo_surface_destroy (tmpImageSurfaceEquiv); - surf->imageSurfaceEquiv = NULL; - } else { - surf->imageSurfaceEquiv = tmpImageSurfaceEquiv; - surf->ownsData = FALSE; - } - - return (cairo_surface_t *) surf; + return &surf->base; } /** - * cairo_quartz_surface_get_cg_context + * cairo_quartz_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. * - * Since: 1.4 + * Since: 1.6 **/ CGContextRef cairo_quartz_surface_get_cg_context (cairo_surface_t *surface) @@ -3496,35 +2476,31 @@ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface) return NULL; } -static cairo_bool_t +/** + * _cairo_surface_is_quartz: + * @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 (const cairo_surface_t *surface) { return surface->backend == &cairo_quartz_surface_backend; } -cairo_surface_t * -cairo_quartz_surface_get_image (cairo_surface_t *surface) -{ - cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface; - cairo_image_surface_t *image; - - if (_cairo_quartz_get_image(quartz, &image)) - return NULL; - - return (cairo_surface_t *)image; -} - /* Debug stuff */ #ifdef QUARTZ_DEBUG #include -void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest) +void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest) { Handle dataRef = NULL; OSType dataRefType; - CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII); + CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII); GraphicsExportComponent grex = 0; unsigned long sizeWritten; @@ -3532,35 +2508,35 @@ void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest) ComponentResult result; // create the data reference - result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle, - 0, &dataRef, &dataRefType); + result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle, + 0, &dataRef, &dataRefType); if (NULL != dataRef && noErr == result) { // get the PNG exporter - result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG, - &grex); + result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG, + &grex); if (grex) { // tell the exporter where to find its source image - result = GraphicsExportSetInputCGImage(grex, inImageRef); + result = GraphicsExportSetInputCGImage (grex, inImageRef); if (noErr == result) { // tell the exporter where to save the exporter image - result = GraphicsExportSetOutputDataReference(grex, dataRef, - dataRefType); + result = GraphicsExportSetOutputDataReference (grex, dataRef, + dataRefType); if (noErr == result) { // write the PNG file - result = GraphicsExportDoExport(grex, &sizeWritten); + result = GraphicsExportDoExport (grex, &sizeWritten); } } // remember to close the component - CloseComponent(grex); + CloseComponent (grex); } // remember to dispose of the data reference handle - DisposeHandle(dataRef); + DisposeHandle (dataRef); } } @@ -3577,7 +2553,7 @@ quartz_image_to_png (CGImageRef imgref, char *dest) dest = sptr; } - ExportCGImageToPNGFile(imgref, dest); + ExportCGImageToPNGFile (imgref, dest); } void @@ -3604,9 +2580,9 @@ quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest) return; } - ExportCGImageToPNGFile(imgref, dest); + ExportCGImageToPNGFile (imgref, dest); - CGImageRelease(imgref); + CGImageRelease (imgref); } #endif /* QUARTZ_DEBUG */ diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h index acfe9fb968ce..9e11e876fee3 100644 --- a/gfx/cairo/cairo/src/cairo-quartz.h +++ b/gfx/cairo/cairo/src/cairo-quartz.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright � 2006, 2007 Mozilla Corporation + * Copyright © 2006, 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -39,14 +39,8 @@ #include "cairo.h" #if CAIRO_HAS_QUARTZ_SURFACE -#include "TargetConditionals.h" -#if !TARGET_OS_IPHONE #include -#else -#include -#include -#endif CAIRO_BEGIN_DECLS @@ -55,13 +49,6 @@ cairo_quartz_surface_create (cairo_format_t format, unsigned int width, unsigned int height); -cairo_public cairo_surface_t * -cairo_quartz_surface_create_for_data (unsigned char *data, - cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride); - cairo_public cairo_surface_t * cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, unsigned int width, @@ -70,9 +57,6 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, cairo_public CGContextRef cairo_quartz_surface_get_cg_context (cairo_surface_t *surface); -cairo_public cairo_surface_t * -cairo_quartz_surface_get_image (cairo_surface_t *surface); - #if CAIRO_HAS_QUARTZ_FONT /* @@ -82,10 +66,8 @@ cairo_quartz_surface_get_image (cairo_surface_t *surface); cairo_public cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font); -#if !defined(__LP64__) && !TARGET_OS_IPHONE cairo_public cairo_font_face_t * cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id); -#endif #endif /* CAIRO_HAS_QUARTZ_FONT */ diff --git a/gfx/cairo/cairo/src/cairo-raster-source-pattern.c b/gfx/cairo/cairo/src/cairo-raster-source-pattern.c new file mode 100644 index 000000000000..64520feae522 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-raster-source-pattern.c @@ -0,0 +1,430 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* 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 + */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include "cairo-pattern-private.h" + +/** + * SECTION:cairo-raster-source + * @Title: Raster Sources + * @Short_Description: Supplying arbitrary image data + * @See_Also: #cairo_pattern_t + * + * The raster source provides the ability to supply arbitrary pixel data + * whilst rendering. The pixels are queried at the time of rasterisation + * by means of user callback functions, allowing for the ultimate + * flexibility. For example, in handling compressed image sources, you + * may keep a MRU cache of decompressed images and decompress sources on the + * fly and discard old ones to conserve memory. + * + * For the raster source to be effective, you must at least specify + * the acquire and release callbacks which are used to retrieve the pixel + * data for the region of interest and demark when it can be freed afterwards. + * Other callbacks are provided for when the pattern is copied temporarily + * during rasterisation, or more permanently as a snapshot in order to keep + * the pixel data available for printing. + **/ + +cairo_surface_t * +_cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern, + cairo_surface_t *target, + const cairo_rectangle_int_t *extents) +{ + cairo_raster_source_pattern_t *pattern = + (cairo_raster_source_pattern_t *) abstract_pattern; + + if (pattern->acquire == NULL) + return NULL; + + if (extents == NULL) + extents = &pattern->extents; + + return pattern->acquire (&pattern->base, pattern->user_data, + target, extents); +} + +void +_cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern, + cairo_surface_t *surface) +{ + cairo_raster_source_pattern_t *pattern = + (cairo_raster_source_pattern_t *) abstract_pattern; + + if (pattern->release == NULL) + return; + + pattern->release (&pattern->base, pattern->user_data, surface); +} + +cairo_status_t +_cairo_raster_source_pattern_init_copy (cairo_pattern_t *abstract_pattern, + const cairo_pattern_t *other) +{ + cairo_raster_source_pattern_t *pattern = + (cairo_raster_source_pattern_t *) abstract_pattern; + cairo_status_t status; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_raster_source_pattern_t))); + memcpy(pattern, other, sizeof (cairo_raster_source_pattern_t)); + + status = CAIRO_STATUS_SUCCESS; + if (pattern->copy) + status = pattern->copy (&pattern->base, pattern->user_data, other); + + return status; +} + +cairo_status_t +_cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern) +{ + cairo_raster_source_pattern_t *pattern = + (cairo_raster_source_pattern_t *) abstract_pattern; + + if (pattern->snapshot == NULL) + return CAIRO_STATUS_SUCCESS; + + return pattern->snapshot (&pattern->base, pattern->user_data); +} + +void +_cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern) +{ + cairo_raster_source_pattern_t *pattern = + (cairo_raster_source_pattern_t *) abstract_pattern; + + if (pattern->finish == NULL) + return; + + pattern->finish (&pattern->base, pattern->user_data); +} + +/* Public interface */ + +/** + * cairo_pattern_create_raster_source: + * @user_data: the user data to be passed to all callbacks + * @content: content type for the pixel data that will be returned. Knowing + * the content type ahead of time is used for analysing the operation and + * picking the appropriate rendering path. + * @width: maximum size of the sample area + * @height: maximum size of the sample area + * + * Creates a new user pattern for providing pixel data. + * + * Use the setter functions to associate callbacks with the returned + * pattern. The only mandatory callback is acquire. + * + * Return value: a newly created #cairo_pattern_t. Free with + * cairo_pattern_destroy() when you are done using it. + * + * Since: 1.12 + **/ +cairo_pattern_t * +cairo_pattern_create_raster_source (void *user_data, + cairo_content_t content, + int width, int height) +{ + cairo_raster_source_pattern_t *pattern; + + CAIRO_MUTEX_INITIALIZE (); + + if (width < 0 || height < 0) + return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_CONTENT); + + pattern = calloc (1, sizeof (*pattern)); + if (unlikely (pattern == NULL)) + return _cairo_pattern_create_in_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_pattern_init (&pattern->base, + CAIRO_PATTERN_TYPE_RASTER_SOURCE); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + pattern->content = content; + + pattern->extents.x = 0; + pattern->extents.y = 0; + pattern->extents.width = width; + pattern->extents.height = height; + + pattern->user_data = user_data; + + return &pattern->base; +} + +/** + * cairo_raster_source_pattern_set_callback_data: + * @pattern: the pattern to update + * @data: the user data to be passed to all callbacks + * + * Updates the user data that is provided to all callbacks. + * + * Since: 1.12 + **/ +void +cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *abstract_pattern, + void *data) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + pattern->user_data = data; +} + +/** + * cairo_raster_source_pattern_get_callback_data: + * @pattern: the pattern to update + * + * Queries the current user data. + * + * Return value: the current user-data passed to each callback + * + * Since: 1.12 + **/ +void * +cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *abstract_pattern) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return NULL; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + return pattern->user_data; +} + +/** + * cairo_raster_source_pattern_set_acquire: + * @pattern: the pattern to update + * @acquire: acquire callback + * @release: release callback + * + * Specifies the callbacks used to generate the image surface for a rendering + * operation (acquire) and the function used to cleanup that surface afterwards. + * + * The @acquire callback should create a surface (preferably an image + * surface created to match the target using + * cairo_surface_create_similar_image()) that defines at least the region + * of interest specified by extents. The surface is allowed to be the entire + * sample area, but if it does contain a subsection of the sample area, + * the surface extents should be provided by setting the device offset (along + * with its width and height) using cairo_surface_set_device_offset(). + * + * Since: 1.12 + **/ +void +cairo_raster_source_pattern_set_acquire (cairo_pattern_t *abstract_pattern, + cairo_raster_source_acquire_func_t acquire, + cairo_raster_source_release_func_t release) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + pattern->acquire = acquire; + pattern->release = release; +} + +/** + * cairo_raster_source_pattern_get_acquire: + * @pattern: the pattern to query + * @acquire: return value for the current acquire callback + * @release: return value for the current release callback + * + * Queries the current acquire and release callbacks. + * + * Since: 1.12 + **/ +void +cairo_raster_source_pattern_get_acquire (cairo_pattern_t *abstract_pattern, + cairo_raster_source_acquire_func_t *acquire, + cairo_raster_source_release_func_t *release) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + if (acquire) + *acquire = pattern->acquire; + if (release) + *release = pattern->release; +} + +/** + * cairo_raster_source_pattern_set_snapshot: + * @pattern: the pattern to update + * @snapshot: snapshot callback + * + * Sets the callback that will be used whenever a snapshot is taken of the + * pattern, that is whenever the current contents of the pattern should be + * preserved for later use. This is typically invoked whilst printing. + * + * Since: 1.12 + **/ +void +cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *abstract_pattern, + cairo_raster_source_snapshot_func_t snapshot) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + pattern->snapshot = snapshot; +} + +/** + * cairo_raster_source_pattern_get_snapshot: + * @pattern: the pattern to query + * + * Queries the current snapshot callback. + * + * Return value: the current snapshot callback + * + * Since: 1.12 + **/ +cairo_raster_source_snapshot_func_t +cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *abstract_pattern) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return NULL; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + return pattern->snapshot; +} + +/** + * cairo_raster_source_pattern_set_copy: + * @pattern: the pattern to update + * @copy: the copy callback + * + * Updates the copy callback which is used whenever a temporary copy of the + * pattern is taken. + * + * Since: 1.12 + **/ +void +cairo_raster_source_pattern_set_copy (cairo_pattern_t *abstract_pattern, + cairo_raster_source_copy_func_t copy) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + pattern->copy = copy; +} + +/** + * cairo_raster_source_pattern_get_copy: + * @pattern: the pattern to query + * + * Queries the current copy callback. + * + * Return value: the current copy callback + * + * Since: 1.12 + **/ +cairo_raster_source_copy_func_t +cairo_raster_source_pattern_get_copy (cairo_pattern_t *abstract_pattern) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return NULL; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + return pattern->copy; +} + +/** + * cairo_raster_source_pattern_set_finish: + * @pattern: the pattern to update + * @finish: the finish callback + * + * Updates the finish callback which is used whenever a pattern (or a copy + * thereof) will no longer be used. + * + * Since: 1.12 + **/ +void +cairo_raster_source_pattern_set_finish (cairo_pattern_t *abstract_pattern, + cairo_raster_source_finish_func_t finish) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + pattern->finish = finish; +} + +/** + * cairo_raster_source_pattern_get_finish: + * @pattern: the pattern to query + * + * Queries the current finish callback. + * + * Return value: the current finish callback + * + * Since: 1.12 + **/ +cairo_raster_source_finish_func_t +cairo_raster_source_pattern_get_finish (cairo_pattern_t *abstract_pattern) +{ + cairo_raster_source_pattern_t *pattern; + + if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return NULL; + + pattern = (cairo_raster_source_pattern_t *) abstract_pattern; + return pattern->finish; +} diff --git a/gfx/cairo/cairo/src/cairo-recording-surface-inline.h b/gfx/cairo/cairo/src/cairo-recording-surface-inline.h new file mode 100644 index 000000000000..9002ccd69381 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-recording-surface-inline.h @@ -0,0 +1,68 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Kristian Høgsberg + * Adrian Johnson + */ + +#ifndef CAIRO_RECORDING_SURFACE_INLINE_H +#define CAIRO_RECORDING_SURFACE_INLINE_H + +#include "cairo-recording-surface-private.h" + +static inline cairo_bool_t +_cairo_recording_surface_get_bounds (cairo_surface_t *surface, + cairo_rectangle_t *extents) +{ + cairo_recording_surface_t *recording = (cairo_recording_surface_t *)surface; + if (recording->unbounded) + return FALSE; + + *extents = recording->extents_pixels; + return TRUE; +} + +/** + * _cairo_surface_is_recording: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_recording_surface_t + * + * Return value: %TRUE if the surface is a recording surface + **/ +static inline cairo_bool_t +_cairo_surface_is_recording (const cairo_surface_t *surface) +{ + return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING; +} + +#endif /* CAIRO_RECORDING_SURFACE_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-recording-surface-private.h b/gfx/cairo/cairo/src/cairo-recording-surface-private.h index 4ec5f88b47b5..c1827f5b3d6e 100644 --- a/gfx/cairo/cairo/src/cairo-recording-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-recording-surface-private.h @@ -39,7 +39,8 @@ #include "cairoint.h" #include "cairo-path-fixed-private.h" -#include "cairo-clip-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-backend-private.h" typedef enum { /* The 5 basic drawing operations. */ @@ -48,6 +49,9 @@ typedef enum { CAIRO_COMMAND_STROKE, CAIRO_COMMAND_FILL, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, + + /* cairo_tag_begin()/cairo_tag_end() */ + CAIRO_COMMAND_TAG, } cairo_command_type_t; typedef enum { @@ -58,9 +62,13 @@ typedef enum { typedef struct _cairo_command_header { cairo_command_type_t type; - cairo_recording_region_type_t region; + cairo_recording_region_type_t region; cairo_operator_t op; - cairo_clip_t clip; + cairo_rectangle_int_t extents; + cairo_clip_t *clip; + + int index; + struct _cairo_command_header *chain; } cairo_command_header_t; typedef struct _cairo_command_paint { @@ -107,6 +115,17 @@ typedef struct _cairo_command_show_text_glyphs { cairo_scaled_font_t *scaled_font; } cairo_command_show_text_glyphs_t; +typedef struct _cairo_command_tag { + cairo_command_header_t header; + cairo_bool_t begin; + char *tag_name; + char *attributes; + cairo_pattern_union_t source; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; +} cairo_command_tag_t; + typedef union _cairo_command { cairo_command_header_t header; @@ -115,13 +134,12 @@ typedef union _cairo_command { cairo_command_stroke_t stroke; cairo_command_fill_t fill; cairo_command_show_text_glyphs_t show_text_glyphs; + cairo_command_tag_t tag; } cairo_command_t; typedef struct _cairo_recording_surface { cairo_surface_t base; - cairo_content_t content; - /* A recording-surface is logically unbounded, but when used as a * source we need to render it to an image, so we need a size at * which to create that image. */ @@ -129,11 +147,18 @@ typedef struct _cairo_recording_surface { cairo_rectangle_int_t extents; cairo_bool_t unbounded; - cairo_clip_t clip; - cairo_array_t commands; + unsigned int *indices; + unsigned int num_indices; + cairo_bool_t optimize_clears; + cairo_bool_t has_bilevel_alpha; + cairo_bool_t has_only_op_over; - int replay_start_idx; + struct bbtree { + cairo_box_t extents; + struct bbtree *left, *right; + cairo_command_header_t *chain; + } bbtree; } cairo_recording_surface_t; slim_hidden_proto (cairo_recording_surface_create); @@ -142,18 +167,26 @@ cairo_private cairo_int_status_t _cairo_recording_surface_get_path (cairo_surface_t *surface, cairo_path_fixed_t *path); +cairo_private cairo_status_t +_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, + long unsigned index, + cairo_surface_t *target); + cairo_private cairo_status_t _cairo_recording_surface_replay (cairo_surface_t *surface, cairo_surface_t *target); - cairo_private cairo_status_t -_cairo_recording_surface_replay_analyze_recording_pattern (cairo_surface_t *surface, - cairo_surface_t *target); +_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); cairo_private cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target); + const cairo_matrix_t *surface_transform, + 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, @@ -165,7 +198,15 @@ _cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, cairo_box_t *bbox, const cairo_matrix_t *transform); +cairo_private cairo_status_t +_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform); + cairo_private cairo_bool_t -_cairo_surface_is_recording (const cairo_surface_t *surface); +_cairo_recording_surface_has_only_bilevel_alpha (cairo_recording_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface); #endif /* CAIRO_RECORDING_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/cairo-recording-surface.c b/gfx/cairo/cairo/src/cairo-recording-surface.c index 0e955b83f265..71a44798f3ef 100644 --- a/gfx/cairo/cairo/src/cairo-recording-surface.c +++ b/gfx/cairo/cairo/src/cairo-recording-surface.c @@ -54,12 +54,12 @@ * operations applied to the recording surface had instead been applied to the * target surface, you can use code like this: * - * cairo_t *cr; + * cairo_t *cr; * - * cr = cairo_create (target); - * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0); - * cairo_paint (cr); - * cairo_destroy (cr); + * cr = cairo_create (target); + * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0); + * cairo_paint (cr); + * cairo_destroy (cr); * * * A recording surface is logically unbounded, i.e. it has no implicit constraint @@ -74,14 +74,22 @@ * improved by improving the implementation of snapshot for the * various objects. For example, it would be nice to have a * copy-on-write implementation for _cairo_surface_snapshot. - */ + **/ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-clip-private.h" +#include "cairo-combsort-inline.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" -#include "cairo-recording-surface-private.h" +#include "cairo-image-surface-private.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, @@ -98,7 +106,7 @@ static const cairo_surface_backend_t cairo_recording_surface_backend; * This macro was added for completeness in cairo 1.10. * * Since: 1.10 - */ + **/ /* Currently all recording surfaces do have a size which should be passed * in as the maximum size of any target surface against which the @@ -109,6 +117,248 @@ static const cairo_surface_backend_t cairo_recording_surface_backend; * according to the intended replay target). */ +static int bbtree_left_or_right (struct bbtree *bbt, + const cairo_box_t *box) +{ + int left, right; + + if (bbt->left) { + cairo_box_t *e = &bbt->left->extents; + cairo_box_t b; + + b.p1.x = MIN (e->p1.x, box->p1.x); + b.p1.y = MIN (e->p1.y, box->p1.y); + b.p2.x = MAX (e->p2.x, box->p2.x); + b.p2.y = MAX (e->p2.y, box->p2.y); + + left = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y); + left -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y); + } else + left = 0; + + if (bbt->right) { + cairo_box_t *e = &bbt->right->extents; + cairo_box_t b; + + b.p1.x = MIN (e->p1.x, box->p1.x); + b.p1.y = MIN (e->p1.y, box->p1.y); + b.p2.x = MAX (e->p2.x, box->p2.x); + b.p2.y = MAX (e->p2.y, box->p2.y); + + right = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y); + right -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y); + } else + right = 0; + + return left <= right; +} + +#define INVALID_CHAIN ((cairo_command_header_t *)-1) + +static struct bbtree * +bbtree_new (const cairo_box_t *box, cairo_command_header_t *chain) +{ + struct bbtree *bbt = _cairo_malloc (sizeof (*bbt)); + if (bbt == NULL) + return NULL; + bbt->extents = *box; + bbt->left = bbt->right = NULL; + bbt->chain = chain; + return bbt; +} + +static void +bbtree_init (struct bbtree *bbt, cairo_command_header_t *header) +{ + _cairo_box_from_rectangle (&bbt->extents, &header->extents); + bbt->chain = header; +} + +static cairo_status_t +bbtree_add (struct bbtree *bbt, + cairo_command_header_t *header, + const cairo_box_t *box) +{ + if (box->p1.x < bbt->extents.p1.x || box->p1.y < bbt->extents.p1.y || + box->p2.x > bbt->extents.p2.x || box->p2.y > bbt->extents.p2.y) + { + if (bbt->chain) { + if (bbtree_left_or_right (bbt, &bbt->extents)) { + if (bbt->left == NULL) { + bbt->left = bbtree_new (&bbt->extents, bbt->chain); + if (unlikely (bbt->left == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + bbtree_add (bbt->left, bbt->chain, &bbt->extents); + } else { + if (bbt->right == NULL) { + bbt->right = bbtree_new (&bbt->extents, bbt->chain); + if (unlikely (bbt->right == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + bbtree_add (bbt->right, bbt->chain, &bbt->extents); + } + + bbt->chain = NULL; + } + + bbt->extents.p1.x = MIN (bbt->extents.p1.x, box->p1.x); + bbt->extents.p1.y = MIN (bbt->extents.p1.y, box->p1.y); + bbt->extents.p2.x = MAX (bbt->extents.p2.x, box->p2.x); + bbt->extents.p2.y = MAX (bbt->extents.p2.y, box->p2.y); + } + + if (box->p1.x == bbt->extents.p1.x && box->p1.y == bbt->extents.p1.y && + box->p2.x == bbt->extents.p2.x && box->p2.y == bbt->extents.p2.y) + { + cairo_command_header_t *last = header; + while (last->chain) /* expected to be infrequent */ + last = last->chain; + last->chain = bbt->chain; + bbt->chain = header; + return CAIRO_STATUS_SUCCESS; + } + + if (bbtree_left_or_right (bbt, box)) { + if (bbt->left == NULL) { + bbt->left = bbtree_new (box, header); + if (unlikely (bbt->left == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + return bbtree_add (bbt->left, header, box); + } else { + if (bbt->right == NULL) { + bbt->right = bbtree_new (box, header); + if (unlikely (bbt->right == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + return bbtree_add (bbt->right, header, box); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void bbtree_del (struct bbtree *bbt) +{ + if (bbt->left) + bbtree_del (bbt->left); + if (bbt->right) + bbtree_del (bbt->right); + + free (bbt); +} + +static cairo_bool_t box_outside (const cairo_box_t *a, const cairo_box_t *b) +{ + return + a->p1.x >= b->p2.x || a->p1.y >= b->p2.y || + a->p2.x <= b->p1.x || a->p2.y <= b->p1.y; +} + +static void +bbtree_foreach_mark_visible (struct bbtree *bbt, + const cairo_box_t *box, + unsigned int **indices) +{ + cairo_command_header_t *chain; + + for (chain = bbt->chain; chain; chain = chain->chain) + *(*indices)++ = chain->index; + + if (bbt->left && ! box_outside (box, &bbt->left->extents)) + bbtree_foreach_mark_visible (bbt->left, box, indices); + if (bbt->right && ! box_outside (box, &bbt->right->extents)) + bbtree_foreach_mark_visible (bbt->right, box, indices); +} + +static inline int intcmp (const unsigned int a, const unsigned int b) +{ + return a - b; +} +CAIRO_COMBSORT_DECLARE (sort_indices, unsigned int, intcmp) + +static inline int sizecmp (unsigned int a, unsigned int b, cairo_command_header_t **elements) +{ + const cairo_rectangle_int_t *r; + + r = &elements[a]->extents; + a = r->width * r->height; + + r = &elements[b]->extents; + b = r->width * r->height; + + return b - a; +} +CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_commands, unsigned int, sizecmp) + +static void +_cairo_recording_surface_destroy_bbtree (cairo_recording_surface_t *surface) +{ + cairo_command_t **elements; + int i, num_elements; + + if (surface->bbtree.chain == INVALID_CHAIN) + return; + + if (surface->bbtree.left) { + bbtree_del (surface->bbtree.left); + surface->bbtree.left = NULL; + } + if (surface->bbtree.right) { + bbtree_del (surface->bbtree.right); + surface->bbtree.right = NULL; + } + + elements = _cairo_array_index (&surface->commands, 0); + num_elements = surface->commands.num_elements; + for (i = 0; i < num_elements; i++) + elements[i]->header.chain = NULL; + + surface->bbtree.chain = INVALID_CHAIN; +} + +static cairo_status_t +_cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface) +{ + cairo_command_t **elements = _cairo_array_index (&surface->commands, 0); + unsigned int *indices; + cairo_status_t status; + unsigned int i, count; + + count = surface->commands.num_elements; + if (count > surface->num_indices) { + free (surface->indices); + surface->indices = _cairo_malloc_ab (count, sizeof (int)); + if (unlikely (surface->indices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + surface->num_indices = count; + } + + indices = surface->indices; + for (i = 0; i < count; i++) + indices[i] = i; + + sort_commands (indices, count, elements); + + bbtree_init (&surface->bbtree, &elements[indices[0]]->header); + for (i = 1; i < count; i++) { + cairo_command_header_t *header = &elements[indices[i]]->header; + cairo_box_t box; + + _cairo_box_from_rectangle (&box, &header->extents); + status = bbtree_add (&surface->bbtree, header, &box); + if (unlikely (status)) + goto cleanup; + } + + return CAIRO_STATUS_SUCCESS; + +cleanup: + bbtree_del (&surface->bbtree); + return status; +} + /** * cairo_recording_surface_create: * @content: the content of the recording surface @@ -134,49 +384,48 @@ cairo_surface_t * cairo_recording_surface_create (cairo_content_t content, const cairo_rectangle_t *extents) { - cairo_recording_surface_t *recording_surface; - cairo_status_t status; + cairo_recording_surface_t *surface; - recording_surface = malloc (sizeof (cairo_recording_surface_t)); - if (unlikely (recording_surface == NULL)) + surface = _cairo_malloc (sizeof (cairo_recording_surface_t)); + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - _cairo_surface_init (&recording_surface->base, + _cairo_surface_init (&surface->base, &cairo_recording_surface_backend, NULL, /* device */ - content); + content, + TRUE); /* is_vector */ - recording_surface->content = content; - recording_surface->unbounded = TRUE; - _cairo_clip_init (&recording_surface->clip); + surface->unbounded = TRUE; /* unbounded -> 'infinite' extents */ if (extents != NULL) { - recording_surface->extents_pixels = *extents; + surface->extents_pixels = *extents; /* XXX check for overflow */ - recording_surface->extents.x = floor (extents->x); - recording_surface->extents.y = floor (extents->y); - recording_surface->extents.width = ceil (extents->x + extents->width) - recording_surface->extents.x; - recording_surface->extents.height = ceil (extents->y + extents->height) - recording_surface->extents.y; + surface->extents.x = floor (extents->x); + surface->extents.y = floor (extents->y); + surface->extents.width = ceil (extents->x + extents->width) - surface->extents.x; + surface->extents.height = ceil (extents->y + extents->height) - surface->extents.y; - status = _cairo_clip_rectangle (&recording_surface->clip, - &recording_surface->extents); - if (unlikely (status)) { - free (recording_surface); - return _cairo_surface_create_in_error (status); - } - - recording_surface->unbounded = FALSE; + surface->unbounded = FALSE; } - _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); + _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); - recording_surface->replay_start_idx = 0; - recording_surface->base.is_clear = TRUE; + surface->base.is_clear = TRUE; - return &recording_surface->base; + surface->bbtree.left = surface->bbtree.right = NULL; + surface->bbtree.chain = INVALID_CHAIN; + + surface->indices = NULL; + surface->num_indices = 0; + surface->optimize_clears = TRUE; + surface->has_bilevel_alpha = FALSE; + surface->has_only_op_over = FALSE; + + return &surface->base; } slim_hidden_def (cairo_recording_surface_create); @@ -196,12 +445,12 @@ _cairo_recording_surface_create_similar (void *abstract_surface, static cairo_status_t _cairo_recording_surface_finish (void *abstract_surface) { - cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_recording_surface_t *surface = abstract_surface; cairo_command_t **elements; int i, num_elements; - num_elements = recording_surface->commands.num_elements; - elements = _cairo_array_index (&recording_surface->commands, 0); + num_elements = surface->commands.num_elements; + elements = _cairo_array_index (&surface->commands, 0); for (i = 0; i < num_elements; i++) { cairo_command_t *command = elements[i]; @@ -234,55 +483,146 @@ _cairo_recording_surface_finish (void *abstract_surface) cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); break; - default: + case CAIRO_COMMAND_TAG: + free (command->tag.tag_name); + if (command->tag.begin) { + free (command->tag.attributes); + _cairo_pattern_fini (&command->tag.source.base); + _cairo_stroke_style_fini (&command->tag.style); + } + break; + + default: ASSERT_NOT_REACHED; } - _cairo_clip_fini (&command->header.clip); + _cairo_clip_destroy (command->header.clip); free (command); } - _cairo_array_fini (&recording_surface->commands); - _cairo_clip_fini (&recording_surface->clip); + _cairo_array_fini (&surface->commands); + + if (surface->bbtree.left) + bbtree_del (surface->bbtree.left); + if (surface->bbtree.right) + bbtree_del (surface->bbtree.right); + + free (surface->indices); return CAIRO_STATUS_SUCCESS; } +struct proxy { + cairo_surface_t base; + cairo_surface_t *image; +}; + +static cairo_status_t +proxy_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + struct proxy *proxy = abstract_surface; + return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra); +} + +static void +proxy_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + struct proxy *proxy = abstract_surface; + _cairo_surface_release_source_image (proxy->image, image, image_extra); +} + +static cairo_status_t +proxy_finish (void *abstract_surface) +{ + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t proxy_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + proxy_finish, + NULL, + + NULL, /* create similar */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, + proxy_acquire_source_image, + proxy_release_source_image, +}; + +static cairo_surface_t * +attach_proxy (cairo_surface_t *source, + cairo_surface_t *image) +{ + struct proxy *proxy; + + proxy = _cairo_malloc (sizeof (*proxy)); + if (unlikely (proxy == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content, FALSE); + + proxy->image = image; + _cairo_surface_attach_snapshot (source, &proxy->base, NULL); + + return &proxy->base; +} + +static void +detach_proxy (cairo_surface_t *source, + cairo_surface_t *proxy) +{ + cairo_surface_finish (proxy); + cairo_surface_destroy (proxy); +} + +static cairo_surface_t * +get_proxy (cairo_surface_t *proxy) +{ + return ((struct proxy *)proxy)->image; +} + static cairo_status_t _cairo_recording_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { - cairo_status_t status; cairo_recording_surface_t *surface = abstract_surface; - cairo_surface_t *image; + cairo_surface_t *image, *proxy; + cairo_status_t status; - image = _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend); - if (image != NULL) { - *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); + proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend); + if (proxy != NULL) { + *image_out = (cairo_image_surface_t *) + cairo_surface_reference (get_proxy (proxy)); *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } - image = _cairo_image_surface_create_with_content (surface->content, + assert (! surface->unbounded); + image = _cairo_image_surface_create_with_content (surface->base.content, surface->extents.width, surface->extents.height); + cairo_surface_set_device_offset (image, -surface->extents.x, -surface->extents.y); if (unlikely (image->status)) return image->status; - cairo_surface_set_device_offset (image, - -surface->extents.x, - -surface->extents.y); - + /* Handle recursion by returning future reads from the current image */ + proxy = attach_proxy (abstract_surface, image); status = _cairo_recording_surface_replay (&surface->base, image); + detach_proxy (abstract_surface, proxy); + if (unlikely (status)) { cairo_surface_destroy (image); return status; } - cairo_surface_attach_snapshot (&surface->base, image, NULL); - *image_out = (cairo_image_surface_t *) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; @@ -297,40 +637,107 @@ _cairo_recording_surface_release_source_image (void *abstract_surface, } static cairo_status_t -_command_init (cairo_recording_surface_t *recording_surface, +_command_init (cairo_recording_surface_t *surface, cairo_command_header_t *command, cairo_command_type_t type, cairo_operator_t op, - cairo_clip_t *clip) + cairo_composite_rectangles_t *composite) { cairo_status_t status = CAIRO_STATUS_SUCCESS; command->type = type; command->op = op; command->region = CAIRO_RECORDING_REGION_ALL; - _cairo_clip_init_copy (&command->clip, clip); - if (recording_surface->clip.path != NULL) - status = _cairo_clip_apply_clip (&command->clip, &recording_surface->clip); + + command->extents = composite->unbounded; + command->chain = NULL; + command->index = surface->commands.num_elements; + + /* steal the clip */ + command->clip = NULL; + if (! _cairo_composite_rectangles_can_reduce_clip (composite, + composite->clip)) + { + command->clip = composite->clip; + composite->clip = NULL; + } return status; } +static void +_cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface) +{ + cairo_surface_flush (&surface->base); +} + +static cairo_status_t +_cairo_recording_surface_commit (cairo_recording_surface_t *surface, + cairo_command_header_t *command) +{ + _cairo_recording_surface_break_self_copy_loop (surface); + return _cairo_array_append (&surface->commands, &command); +} + +static void +_cairo_recording_surface_reset (cairo_recording_surface_t *surface) +{ + /* Reset the commands and temporaries */ + _cairo_recording_surface_finish (surface); + + surface->bbtree.left = surface->bbtree.right = NULL; + surface->bbtree.chain = INVALID_CHAIN; + + surface->indices = NULL; + surface->num_indices = 0; + + _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); +} + static cairo_int_status_t _cairo_recording_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_recording_surface_t *surface = abstract_surface; cairo_command_paint_t *command; + cairo_composite_rectangles_t composite; - command = malloc (sizeof (cairo_command_paint_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_PAINT, op, clip); + if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { + if (surface->optimize_clears) { + _cairo_recording_surface_reset (surface); + return CAIRO_STATUS_SUCCESS; + } + } + + if (clip == NULL && surface->optimize_clears && + (op == CAIRO_OPERATOR_SOURCE || + (op == CAIRO_OPERATOR_OVER && + (surface->base.is_clear || _cairo_pattern_is_opaque_solid (source))))) + { + _cairo_recording_surface_reset (surface); + } + + status = _cairo_composite_rectangles_init_for_paint (&composite, + &surface->base, + op, source, + clip); + if (unlikely (status)) + return status; + + command = _cairo_malloc (sizeof (cairo_command_paint_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status = _command_init (surface, + &command->header, CAIRO_COMMAND_PAINT, op, + &composite); if (unlikely (status)) goto CLEANUP_COMMAND; @@ -338,23 +745,22 @@ _cairo_recording_surface_paint (void *abstract_surface, if (unlikely (status)) goto CLEANUP_COMMAND; - status = _cairo_array_append (&recording_surface->commands, &command); + status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_SOURCE; - /* An optimisation that takes care to not replay what was done - * before surface is cleared. We don't erase recorded commands - * since we may have earlier snapshots of this surface. */ - if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) - recording_surface->replay_start_idx = recording_surface->commands.num_elements; + _cairo_recording_surface_destroy_bbtree (surface); + _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); + _cairo_clip_destroy (command->header.clip); free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); return status; } @@ -363,18 +769,31 @@ _cairo_recording_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_recording_surface_t *surface = abstract_surface; cairo_command_mask_t *command; + cairo_composite_rectangles_t composite; - command = malloc (sizeof (cairo_command_mask_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_MASK, op, clip); + status = _cairo_composite_rectangles_init_for_mask (&composite, + &surface->base, + op, source, mask, + clip); + if (unlikely (status)) + return status; + + command = _cairo_malloc (sizeof (cairo_command_mask_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status = _command_init (surface, + &command->header, CAIRO_COMMAND_MASK, op, + &composite); if (unlikely (status)) goto CLEANUP_COMMAND; @@ -386,10 +805,13 @@ _cairo_recording_surface_mask (void *abstract_surface, if (unlikely (status)) goto CLEANUP_SOURCE; - status = _cairo_array_append (&recording_surface->commands, &command); + status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_MASK; + _cairo_recording_surface_destroy_bbtree (surface); + + _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_MASK: @@ -397,8 +819,10 @@ _cairo_recording_surface_mask (void *abstract_surface, CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); + _cairo_clip_destroy (command->header.clip); free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); return status; } @@ -406,24 +830,38 @@ static cairo_int_status_t _cairo_recording_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_recording_surface_t *surface = abstract_surface; cairo_command_stroke_t *command; + cairo_composite_rectangles_t composite; - command = malloc (sizeof (cairo_command_stroke_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); - status = _command_init (recording_surface, - &command->header, CAIRO_COMMAND_STROKE, op, clip); + status = _cairo_composite_rectangles_init_for_stroke (&composite, + &surface->base, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) + return status; + + command = _cairo_malloc (sizeof (cairo_command_stroke_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status = _command_init (surface, + &command->header, CAIRO_COMMAND_STROKE, op, + &composite); if (unlikely (status)) goto CLEANUP_COMMAND; @@ -444,10 +882,13 @@ _cairo_recording_surface_stroke (void *abstract_surface, command->tolerance = tolerance; command->antialias = antialias; - status = _cairo_array_append (&recording_surface->commands, &command); + status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_STYLE; + _cairo_recording_surface_destroy_bbtree (surface); + + _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_STYLE: @@ -457,8 +898,10 @@ _cairo_recording_surface_stroke (void *abstract_surface, CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); + _cairo_clip_destroy (command->header.clip); free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); return status; } @@ -466,22 +909,35 @@ static cairo_int_status_t _cairo_recording_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_recording_surface_t *surface = abstract_surface; cairo_command_fill_t *command; + cairo_composite_rectangles_t composite; - command = malloc (sizeof (cairo_command_fill_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); - status =_command_init (recording_surface, - &command->header, CAIRO_COMMAND_FILL, op, clip); + status = _cairo_composite_rectangles_init_for_fill (&composite, + &surface->base, + op, source, path, + clip); + if (unlikely (status)) + return status; + + command = _cairo_malloc (sizeof (cairo_command_fill_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status =_command_init (surface, + &command->header, CAIRO_COMMAND_FILL, op, + &composite); if (unlikely (status)) goto CLEANUP_COMMAND; @@ -497,10 +953,13 @@ _cairo_recording_surface_fill (void *abstract_surface, command->tolerance = tolerance; command->antialias = antialias; - status = _cairo_array_append (&recording_surface->commands, &command); + status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_PATH; + _cairo_recording_surface_destroy_bbtree (surface); + + _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_PATH: @@ -508,8 +967,10 @@ _cairo_recording_surface_fill (void *abstract_surface, CLEANUP_SOURCE: _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); + _cairo_clip_destroy (command->header.clip); free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); return status; } @@ -531,19 +992,34 @@ _cairo_recording_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_recording_surface_t *recording_surface = abstract_surface; + cairo_recording_surface_t *surface = abstract_surface; cairo_command_show_text_glyphs_t *command; + cairo_composite_rectangles_t composite; - command = malloc (sizeof (cairo_command_show_text_glyphs_t)); - if (unlikely (command == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); - status = _command_init (recording_surface, + status = _cairo_composite_rectangles_init_for_glyphs (&composite, + &surface->base, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + NULL); + if (unlikely (status)) + return status; + + command = _cairo_malloc (sizeof (cairo_command_show_text_glyphs_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status = _command_init (surface, &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, - op, clip); + op, &composite); if (unlikely (status)) goto CLEANUP_COMMAND; @@ -559,7 +1035,7 @@ _cairo_recording_surface_show_text_glyphs (void *abstract_surface, command->num_clusters = num_clusters; if (utf8_len) { - command->utf8 = malloc (utf8_len); + command->utf8 = _cairo_malloc (utf8_len); if (unlikely (command->utf8 == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_ARRAYS; @@ -587,10 +1063,11 @@ _cairo_recording_surface_show_text_glyphs (void *abstract_surface, command->scaled_font = cairo_scaled_font_reference (scaled_font); - status = _cairo_array_append (&recording_surface->commands, &command); + status = _cairo_recording_surface_commit (surface, &command->header); if (unlikely (status)) goto CLEANUP_SCALED_FONT; + _cairo_composite_rectangles_fini (&composite); return CAIRO_STATUS_SUCCESS; CLEANUP_SCALED_FONT: @@ -602,13 +1079,488 @@ _cairo_recording_surface_show_text_glyphs (void *abstract_surface, _cairo_pattern_fini (&command->source.base); CLEANUP_COMMAND: - _cairo_clip_fini (&command->header.clip); + _cairo_clip_destroy (command->header.clip); free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); return status; } +static cairo_int_status_t +_cairo_recording_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_tag_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + status = _cairo_composite_rectangles_init_for_paint (&composite, + &surface->base, + CAIRO_OPERATOR_SOURCE, + source ? source : &_cairo_pattern_black.base, + clip); + if (unlikely (status)) + return status; + + command = calloc (1, sizeof (cairo_command_tag_t)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMPOSITE; + } + + status = _command_init (surface, + &command->header, CAIRO_COMMAND_TAG, CAIRO_OPERATOR_SOURCE, + &composite); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + command->begin = begin; + command->tag_name = strdup (tag_name); + if (unlikely (command->tag_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_COMMAND; + } + if (begin) { + if (attributes) { + command->attributes = strdup (attributes); + if (unlikely (command->attributes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_STRINGS; + } + } + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_STRINGS; + + status = _cairo_stroke_style_init_copy (&command->style, style); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + command->ctm = *ctm; + command->ctm_inverse = *ctm_inverse; + } + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) { + if (begin) + goto CLEANUP_STYLE; + else + goto CLEANUP_STRINGS; + } + + _cairo_recording_surface_destroy_bbtree (surface); + + _cairo_composite_rectangles_fini (&composite); + return CAIRO_STATUS_SUCCESS; + + CLEANUP_STYLE: + _cairo_stroke_style_fini (&command->style); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_STRINGS: + free (command->tag_name); + free (command->attributes); + CLEANUP_COMMAND: + _cairo_clip_destroy (command->header.clip); + free (command); + CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); + return status; +} + +static void +_command_init_copy (cairo_recording_surface_t *surface, + cairo_command_header_t *dst, + const cairo_command_header_t *src) +{ + dst->type = src->type; + dst->op = src->op; + dst->region = CAIRO_RECORDING_REGION_ALL; + + dst->extents = src->extents; + dst->chain = NULL; + dst->index = surface->commands.num_elements; + + dst->clip = _cairo_clip_copy (src->clip); +} + +static cairo_status_t +_cairo_recording_surface_copy__paint (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_paint_t *command; + cairo_status_t status; + + command = _cairo_malloc (sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + status = _cairo_pattern_init_copy (&command->source.base, + &src->paint.source.base); + if (unlikely (status)) + goto err_command; + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto err_source; + + return CAIRO_STATUS_SUCCESS; + +err_source: + _cairo_pattern_fini (&command->source.base); +err_command: + free(command); +err: + return status; +} + +static cairo_status_t +_cairo_recording_surface_copy__mask (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_mask_t *command; + cairo_status_t status; + + command = _cairo_malloc (sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + status = _cairo_pattern_init_copy (&command->source.base, + &src->mask.source.base); + if (unlikely (status)) + goto err_command; + + status = _cairo_pattern_init_copy (&command->mask.base, + &src->mask.mask.base); + if (unlikely (status)) + goto err_source; + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto err_mask; + + return CAIRO_STATUS_SUCCESS; + +err_mask: + _cairo_pattern_fini (&command->mask.base); +err_source: + _cairo_pattern_fini (&command->source.base); +err_command: + free(command); +err: + return status; +} + +static cairo_status_t +_cairo_recording_surface_copy__stroke (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_stroke_t *command; + cairo_status_t status; + + command = _cairo_malloc (sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + status = _cairo_pattern_init_copy (&command->source.base, + &src->stroke.source.base); + if (unlikely (status)) + goto err_command; + + status = _cairo_path_fixed_init_copy (&command->path, &src->stroke.path); + if (unlikely (status)) + goto err_source; + + status = _cairo_stroke_style_init_copy (&command->style, + &src->stroke.style); + if (unlikely (status)) + goto err_path; + + command->ctm = src->stroke.ctm; + command->ctm_inverse = src->stroke.ctm_inverse; + command->tolerance = src->stroke.tolerance; + command->antialias = src->stroke.antialias; + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto err_style; + + return CAIRO_STATUS_SUCCESS; + +err_style: + _cairo_stroke_style_fini (&command->style); +err_path: + _cairo_path_fixed_fini (&command->path); +err_source: + _cairo_pattern_fini (&command->source.base); +err_command: + free(command); +err: + return status; +} + +static cairo_status_t +_cairo_recording_surface_copy__fill (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_fill_t *command; + cairo_status_t status; + + command = _cairo_malloc (sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + status = _cairo_pattern_init_copy (&command->source.base, + &src->fill.source.base); + if (unlikely (status)) + goto err_command; + + status = _cairo_path_fixed_init_copy (&command->path, &src->fill.path); + if (unlikely (status)) + goto err_source; + + command->fill_rule = src->fill.fill_rule; + command->tolerance = src->fill.tolerance; + command->antialias = src->fill.antialias; + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto err_path; + + return CAIRO_STATUS_SUCCESS; + +err_path: + _cairo_path_fixed_fini (&command->path); +err_source: + _cairo_pattern_fini (&command->source.base); +err_command: + free(command); +err: + return status; +} + +static cairo_status_t +_cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_show_text_glyphs_t *command; + cairo_status_t status; + + command = _cairo_malloc (sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + status = _cairo_pattern_init_copy (&command->source.base, + &src->show_text_glyphs.source.base); + if (unlikely (status)) + goto err_command; + + command->utf8 = NULL; + command->utf8_len = src->show_text_glyphs.utf8_len; + command->glyphs = NULL; + command->num_glyphs = src->show_text_glyphs.num_glyphs; + command->clusters = NULL; + command->num_clusters = src->show_text_glyphs.num_clusters; + + if (command->utf8_len) { + command->utf8 = _cairo_malloc (command->utf8_len); + if (unlikely (command->utf8 == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err_arrays; + } + memcpy (command->utf8, src->show_text_glyphs.utf8, command->utf8_len); + } + if (command->num_glyphs) { + command->glyphs = _cairo_malloc_ab (command->num_glyphs, + sizeof (command->glyphs[0])); + if (unlikely (command->glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err_arrays; + } + memcpy (command->glyphs, src->show_text_glyphs.glyphs, + sizeof (command->glyphs[0]) * command->num_glyphs); + } + if (command->num_clusters) { + command->clusters = _cairo_malloc_ab (command->num_clusters, + sizeof (command->clusters[0])); + if (unlikely (command->clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err_arrays; + } + memcpy (command->clusters, src->show_text_glyphs.clusters, + sizeof (command->clusters[0]) * command->num_clusters); + } + + command->cluster_flags = src->show_text_glyphs.cluster_flags; + + command->scaled_font = + cairo_scaled_font_reference (src->show_text_glyphs.scaled_font); + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto err_arrays; + + return CAIRO_STATUS_SUCCESS; + +err_arrays: + free (command->utf8); + free (command->glyphs); + free (command->clusters); + _cairo_pattern_fini (&command->source.base); +err_command: + free(command); +err: + return status; +} + +static cairo_status_t +_cairo_recording_surface_copy__tag (cairo_recording_surface_t *surface, + const cairo_command_t *src) +{ + cairo_command_tag_t *command; + cairo_status_t status; + + command = calloc (1, sizeof (*command)); + if (unlikely (command == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err; + } + + _command_init_copy (surface, &command->header, &src->header); + + command->begin = src->tag.begin; + command->tag_name = strdup (src->tag.tag_name); + if (unlikely (command->tag_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err_command; + } + if (src->tag.begin) { + if (src->tag.attributes) { + command->attributes = strdup (src->tag.attributes); + if (unlikely (command->attributes == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto err_command; + } + } + + status = _cairo_pattern_init_copy (&command->source.base, + &src->tag.source.base); + if (unlikely (status)) + goto err_command; + + status = _cairo_stroke_style_init_copy (&command->style, + &src->tag.style); + if (unlikely (status)) + goto err_source; + + command->ctm = src->tag.ctm; + command->ctm_inverse = src->tag.ctm_inverse; + } + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) { + if (src->tag.begin) + goto err_style; + else + goto err_command; + } + + return CAIRO_STATUS_SUCCESS; + +err_style: + _cairo_stroke_style_fini (&command->style); +err_source: + _cairo_pattern_fini (&command->source.base); +err_command: + free(command->tag_name); + free(command->attributes); + free(command); +err: + return status; +} + +static cairo_status_t +_cairo_recording_surface_copy (cairo_recording_surface_t *dst, + cairo_recording_surface_t *src) +{ + cairo_command_t **elements; + int i, num_elements; + cairo_status_t status; + + elements = _cairo_array_index (&src->commands, 0); + num_elements = src->commands.num_elements; + for (i = 0; i < num_elements; i++) { + const cairo_command_t *command = elements[i]; + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + status = _cairo_recording_surface_copy__paint (dst, command); + break; + + case CAIRO_COMMAND_MASK: + status = _cairo_recording_surface_copy__mask (dst, command); + break; + + case CAIRO_COMMAND_STROKE: + status = _cairo_recording_surface_copy__stroke (dst, command); + break; + + case CAIRO_COMMAND_FILL: + status = _cairo_recording_surface_copy__fill (dst, command); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + status = _cairo_recording_surface_copy__glyphs (dst, command); + break; + + case CAIRO_COMMAND_TAG: + status = _cairo_recording_surface_copy__tag (dst, command); + break; + + default: + ASSERT_NOT_REACHED; + } + + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + /** - * _cairo_recording_surface_snapshot + * _cairo_recording_surface_snapshot: * @surface: a #cairo_surface_t which must be a recording surface * * Make an immutable copy of @surface. It is an error to call a @@ -624,39 +1576,42 @@ static cairo_surface_t * _cairo_recording_surface_snapshot (void *abstract_other) { cairo_recording_surface_t *other = abstract_other; - cairo_recording_surface_t *recording_surface; + cairo_recording_surface_t *surface; cairo_status_t status; - recording_surface = malloc (sizeof (cairo_recording_surface_t)); - if (unlikely (recording_surface == NULL)) + surface = _cairo_malloc (sizeof (cairo_recording_surface_t)); + if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - _cairo_surface_init (&recording_surface->base, + _cairo_surface_init (&surface->base, &cairo_recording_surface_backend, NULL, /* device */ - other->base.content); + other->base.content, + other->base.is_vector); - recording_surface->extents_pixels = other->extents_pixels; - recording_surface->extents = other->extents; - recording_surface->unbounded = other->unbounded; - recording_surface->content = other->content; + surface->extents_pixels = other->extents_pixels; + surface->extents = other->extents; + surface->unbounded = other->unbounded; - _cairo_clip_init_copy (&recording_surface->clip, &other->clip); + surface->base.is_clear = other->base.is_clear; - /* XXX We should in theory be able to reuse the original array, but we - * need to handle reference cycles during subsurface and self-copy. - */ - recording_surface->replay_start_idx = 0; - recording_surface->base.is_clear = TRUE; + surface->bbtree.left = surface->bbtree.right = NULL; + surface->bbtree.chain = INVALID_CHAIN; - _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *)); - status = _cairo_recording_surface_replay (&other->base, &recording_surface->base); + surface->indices = NULL; + surface->num_indices = 0; + surface->optimize_clears = TRUE; + surface->has_bilevel_alpha = other->has_bilevel_alpha; + surface->has_only_op_over = other->has_only_op_over; + + _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); + status = _cairo_recording_surface_copy (surface, other); if (unlikely (status)) { - cairo_surface_destroy (&recording_surface->base); + cairo_surface_destroy (&surface->base); return _cairo_surface_create_in_error (status); } - return &recording_surface->base; + return &surface->base; } static cairo_bool_t @@ -672,43 +1627,30 @@ _cairo_recording_surface_get_extents (void *abstract_surface, return TRUE; } -/** - * _cairo_surface_is_recording: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a #cairo_recording_surface_t - * - * Return value: %TRUE if the surface is a recording surface - **/ -cairo_bool_t -_cairo_surface_is_recording (const cairo_surface_t *surface) -{ - return surface->backend == &cairo_recording_surface_backend; -} - static const cairo_surface_backend_t cairo_recording_surface_backend = { CAIRO_SURFACE_TYPE_RECORDING, - _cairo_recording_surface_create_similar, _cairo_recording_surface_finish, + + _cairo_default_context_create, + + _cairo_recording_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, _cairo_recording_surface_acquire_source_image, _cairo_recording_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + _cairo_recording_surface_snapshot, + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_recording_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* Here are the 5 basic drawing operations, (which are in some * sense the only things that cairo_recording_surface should need to @@ -719,37 +1661,32 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { _cairo_recording_surface_mask, _cairo_recording_surface_stroke, _cairo_recording_surface_fill, + NULL, /* fill-stroke */ NULL, - - _cairo_recording_surface_snapshot, - - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _cairo_recording_surface_has_show_text_glyphs, - _cairo_recording_surface_show_text_glyphs + _cairo_recording_surface_show_text_glyphs, + NULL, /* get_supported_mime_types */ + _cairo_recording_surface_tag, }; cairo_int_status_t -_cairo_recording_surface_get_path (cairo_surface_t *surface, +_cairo_recording_surface_get_path (cairo_surface_t *abstract_surface, cairo_path_fixed_t *path) { - cairo_recording_surface_t *recording_surface; + cairo_recording_surface_t *surface; cairo_command_t **elements; int i, num_elements; cairo_int_status_t status; - if (surface->status) - return surface->status; + if (unlikely (abstract_surface->status)) + return abstract_surface->status; - recording_surface = (cairo_recording_surface_t *) surface; + surface = (cairo_recording_surface_t *) abstract_surface; status = CAIRO_STATUS_SUCCESS; - num_elements = recording_surface->commands.num_elements; - elements = _cairo_array_index (&recording_surface->commands, 0); - for (i = recording_surface->replay_start_idx; i < num_elements; i++) { + num_elements = surface->commands.num_elements; + elements = _cairo_array_index (&surface->commands, 0); + for (i = 0; i < num_elements; i++) { cairo_command_t *command = elements[i]; switch (command->header.type) { @@ -765,14 +1702,14 @@ _cairo_recording_surface_get_path (cairo_surface_t *surface, _cairo_traps_init (&traps); /* XXX call cairo_stroke_to_path() when that is implemented */ - status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path, - &command->stroke.style, - &command->stroke.ctm, - &command->stroke.ctm_inverse, - command->stroke.tolerance, - &traps); + status = _cairo_path_fixed_stroke_polygon_to_traps (&command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + &traps); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = _cairo_traps_path (&traps, path); _cairo_traps_fini (&traps); @@ -781,7 +1718,7 @@ _cairo_recording_surface_get_path (cairo_surface_t *surface, case CAIRO_COMMAND_FILL: { status = _cairo_path_fixed_append (path, - &command->fill.path, CAIRO_DIRECTION_FORWARD, + &command->fill.path, 0, 0); break; } @@ -794,6 +1731,9 @@ _cairo_recording_surface_get_path (cairo_surface_t *surface, break; } + case CAIRO_COMMAND_TAG: + break; + default: ASSERT_NOT_REACHED; } @@ -802,60 +1742,180 @@ _cairo_recording_surface_get_path (cairo_surface_t *surface, break; } - return _cairo_surface_set_error (surface, status); + return status; +} + +static int +_cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + unsigned int num_visible, *indices; + cairo_box_t box; + + if (surface->commands.num_elements == 0) + return 0; + + _cairo_box_from_rectangle (&box, extents); + + if (surface->bbtree.chain == INVALID_CHAIN) + _cairo_recording_surface_create_bbtree (surface); + + indices = surface->indices; + bbtree_foreach_mark_visible (&surface->bbtree, &box, &indices); + num_visible = indices - surface->indices; + if (num_visible > 1) + sort_indices (surface->indices, num_visible); + + return num_visible; +} + +static void +_cairo_recording_surface_merge_source_attributes (cairo_recording_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) +{ + if (op != CAIRO_OPERATOR_OVER) + surface->has_only_op_over = FALSE; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) source; + cairo_surface_t *surf = surf_pat->surface; + cairo_surface_t *free_me = NULL; + + if (_cairo_surface_is_snapshot (surf)) + free_me = surf = _cairo_surface_snapshot_get_target (surf); + + if (unlikely (surf->status)) + // There was some kind of error and the surface could be a nil error + // surface with various "problems" (e.g. ->backend == NULL). + return; + + if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) { + cairo_recording_surface_t *rec_surf = (cairo_recording_surface_t *) surf; + + if (! _cairo_recording_surface_has_only_bilevel_alpha (rec_surf)) + surface->has_bilevel_alpha = FALSE; + + if (! _cairo_recording_surface_has_only_op_over (rec_surf)) + surface->has_only_op_over = FALSE; + + } else if (surf->type == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *img_surf = (cairo_image_surface_t *) surf; + + if (_cairo_image_analyze_transparency (img_surf) == CAIRO_IMAGE_HAS_ALPHA) + surface->has_bilevel_alpha = FALSE; + + } else { + if (!_cairo_pattern_is_clear (source) && !_cairo_pattern_is_opaque (source, NULL)) + surface->has_bilevel_alpha = FALSE; + } + + cairo_surface_destroy (free_me); + return; + + } else if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { + cairo_surface_t *image; + cairo_surface_t *raster; + + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + raster = _cairo_raster_source_pattern_acquire (source, image, NULL); + cairo_surface_destroy (image); + if (raster) { + if (raster->type == CAIRO_SURFACE_TYPE_IMAGE) { + if (_cairo_image_analyze_transparency ((cairo_image_surface_t *)raster) == CAIRO_IMAGE_HAS_ALPHA) + surface->has_bilevel_alpha = FALSE; + } + + _cairo_raster_source_pattern_release (source, raster); + if (raster->type == CAIRO_SURFACE_TYPE_IMAGE) + return; + } + } + + if (!_cairo_pattern_is_clear (source) && !_cairo_pattern_is_opaque (source, NULL)) + surface->has_bilevel_alpha = FALSE; } -#define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL) static cairo_status_t -_cairo_recording_surface_replay_internal (cairo_surface_t *surface, +_cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface, const cairo_rectangle_int_t *surface_extents, + const cairo_matrix_t *surface_transform, cairo_surface_t *target, + const cairo_clip_t *target_clip, + cairo_bool_t surface_is_unbounded, cairo_recording_replay_type_t type, cairo_recording_region_type_t region) { - cairo_recording_surface_t *recording_surface; - cairo_command_t **elements; - int i, num_elements; - cairo_int_status_t status; cairo_surface_wrapper_t wrapper; + cairo_command_t **elements; + cairo_bool_t replay_all = + type == CAIRO_RECORDING_CREATE_REGIONS || region == CAIRO_RECORDING_REGION_ALL; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_rectangle_int_t extents; + cairo_bool_t use_indices = FALSE; + const cairo_rectangle_int_t *r; + unsigned int i, num_elements; - if (unlikely (surface->status)) - return surface->status; + if (unlikely (surface->base.status)) + return surface->base.status; if (unlikely (target->status)) return target->status; - if (unlikely (surface->finished)) + if (unlikely (surface->base.finished)) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - if (surface->is_clear) + if (surface->base.is_clear) return CAIRO_STATUS_SUCCESS; - assert (_cairo_surface_is_recording (surface)); + assert (_cairo_surface_is_recording (&surface->base)); _cairo_surface_wrapper_init (&wrapper, target); - _cairo_surface_wrapper_set_extents (&wrapper, surface_extents); + if (surface_extents) + _cairo_surface_wrapper_intersect_extents (&wrapper, surface_extents); + r = &_cairo_unbounded_rectangle; + if (! surface->unbounded && !surface_is_unbounded) { + _cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents); + r = &surface->extents; + } + _cairo_surface_wrapper_set_inverse_transform (&wrapper, surface_transform); + _cairo_surface_wrapper_set_clip (&wrapper, target_clip); - recording_surface = (cairo_recording_surface_t *) surface; - status = CAIRO_STATUS_SUCCESS; + /* Compute the extents of the target clip in recorded device space */ + if (! _cairo_surface_wrapper_get_target_extents (&wrapper, surface_is_unbounded, &extents)) + goto done; - num_elements = recording_surface->commands.num_elements; - elements = _cairo_array_index (&recording_surface->commands, 0); + surface->has_bilevel_alpha = TRUE; + surface->has_only_op_over = TRUE; - for (i = recording_surface->replay_start_idx; i < num_elements; i++) { - cairo_command_t *command = elements[i]; + num_elements = surface->commands.num_elements; + elements = _cairo_array_index (&surface->commands, 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; + } - if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) { - if (command->header.region != region) - continue; - } + for (i = 0; i < num_elements; i++) { + cairo_command_t *command = elements[use_indices ? surface->indices[i] : i]; + + if (! replay_all && command->header.region != region) + continue; + + if (! _cairo_rectangle_intersects (&extents, &command->header.extents)) + continue; switch (command->header.type) { case CAIRO_COMMAND_PAINT: status = _cairo_surface_wrapper_paint (&wrapper, command->header.op, &command->paint.source.base, - _clip (command)); + command->header.clip); + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->paint.source.base); + } break; case CAIRO_COMMAND_MASK: @@ -863,11 +1923,18 @@ _cairo_recording_surface_replay_internal (cairo_surface_t *surface, command->header.op, &command->mask.source.base, &command->mask.mask.base, - _clip (command)); + command->header.clip); + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->mask.source.base); + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->mask.mask.base); + } break; case CAIRO_COMMAND_STROKE: - { status = _cairo_surface_wrapper_stroke (&wrapper, command->header.op, &command->stroke.source.base, @@ -877,49 +1944,65 @@ _cairo_recording_surface_replay_internal (cairo_surface_t *surface, &command->stroke.ctm_inverse, command->stroke.tolerance, command->stroke.antialias, - _clip (command)); + command->header.clip); + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->stroke.source.base); + } break; - } + case CAIRO_COMMAND_FILL: - { - cairo_command_t *stroke_command; + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) { + cairo_command_t *stroke_command; - stroke_command = NULL; - if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) - stroke_command = elements[i + 1]; + stroke_command = NULL; + if (type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) + stroke_command = elements[i + 1]; - if (stroke_command != NULL && - type == CAIRO_RECORDING_REPLAY && - region != CAIRO_RECORDING_REGION_ALL) - { - if (stroke_command->header.region != region) - stroke_command = NULL; + if (stroke_command != NULL && + type == CAIRO_RECORDING_REPLAY && + region != CAIRO_RECORDING_REGION_ALL) + { + if (stroke_command->header.region != region) + stroke_command = NULL; + } + + if (stroke_command != NULL && + stroke_command->header.type == CAIRO_COMMAND_STROKE && + _cairo_path_fixed_equal (&command->fill.path, + &stroke_command->stroke.path) && + _cairo_clip_equal (command->header.clip, + stroke_command->header.clip)) + { + status = _cairo_surface_wrapper_fill_stroke (&wrapper, + command->header.op, + &command->fill.source.base, + command->fill.fill_rule, + command->fill.tolerance, + command->fill.antialias, + &command->fill.path, + stroke_command->header.op, + &stroke_command->stroke.source.base, + &stroke_command->stroke.style, + &stroke_command->stroke.ctm, + &stroke_command->stroke.ctm_inverse, + stroke_command->stroke.tolerance, + stroke_command->stroke.antialias, + command->header.clip); + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->fill.source.base); + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->stroke.source.base); + } + i++; + } } - - if (stroke_command != NULL && - stroke_command->header.type == CAIRO_COMMAND_STROKE && - _cairo_path_fixed_is_equal (&command->fill.path, - &stroke_command->stroke.path)) - { - status = _cairo_surface_wrapper_fill_stroke (&wrapper, - command->header.op, - &command->fill.source.base, - command->fill.fill_rule, - command->fill.tolerance, - command->fill.antialias, - &command->fill.path, - stroke_command->header.op, - &stroke_command->stroke.source.base, - &stroke_command->stroke.style, - &stroke_command->stroke.ctm, - &stroke_command->stroke.ctm_inverse, - stroke_command->stroke.tolerance, - stroke_command->stroke.antialias, - _clip (command)); - i++; - } - else - { + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_surface_wrapper_fill (&wrapper, command->header.op, &command->fill.source.base, @@ -927,52 +2010,56 @@ _cairo_recording_surface_replay_internal (cairo_surface_t *surface, command->fill.fill_rule, command->fill.tolerance, command->fill.antialias, - _clip (command)); + command->header.clip); + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->fill.source.base); + } } break; - } + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: - { - cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs; - cairo_glyph_t *glyphs_copy; - int num_glyphs = command->show_text_glyphs.num_glyphs; - - /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed - * to modify the glyph array that's passed in. We must always - * copy the array before handing it to the backend. - */ - glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unlikely (glyphs_copy == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - break; - } - - memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, command->header.op, &command->show_text_glyphs.source.base, command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, - glyphs_copy, num_glyphs, + command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs, command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, command->show_text_glyphs.cluster_flags, command->show_text_glyphs.scaled_font, - _clip (command)); - free (glyphs_copy); + command->header.clip); + if (type == CAIRO_RECORDING_CREATE_REGIONS) { + _cairo_recording_surface_merge_source_attributes (surface, + command->header.op, + &command->show_text_glyphs.source.base); + } break; - } + + case CAIRO_COMMAND_TAG: + status = _cairo_surface_wrapper_tag (&wrapper, + command->tag.begin, + command->tag.tag_name, + command->tag.attributes, + &command->tag.source.base, + &command->tag.style, + &command->tag.ctm, + &command->tag.ctm_inverse, + command->header.clip); + break; + default: ASSERT_NOT_REACHED; } - if (type == CAIRO_RECORDING_CREATE_REGIONS) { - if (status == CAIRO_STATUS_SUCCESS) { + if (type == CAIRO_RECORDING_CREATE_REGIONS && command->header.region != CAIRO_RECORDING_REGION_NATIVE) { + if (status == CAIRO_INT_STATUS_SUCCESS) { command->header.region = CAIRO_RECORDING_REGION_NATIVE; } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK; - status = CAIRO_STATUS_SUCCESS; + status = CAIRO_INT_STATUS_SUCCESS; } else { - assert (_cairo_status_is_error (status)); + assert (_cairo_int_status_is_error (status)); } } @@ -980,18 +2067,113 @@ _cairo_recording_surface_replay_internal (cairo_surface_t *surface, break; } - /* free up any caches */ - for (i = recording_surface->replay_start_idx; i < num_elements; i++) { - cairo_command_t *command = elements[i]; +done: + _cairo_surface_wrapper_fini (&wrapper); + return _cairo_surface_set_error (&surface->base, status); +} - _cairo_clip_drop_cache (&command->header.clip); +cairo_status_t +_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface, + long unsigned index, + cairo_surface_t *target) +{ + cairo_surface_wrapper_t wrapper; + cairo_command_t **elements, *command; + cairo_int_status_t status; + + if (unlikely (surface->base.status)) + return surface->base.status; + + if (unlikely (target->status)) + return target->status; + + if (unlikely (surface->base.finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + assert (_cairo_surface_is_recording (&surface->base)); + + /* XXX + * Use a surface wrapper because we may want to do transformed + * replay in the future. + */ + _cairo_surface_wrapper_init (&wrapper, target); + + if (index > surface->commands.num_elements) + return _cairo_error (CAIRO_STATUS_READ_ERROR); + + elements = _cairo_array_index (&surface->commands, 0); + command = elements[index]; + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + status = _cairo_surface_wrapper_paint (&wrapper, + command->header.op, + &command->paint.source.base, + command->header.clip); + break; + + case CAIRO_COMMAND_MASK: + status = _cairo_surface_wrapper_mask (&wrapper, + command->header.op, + &command->mask.source.base, + &command->mask.mask.base, + command->header.clip); + break; + + case CAIRO_COMMAND_STROKE: + status = _cairo_surface_wrapper_stroke (&wrapper, + command->header.op, + &command->stroke.source.base, + &command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + command->stroke.antialias, + command->header.clip); + break; + + case CAIRO_COMMAND_FILL: + status = _cairo_surface_wrapper_fill (&wrapper, + command->header.op, + &command->fill.source.base, + &command->fill.path, + command->fill.fill_rule, + command->fill.tolerance, + command->fill.antialias, + command->header.clip); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, + command->header.op, + &command->show_text_glyphs.source.base, + 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, + command->show_text_glyphs.cluster_flags, + command->show_text_glyphs.scaled_font, + command->header.clip); + break; + + case CAIRO_COMMAND_TAG: + status = _cairo_surface_wrapper_tag (&wrapper, + command->tag.begin, + command->tag.tag_name, + command->tag.attributes, + &command->tag.source.base, + &command->tag.style, + &command->tag.ctm, + &command->tag.ctm_inverse, + command->header.clip); + break; + + default: + ASSERT_NOT_REACHED; } _cairo_surface_wrapper_fini (&wrapper); - - return _cairo_surface_set_error (surface, status); + return _cairo_surface_set_error (&surface->base, status); } - /** * _cairo_recording_surface_replay: * @surface: the #cairo_recording_surface_t @@ -1008,8 +2190,20 @@ cairo_status_t _cairo_recording_surface_replay (cairo_surface_t *surface, cairo_surface_t *target) { - return _cairo_recording_surface_replay_internal (surface, NULL, - target, + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL, + target, NULL, FALSE, + CAIRO_RECORDING_REPLAY, + CAIRO_RECORDING_REGION_ALL); +} + +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) +{ + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform, + target, target_clip, FALSE, CAIRO_RECORDING_REPLAY, CAIRO_RECORDING_REGION_ALL); } @@ -1022,10 +2216,13 @@ _cairo_recording_surface_replay (cairo_surface_t *surface, */ cairo_status_t _cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, - cairo_surface_t *target) + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded) { - return _cairo_recording_surface_replay_internal (surface, NULL, - target, + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform, + target, NULL, + surface_is_unbounded, CAIRO_RECORDING_CREATE_REGIONS, CAIRO_RECORDING_REGION_ALL); } @@ -1036,8 +2233,9 @@ _cairo_recording_surface_replay_region (cairo_surface_t *surface, cairo_surface_t *target, cairo_recording_region_type_t region) { - return _cairo_recording_surface_replay_internal (surface, surface_extents, - target, + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, + surface_extents, NULL, + target, NULL, FALSE, CAIRO_RECORDING_REPLAY, region); } @@ -1051,7 +2249,7 @@ _recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, cairo_surface_t *analysis_surface; cairo_status_t status; - null_surface = cairo_null_surface_create (surface->content); + null_surface = _cairo_null_surface_create (surface->base.content); analysis_surface = _cairo_analysis_surface_create (null_surface); cairo_surface_destroy (null_surface); @@ -1095,7 +2293,7 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface, memset (&bbox, 0, sizeof (bbox)); - if (! _cairo_surface_is_recording (surface)) { + if (surface->status || ! _cairo_surface_is_recording (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); goto DONE; } @@ -1132,3 +2330,54 @@ _cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, return _recording_surface_get_ink_bbox (surface, bbox, transform); } + +cairo_status_t +_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + return _recording_surface_get_ink_bbox (surface, bbox, transform); +} + +/** + * cairo_recording_surface_get_extents: + * @surface: a #cairo_recording_surface_t + * @extents: the #cairo_rectangle_t to be assigned the extents + * + * Get the extents of the recording-surface. + * + * Return value: %TRUE if the surface is bounded, of recording type, and + * not in an error state, otherwise %FALSE + * + * Since: 1.12 + **/ +cairo_bool_t +cairo_recording_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_t *extents) +{ + cairo_recording_surface_t *record; + + if (surface->status || ! _cairo_surface_is_recording (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return FALSE; + } + + record = (cairo_recording_surface_t *)surface; + if (record->unbounded) + return FALSE; + + *extents = record->extents_pixels; + return TRUE; +} + +cairo_bool_t +_cairo_recording_surface_has_only_bilevel_alpha (cairo_recording_surface_t *surface) +{ + return surface->has_bilevel_alpha; +} + +cairo_bool_t +_cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface) +{ + return surface->has_only_op_over; +} diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c index 608da53eaf79..2d51d7b10a4b 100644 --- a/gfx/cairo/cairo/src/cairo-rectangle.c +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -39,6 +39,15 @@ #include "cairoint.h" +#include "cairo-box-inline.h" + +const cairo_rectangle_int_t _cairo_empty_rectangle = { 0, 0, 0, 0 }; +const cairo_rectangle_int_t _cairo_unbounded_rectangle = { + CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN, + CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN, + CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN, +}; + cairo_private void _cairo_box_from_doubles (cairo_box_t *box, double *x1, double *y1, @@ -76,42 +85,10 @@ _cairo_boxes_get_extents (const cairo_box_t *boxes, int num_boxes, cairo_box_t *extents) { - int n; - assert (num_boxes > 0); *extents = *boxes; - - for (n = 1; n < num_boxes; n++) { - if (boxes[n].p1.x < extents->p1.x) - extents->p1.x = boxes[n].p1.x; - if (boxes[n].p2.x > extents->p2.x) - extents->p2.x = boxes[n].p2.x; - - if (boxes[n].p1.y < extents->p1.y) - extents->p1.y = boxes[n].p1.y; - if (boxes[n].p2.y > extents->p2.y) - extents->p2.y = boxes[n].p2.y; - } -} - -/* This function will return 'true' if the containing_rectangle contains the - * contained_rectangle, and false otherwise. - */ -cairo_bool_t -_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, - const cairo_rectangle_int_t *contained_rectangle) -{ - if (containing_rectangle->x > contained_rectangle->x || - containing_rectangle->y > contained_rectangle->y) - return FALSE; - - if (containing_rectangle->x + containing_rectangle->width < - contained_rectangle->x + contained_rectangle->width || - containing_rectangle->y + containing_rectangle->height < - contained_rectangle->y + contained_rectangle->height) - return FALSE; - - return TRUE; + while (--num_boxes) + _cairo_box_add_box (extents, ++boxes); } /* XXX We currently have a confusing mix of boxes and rectangles as @@ -169,6 +146,29 @@ _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, } } +/* Extends the dst rectangle to also contain src. + * If one of the rectangles is empty, the result is undefined + */ +void +_cairo_rectangle_union (cairo_rectangle_int_t *dst, + const cairo_rectangle_int_t *src) +{ + int x1, y1, x2, y2; + + x1 = MIN (dst->x, src->x); + y1 = MIN (dst->y, src->y); + /* Beware the unsigned promotion, fortunately we have bits to spare + * as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX + */ + x2 = MAX (dst->x + (int) dst->width, src->x + (int) src->width); + y2 = MAX (dst->y + (int) dst->height, src->y + (int) src->height); + + dst->x = x1; + dst->y = y1; + dst->width = x2 - x1; + dst->height = y2 - y1; +} + #define P1x (line->p1.x) #define P1y (line->p1.y) #define P2x (line->p2.x) @@ -189,7 +189,7 @@ _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, */ cairo_bool_t -_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) +_cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line) { cairo_fixed_t t1=0, t2=0, t3=0, t4=0; cairo_int64_t t1y, t2y, t3x, t4x; @@ -213,7 +213,8 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) xlen = - xlen; } - if (t1 > xlen || t2 < 0) + if ((t1 < 0 || t1 > xlen) && + (t2 < 0 || t2 > xlen)) return FALSE; } else { /* Fully vertical line -- check that X is in bounds */ @@ -231,7 +232,8 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) ylen = - ylen; } - if (t3 > ylen || t4 < 0) + if ((t3 < 0 || t3 > ylen) && + (t4 < 0 || t4 > ylen)) return FALSE; } else { /* Fully horizontal line -- check Y */ @@ -256,11 +258,42 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) return FALSE; } -cairo_bool_t -_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point) +static cairo_status_t +_cairo_box_add_spline_point (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent) { - if (point->x < box->p1.x || point->x > box->p2.x || - point->y < box->p1.y || point->y > box->p2.y) - return FALSE; - return TRUE; + _cairo_box_add_point (closure, point); + + return CAIRO_STATUS_SUCCESS; +} + +/* assumes a has been previously added */ +void +_cairo_box_add_curve_to (cairo_box_t *extents, + const cairo_point_t *a, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + _cairo_box_add_point (extents, d); + if (!_cairo_box_contains_point (extents, b) || + !_cairo_box_contains_point (extents, c)) + { + cairo_status_t status; + + status = _cairo_spline_bound (_cairo_box_add_spline_point, + extents, a, b, c, d); + assert (status == CAIRO_STATUS_SUCCESS); + } +} + +void +_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti, + const cairo_rectangle_t *rectf) +{ + recti->x = floor (rectf->x); + recti->y = floor (rectf->y); + recti->width = ceil (rectf->x + rectf->width) - floor (rectf->x); + recti->height = ceil (rectf->y + rectf->height) - floor (rectf->y); } diff --git a/gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c b/gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c index dab2c151f83a..e353b34e8537 100644 --- a/gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c +++ b/gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c @@ -33,7 +33,7 @@ #include "cairoint.h" -#include "cairo-combsort-private.h" +#include "cairo-combsort-inline.h" #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-list-private.h" @@ -379,18 +379,20 @@ _active_edges_to_spans (sweep_line_t *sweep) for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) { if (cell->x != prev_x && coverage != prev_coverage) { int n = sweep->num_spans++; + int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); sweep->spans[n].x = prev_x; - sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); - sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8; + sweep->spans[n].inverse = 0; + sweep->spans[n].coverage = c - (c >> 8); prev_coverage = coverage; } coverage += cell->covered; if (coverage != prev_coverage) { int n = sweep->num_spans++; + int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); sweep->spans[n].x = cell->x; - sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); - sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8; + sweep->spans[n].inverse = 0; + sweep->spans[n].coverage = c - (c >> 8); prev_coverage = coverage; } coverage += cell->uncovered; @@ -401,13 +403,16 @@ _active_edges_to_spans (sweep_line_t *sweep) if (sweep->num_spans) { if (prev_x <= sweep->xmax) { int n = sweep->num_spans++; + int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8); sweep->spans[n].x = prev_x; - sweep->spans[n].coverage = coverage; + sweep->spans[n].inverse = 0; + sweep->spans[n].coverage = c - (c >> 8); } if (coverage && prev_x < sweep->xmax) { int n = sweep->num_spans++; sweep->spans[n].x = sweep->xmax; + sweep->spans[n].inverse = 1; sweep->spans[n].coverage = 0; } } @@ -489,13 +494,13 @@ generate (cairo_rectangular_scan_converter_t *self, cairo_status_t status; sweep_line_init (&sweep_line); - sweep_line.xmin = self->xmin; - sweep_line.xmax = self->xmax; + sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x); + sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x); sweep_line.start = rectangles; if ((status = setjmp (sweep_line.jmpbuf))) - goto BAIL; + goto out; - sweep_line.current_y = self->ymin; + sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y); start = *sweep_line.start++; do { if (start->top_y != sweep_line.current_y) { @@ -554,9 +559,7 @@ generate (cairo_rectangular_scan_converter_t *self, goto out; } - sweep_line.current_y++; - - do { + while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) { if (stop->bottom_y != sweep_line.current_y) { render_rows (&sweep_line, renderer, stop->bottom_y - sweep_line.current_y); @@ -572,20 +575,81 @@ generate (cairo_rectangular_scan_converter_t *self, goto out; } while (stop->bottom_y == sweep_line.current_y); - sweep_line.current_y++; - } while (TRUE); + } out: - status = renderer->render_rows (renderer, - sweep_line.current_y, - self->ymax - sweep_line.current_y, - NULL, 0); - - BAIL: sweep_line_fini (&sweep_line); return status; } +static void generate_row(cairo_span_renderer_t *renderer, + const rectangle_t *r, + int y, int h, + uint16_t coverage) +{ + cairo_half_open_span_t spans[4]; + unsigned int num_spans = 0; + int x1 = _cairo_fixed_integer_part (r->left); + int x2 = _cairo_fixed_integer_part (r->right); + if (x2 > x1) { + if (! _cairo_fixed_is_integer (r->left)) { + spans[num_spans].x = x1; + spans[num_spans].coverage = + coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8; + num_spans++; + x1++; + } + + if (x2 > x1) { + spans[num_spans].x = x1; + spans[num_spans].coverage = coverage - (coverage >> 8); + num_spans++; + } + + if (! _cairo_fixed_is_integer (r->right)) { + spans[num_spans].x = x2++; + spans[num_spans].coverage = + coverage * _cairo_fixed_fractional_part (r->right) >> 8; + num_spans++; + } + } else { + spans[num_spans].x = x2++; + spans[num_spans].coverage = coverage * (r->right - r->left) >> 8; + num_spans++; + } + + spans[num_spans].x = x2; + spans[num_spans].coverage = 0; + num_spans++; + + renderer->render_rows (renderer, y, h, spans, num_spans); +} + +static cairo_status_t +generate_box (cairo_rectangular_scan_converter_t *self, + cairo_span_renderer_t *renderer) +{ + const rectangle_t *r = self->chunks.base; + int y1 = _cairo_fixed_integer_part (r->top); + int y2 = _cairo_fixed_integer_part (r->bottom); + if (y2 > y1) { + if (! _cairo_fixed_is_integer (r->top)) { + generate_row(renderer, r, y1, 1, + 256 - _cairo_fixed_fractional_part (r->top)); + y1++; + } + + if (y2 > y1) + generate_row(renderer, r, y1, y2-y1, 256); + + if (! _cairo_fixed_is_integer (r->bottom)) + generate_row(renderer, r, y2, 1, + _cairo_fixed_fractional_part (r->bottom)); + } else + generate_row(renderer, r, y1, 1, r->bottom - r->top); + + return CAIRO_STATUS_SUCCESS; +} static cairo_status_t _cairo_rectangular_scan_converter_generate (void *converter, @@ -600,10 +664,14 @@ _cairo_rectangular_scan_converter_generate (void *converter, if (unlikely (self->num_rectangles == 0)) { return renderer->render_rows (renderer, - self->ymin, self->ymax - self->ymin, + _cairo_fixed_integer_part (self->extents.p1.y), + _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y), NULL, 0); } + if (self->num_rectangles == 1) + return generate_box (self, renderer); + rectangles = rectangles_stack; if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) { rectangles = _cairo_malloc_ab (self->num_rectangles + 1, @@ -672,17 +740,22 @@ _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *s if (unlikely (rectangle == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - rectangle->left = box->p1.x; - rectangle->right = box->p2.x; rectangle->dir = dir; + rectangle->left = MAX (box->p1.x, self->extents.p1.x); + rectangle->right = MIN (box->p2.x, self->extents.p2.x); + if (unlikely (rectangle->right <= rectangle->left)) { + self->tail->count--; + return CAIRO_STATUS_SUCCESS; + } - rectangle->top = box->p1.y; - rectangle->top_y = _cairo_fixed_integer_floor (box->p1.y); - rectangle->bottom = box->p2.y; - rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y); - assert (rectangle->bottom_y >= rectangle->top_y); - - self->num_rectangles++; + rectangle->top = MAX (box->p1.y, self->extents.p1.y); + rectangle->top_y = _cairo_fixed_integer_floor (rectangle->top); + rectangle->bottom = MIN (box->p2.y, self->extents.p2.y); + rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom); + if (likely (rectangle->bottom > rectangle->top)) + self->num_rectangles++; + else + self->tail->count--; return CAIRO_STATUS_SUCCESS; } @@ -704,14 +777,9 @@ _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self const cairo_rectangle_int_t *extents) { self->base.destroy = _cairo_rectangular_scan_converter_destroy; - self->base.add_edge = NULL; - self->base.add_polygon = NULL; self->base.generate = _cairo_rectangular_scan_converter_generate; - self->xmin = extents->x; - self->xmax = extents->x + extents->width; - self->ymin = extents->y; - self->ymax = extents->y + extents->height; + _cairo_box_from_rectangle (&self->extents, extents); self->chunks.base = self->buf; self->chunks.next = NULL; diff --git a/gfx/cairo/cairo/src/cairo-reference-count-private.h b/gfx/cairo/cairo/src/cairo-reference-count-private.h index 0d91488ee85b..75fdf3538f57 100644 --- a/gfx/cairo/cairo/src/cairo-reference-count-private.h +++ b/gfx/cairo/cairo/src/cairo-reference-count-private.h @@ -45,13 +45,14 @@ typedef struct { } cairo_reference_count_t; #define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count) +#define _cairo_reference_count_dec(RC) _cairo_atomic_int_dec (&(RC)->ref_count) #define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count) #define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE)) #define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count) -#define CAIRO_REFERENCE_COUNT_INVALID_VALUE (-1) +#define CAIRO_REFERENCE_COUNT_INVALID_VALUE ((cairo_atomic_int_t) -1) #define CAIRO_REFERENCE_COUNT_INVALID {CAIRO_REFERENCE_COUNT_INVALID_VALUE} #define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE) diff --git a/gfx/cairo/cairo/src/cairo-region-private.h b/gfx/cairo/cairo/src/cairo-region-private.h index 11070ba768e4..549e50878982 100644 --- a/gfx/cairo/cairo/src/cairo-region-private.h +++ b/gfx/cairo/cairo/src/cairo-region-private.h @@ -66,6 +66,12 @@ _cairo_region_init_rectangle (cairo_region_t *region, cairo_private void _cairo_region_fini (cairo_region_t *region); +cairo_private cairo_region_t * +_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count); + +cairo_private cairo_box_t * +_cairo_region_get_boxes (const cairo_region_t *region, int *nbox); + CAIRO_END_DECLS #endif /* CAIRO_REGION_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-region.c b/gfx/cairo/cairo/src/cairo-region.c index 112b1d82423a..c1d35e174aea 100644 --- a/gfx/cairo/cairo/src/cairo-region.c +++ b/gfx/cairo/cairo/src/cairo-region.c @@ -52,7 +52,7 @@ * Regions are a simple graphical data type representing an area of * integer-aligned rectangles. They are often used on raster surfaces * to track areas of interest, such as change or clip areas. - */ + **/ static const cairo_region_t _cairo_region_nil = { CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ @@ -104,6 +104,13 @@ _cairo_region_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_SLANT: case CAIRO_STATUS_INVALID_WEIGHT: case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: + case CAIRO_STATUS_DEVICE_FINISHED: + case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_region_t *) &_cairo_region_nil; @@ -132,10 +139,10 @@ _cairo_region_create_in_error (cairo_status_t status) **/ static cairo_status_t _cairo_region_set_error (cairo_region_t *region, - cairo_status_t status) + cairo_status_t status) { - if (! _cairo_status_is_error (status)) - return status; + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ @@ -172,7 +179,7 @@ _cairo_region_fini (cairo_region_t *region) { assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); pixman_region32_fini (®ion->rgn); - VG (VALGRIND_MAKE_MEM_NOACCESS (region, sizeof (cairo_region_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); } /** @@ -234,6 +241,17 @@ cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, if (unlikely (region == NULL)) return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + region->status = CAIRO_STATUS_SUCCESS; + + if (count == 1) { + pixman_region32_init_rect (®ion->rgn, + rects->x, rects->y, + rects->width, rects->height); + + return region; + } + if (count > ARRAY_LENGTH (stack_pboxes)) { pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); if (unlikely (pboxes == NULL)) { @@ -259,12 +277,42 @@ cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } - CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); - region->status = CAIRO_STATUS_SUCCESS; return region; } slim_hidden_def (cairo_region_create_rectangles); +cairo_region_t * +_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count) +{ + cairo_region_t *region; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (unlikely (region == NULL)) + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + region->status = CAIRO_STATUS_SUCCESS; + + if (! pixman_region32_init_rects (®ion->rgn, + (pixman_box32_t *)boxes, count)) { + free (region); + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + return region; +} + +cairo_box_t * +_cairo_region_get_boxes (const cairo_region_t *region, int *nbox) +{ + if (region->status) { + nbox = 0; + return NULL; + } + + return (cairo_box_t *) pixman_region32_rectangles (CONST_CAST ®ion->rgn, nbox); +} + /** * cairo_region_create_rectangle: * @rectangle: a #cairo_rectangle_int_t @@ -473,7 +521,7 @@ slim_hidden_def (cairo_region_get_extents); * cairo_region_status: * @region: a #cairo_region_t * - * Checks whether an error has previous occured for this + * Checks whether an error has previous occurred for this * region object. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY @@ -797,16 +845,6 @@ cairo_region_translate (cairo_region_t *region, } slim_hidden_def (cairo_region_translate); -/** - * cairo_region_overlap_t: - * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region - * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region - * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and - * partially outside the region. - * - * Used as the return value for cairo_region_contains_rectangle(). - */ - /** * cairo_region_contains_rectangle: * @region: a #cairo_region_t diff --git a/gfx/cairo/cairo/src/cairo-rtree-private.h b/gfx/cairo/cairo/src/cairo-rtree-private.h index 191c858713ec..27806cab6033 100644 --- a/gfx/cairo/cairo/src/cairo-rtree-private.h +++ b/gfx/cairo/cairo/src/cairo-rtree-private.h @@ -38,10 +38,11 @@ #define CAIRO_RTREE_PRIVATE_H #include "cairo-compiler-private.h" +#include "cairo-error-private.h" #include "cairo-types-private.h" #include "cairo-freelist-private.h" -#include "cairo-list-private.h" +#include "cairo-list-inline.h" enum { CAIRO_RTREE_NODE_AVAILABLE, @@ -51,7 +52,6 @@ enum { typedef struct _cairo_rtree_node { struct _cairo_rtree_node *children[4], *parent; - void **owner; cairo_list_t link; uint16_t pinned; uint16_t state; @@ -65,6 +65,7 @@ typedef struct _cairo_rtree { cairo_list_t pinned; cairo_list_t available; cairo_list_t evictable; + void (*destroy) (cairo_rtree_node_t *); cairo_freepool_t node_freepool; } cairo_rtree_t; @@ -97,7 +98,8 @@ _cairo_rtree_init (cairo_rtree_t *rtree, int width, int height, int min_size, - int node_size); + int node_size, + void (*destroy)(cairo_rtree_node_t *)); cairo_private cairo_int_status_t _cairo_rtree_insert (cairo_rtree_t *rtree, @@ -111,9 +113,15 @@ _cairo_rtree_evict_random (cairo_rtree_t *rtree, int height, cairo_rtree_node_t **out); +cairo_private void +_cairo_rtree_foreach (cairo_rtree_t *rtree, + void (*func)(cairo_rtree_node_t *, void *data), + void *data); + static inline void * _cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node) { + assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); if (! node->pinned) { cairo_list_move (&node->link, &rtree->pinned); node->pinned = 1; diff --git a/gfx/cairo/cairo/src/cairo-rtree.c b/gfx/cairo/cairo/src/cairo-rtree.c index d6e57916ae6f..dbc040929af8 100644 --- a/gfx/cairo/cairo/src/cairo-rtree.c +++ b/gfx/cairo/cairo/src/cairo-rtree.c @@ -57,7 +57,6 @@ _cairo_rtree_node_create (cairo_rtree_t *rtree, node->children[0] = NULL; node->parent = parent; - node->owner = NULL; node->state = CAIRO_RTREE_NODE_AVAILABLE; node->pinned = FALSE; node->x = x; @@ -78,8 +77,7 @@ _cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node) cairo_list_del (&node->link); if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { - if (node->owner != NULL) - *node->owner = NULL; + rtree->destroy (node); } else { for (i = 0; i < 4 && node->children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, node->children[i]); @@ -186,6 +184,8 @@ _cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node) assert (node->state == CAIRO_RTREE_NODE_OCCUPIED); assert (node->pinned == FALSE); + rtree->destroy (node); + node->state = CAIRO_RTREE_NODE_AVAILABLE; cairo_list_move (&node->link, &rtree->available); @@ -225,15 +225,22 @@ _cairo_rtree_evict_random (cairo_rtree_t *rtree, int height, cairo_rtree_node_t **out) { + cairo_int_status_t ret = CAIRO_INT_STATUS_UNSUPPORTED; cairo_rtree_node_t *node, *next; + cairo_list_t tmp_pinned; int i, cnt; + cairo_list_init (&tmp_pinned); + /* propagate pinned from children to root */ - cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t, - &rtree->pinned, link) - { - if (node->parent != NULL) - _cairo_rtree_pin (rtree, node->parent); + cairo_list_foreach_entry_safe (node, next, + cairo_rtree_node_t, &rtree->pinned, link) { + node = node->parent; + while (node && ! node->pinned) { + node->pinned = 1; + cairo_list_move (&node->link, &tmp_pinned); + node = node->parent; + } } cnt = 0; @@ -245,7 +252,7 @@ _cairo_rtree_evict_random (cairo_rtree_t *rtree, } if (cnt == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; + goto out; cnt = hars_petruska_f54_1_random () % cnt; cairo_list_foreach_entry (node, cairo_rtree_node_t, @@ -253,8 +260,7 @@ _cairo_rtree_evict_random (cairo_rtree_t *rtree, { if (node->width >= width && node->height >= height && cnt-- == 0) { if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { - if (node->owner != NULL) - *node->owner = NULL; + rtree->destroy (node); } else { for (i = 0; i < 4 && node->children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, node->children[i]); @@ -265,60 +271,29 @@ _cairo_rtree_evict_random (cairo_rtree_t *rtree, cairo_list_move (&node->link, &rtree->available); *out = node; - return CAIRO_STATUS_SUCCESS; + ret = CAIRO_STATUS_SUCCESS; + break; } } - return CAIRO_INT_STATUS_UNSUPPORTED; +out: + while (! cairo_list_is_empty (&tmp_pinned)) { + node = cairo_list_first_entry (&tmp_pinned, cairo_rtree_node_t, link); + node->pinned = 0; + cairo_list_move (&node->link, &rtree->evictable); + } + return ret; } void _cairo_rtree_unpin (cairo_rtree_t *rtree) { - cairo_rtree_node_t *node, *next; - cairo_list_t can_collapse; - - if (cairo_list_is_empty (&rtree->pinned)) - return; - - cairo_list_init (&can_collapse); - - cairo_list_foreach_entry_safe (node, next, - cairo_rtree_node_t, - &rtree->pinned, - link) - { - node->pinned = FALSE; - if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) { - cairo_bool_t all_available; - int i; - - node->state = CAIRO_RTREE_NODE_AVAILABLE; - cairo_list_move (&node->link, &rtree->available); - - all_available = TRUE; - node = node->parent; - for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++) - all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE; - - if (all_available) { - cairo_list_move (&node->link, &can_collapse); - for (i = 0; i < 4 && node->children[i] != NULL; i++) - cairo_list_del (&node->children[i]->link); - } - } - else - { - cairo_list_move (&node->link, &rtree->evictable); - } - } - - cairo_list_foreach_entry_safe (node, next, - cairo_rtree_node_t, - &can_collapse, - link) - { - _cairo_rtree_node_collapse (rtree, node); + while (! cairo_list_is_empty (&rtree->pinned)) { + cairo_rtree_node_t *node = cairo_list_first_entry (&rtree->pinned, + cairo_rtree_node_t, + link); + node->pinned = 0; + cairo_list_move (&node->link, &rtree->evictable); } } @@ -327,7 +302,8 @@ _cairo_rtree_init (cairo_rtree_t *rtree, int width, int height, int min_size, - int node_size) + int node_size, + void (*destroy) (cairo_rtree_node_t *)) { assert (node_size >= (int) sizeof (cairo_rtree_node_t)); _cairo_freepool_init (&rtree->node_freepool, node_size); @@ -337,6 +313,7 @@ _cairo_rtree_init (cairo_rtree_t *rtree, cairo_list_init (&rtree->evictable); rtree->min_size = min_size; + rtree->destroy = destroy; memset (&rtree->root, 0, sizeof (rtree->root)); rtree->root.width = width; @@ -351,8 +328,7 @@ _cairo_rtree_reset (cairo_rtree_t *rtree) int i; if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { - if (rtree->root.owner != NULL) - *rtree->root.owner = NULL; + rtree->destroy (&rtree->root); } else { for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); @@ -368,14 +344,41 @@ _cairo_rtree_reset (cairo_rtree_t *rtree) cairo_list_add (&rtree->root.link, &rtree->available); } +static void +_cairo_rtree_node_foreach (cairo_rtree_node_t *node, + void (*func)(cairo_rtree_node_t *, void *data), + void *data) +{ + int i; + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_foreach(node->children[i], func, data); + + func(node, data); +} + +void +_cairo_rtree_foreach (cairo_rtree_t *rtree, + void (*func)(cairo_rtree_node_t *, void *data), + void *data) +{ + int i; + + if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { + func(&rtree->root, data); + } else { + for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) + _cairo_rtree_node_foreach (rtree->root.children[i], func, data); + } +} + void _cairo_rtree_fini (cairo_rtree_t *rtree) { int i; if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { - if (rtree->root.owner != NULL) - *rtree->root.owner = NULL; + rtree->destroy (&rtree->root); } else { for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++) _cairo_rtree_node_destroy (rtree, rtree->root.children[i]); diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-private.h b/gfx/cairo/cairo/src/cairo-scaled-font-private.h index 029377b17880..317d21197953 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font-private.h +++ b/gfx/cairo/cairo/src/cairo-scaled-font-private.h @@ -45,6 +45,8 @@ #include "cairo-mutex-type-private.h" #include "cairo-reference-count-private.h" +CAIRO_BEGIN_DECLS + typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t; struct _cairo_scaled_font { @@ -112,20 +114,73 @@ struct _cairo_scaled_font { cairo_bool_t cache_frozen; cairo_bool_t global_cache_frozen; - /* - * One surface backend may store data in each glyph. - * Whichever surface manages to store its pointer here - * first gets to store data in each glyph - */ - const cairo_surface_backend_t *surface_backend; - void *surface_private; + cairo_list_t dev_privates; /* font backend managing this scaled font */ const cairo_scaled_font_backend_t *backend; cairo_list_t link; }; +struct _cairo_scaled_font_private { + cairo_list_t link; + const void *key; + void (*destroy) (cairo_scaled_font_private_t *, + cairo_scaled_font_t *); +}; + +struct _cairo_scaled_glyph { + cairo_hash_entry_t hash_entry; + + cairo_text_extents_t metrics; /* user-space metrics */ + cairo_text_extents_t fs_metrics; /* font-space metrics */ + cairo_box_t bbox; /* device-space bounds */ + int16_t x_advance; /* device-space rounded X advance */ + int16_t y_advance; /* device-space rounded Y advance */ + + unsigned int has_info; + cairo_image_surface_t *surface; /* device-space image */ + cairo_path_fixed_t *path; /* device-space outline */ + cairo_surface_t *recording_surface; /* device-space recording-surface */ + cairo_image_surface_t *color_surface; /* device-space color image */ + + const void *dev_private_key; + void *dev_private; + cairo_list_t dev_privates; +}; + +struct _cairo_scaled_glyph_private { + cairo_list_t link; + const void *key; + void (*destroy) (cairo_scaled_glyph_private_t *, + cairo_scaled_glyph_t *, + cairo_scaled_font_t *); +}; + +cairo_private cairo_scaled_font_private_t * +_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font, + const void *key); + cairo_private void -_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font); +_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font, + cairo_scaled_font_private_t *priv, + const void *key, + void (*destroy) (cairo_scaled_font_private_t *, + cairo_scaled_font_t *)); + +cairo_private cairo_scaled_glyph_private_t * +_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph, + const void *key); + +cairo_private void +_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_private_t *priv, + const void *key, + void (*destroy) (cairo_scaled_glyph_private_t *, + cairo_scaled_glyph_t *, + cairo_scaled_font_t *)); +cairo_private cairo_bool_t +_cairo_scaled_font_has_color_glyphs (cairo_scaled_font_t *scaled_font); + +CAIRO_END_DECLS #endif /* CAIRO_SCALED_FONT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h b/gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h index b165d9acabe1..e7809f03aa55 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h +++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h @@ -47,6 +47,7 @@ typedef struct _cairo_scaled_font_subsets_glyph { unsigned int subset_glyph_index; cairo_bool_t is_scaled; cairo_bool_t is_composite; + cairo_bool_t is_latin; double x_advance; double y_advance; cairo_bool_t utf8_is_mapped; @@ -124,6 +125,18 @@ _cairo_scaled_font_subsets_create_composite (void); cairo_private void _cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets); +/** + * _cairo_scaled_font_subsets_enable_latin_subset: + * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed + * @use_latin: a #cairo_bool_t indicating if a latin subset is to be used + * + * If enabled, all CP1252 characters will be placed in a separate + * 8-bit latin subset. + **/ +cairo_private void +_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets, + cairo_bool_t use_latin); + /** * _cairo_scaled_font_subsets_map_glyph: * @font_subsets: a #cairo_scaled_font_subsets_t @@ -203,16 +216,16 @@ cairo_private cairo_status_t _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets, cairo_scaled_font_t *scaled_font, unsigned long scaled_font_glyph_index, - const char * utf8, + const char * utf8, int utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph_ret); -typedef cairo_status_t +typedef cairo_int_status_t (*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset, void *closure); /** - * _cairo_scaled_font_subsets_foreach: + * _cairo_scaled_font_subsets_foreach_scaled: * @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 @@ -332,7 +345,7 @@ cairo_private cairo_int_status_t _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset); typedef struct _cairo_cff_subset { - char *font_name; + char *family_name_utf8; char *ps_name; double *widths; double x_min, y_min, x_max, y_max; @@ -373,6 +386,15 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, cairo_private void _cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset); +/** + * _cairo_cff_scaled_font_is_cid_cff: + * @scaled_font: a #cairo_scaled_font_t + * + * Return %TRUE if @scaled_font is a CID CFF font, otherwise return %FALSE. + **/ +cairo_private cairo_bool_t +_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font); + /** * _cairo_cff_fallback_init: * @cff_subset: a #cairo_cff_subset_t to initialize @@ -405,7 +427,7 @@ cairo_private void _cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset); typedef struct _cairo_truetype_subset { - char *font_name; + char *family_name_utf8; char *ps_name; double *widths; double x_min, y_min, x_max, y_max; @@ -417,7 +439,7 @@ typedef struct _cairo_truetype_subset { } cairo_truetype_subset_t; /** - * _cairo_truetype_subset_init: + * _cairo_truetype_subset_init_ps: * @truetype_subset: a #cairo_truetype_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * @@ -425,7 +447,8 @@ typedef struct _cairo_truetype_subset { * #cairo_scaled_font_t and the font backend in use) generate a * truetype file corresponding to @font_subset and initialize * @truetype_subset with information about the subset and the truetype - * data. + * data. The generated font will be suitable for embedding in + * PostScript. * * Return value: %CAIRO_STATUS_SUCCESS if successful, * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a @@ -433,8 +456,29 @@ typedef struct _cairo_truetype_subset { * errors include %CAIRO_STATUS_NO_MEMORY. **/ cairo_private cairo_status_t -_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, - cairo_scaled_font_subset_t *font_subset); +_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_truetype_subset_init_pdf: + * @truetype_subset: a #cairo_truetype_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a + * truetype file corresponding to @font_subset and initialize + * @truetype_subset with information about the subset and the truetype + * data. The generated font will be suitable for embedding in + * PDF. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a + * truetype file, or an non-zero value indicating an error. Possible + * errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset); /** * _cairo_truetype_subset_fini: @@ -447,7 +491,14 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, cairo_private void _cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset); +cairo_private const char * +_cairo_ps_standard_encoding_to_glyphname (int glyph); +cairo_private int +_cairo_unicode_to_winansi (unsigned long unicode); + +cairo_private const char * +_cairo_winansi_to_glyphname (int glyph); typedef struct _cairo_type1_subset { char *base_font; @@ -461,8 +512,6 @@ typedef struct _cairo_type1_subset { } cairo_type1_subset_t; -#if CAIRO_HAS_FT_FONT - /** * _cairo_type1_subset_init: * @type1_subset: a #cairo_type1_subset_t to initialize @@ -496,9 +545,6 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type_subset, cairo_private void _cairo_type1_subset_fini (cairo_type1_subset_t *subset); -#endif /* CAIRO_HAS_FT_FONT */ - - /** * _cairo_type1_scaled_font_is_type1: * @scaled_font: a #cairo_scaled_font_t @@ -530,7 +576,7 @@ _cairo_type1_fallback_init_binary (cairo_type1_subset_t *type_subset, cairo_scaled_font_subset_t *font_subset); /** - * _cairo_type1_fallback_init_hexencode: + * _cairo_type1_fallback_init_hex: * @type1_subset: a #cairo_type1_subset_t to initialize * @font_subset: the #cairo_scaled_font_subset_t to initialize from * @@ -598,23 +644,6 @@ _cairo_type2_charstrings_init (cairo_type2_charstrings_t *charstrings, cairo_private void _cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings); -/** - * _cairo_truetype_create_glyph_to_unicode_map: - * @font_subset: the #cairo_scaled_font_subset_t to initialize from - * - * If possible (depending on the format of the underlying - * #cairo_scaled_font_t and the font backend in use) assign - * the unicode character of each glyph in font_subset to - * fontsubset->to_unicode. - * - * Return value: %CAIRO_STATUS_SUCCESS if successful, - * %CAIRO_INT_STATUS_UNSUPPORTED if the unicode encoding of - * the glyphs is not available. Possible errors include - * %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_private cairo_int_status_t -_cairo_truetype_create_glyph_to_unicode_map (cairo_scaled_font_subset_t *font_subset); - /** * _cairo_truetype_index_to_ucs4: * @scaled_font: the #cairo_scaled_font_t @@ -663,6 +692,49 @@ _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, char **ps_name, char **font_name); +/** + * _cairo_truetype_get_style: + * @scaled_font: the #cairo_scaled_font_t + * @weight: returns the font weight from the OS/2 table + * @bold: returns true if font is bold + * @italic: returns true if font is italic + * + * If the font is a truetype/opentype font with an OS/2 table, get the + * weight, bold, and italic data from the OS/2 table. The weight + * values have the same meaning as the lfWeight field of the Windows + * LOGFONT structure. Refer to the TrueType Specification for + * definition of the weight values. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType + * or the OS/2 table is not present. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font, + int *weight, + cairo_bool_t *bold, + cairo_bool_t *italic); + +/** + * _cairo_escape_ps_name: + * @ps_name: returns the PostScript name with all invalid characters escaped + * + * Ensure that PostSript name is a valid PDF/PostSript name object. + * In PDF names are treated as UTF8 and non ASCII bytes, ' ', + * and '#' are encoded as '#' followed by 2 hex digits that + * encode the byte. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_escape_ps_name (char **ps_name); + +#if DEBUG_SUBSETS +cairo_private void +dump_scaled_font_subsets (cairo_scaled_font_subsets_t *font_subsets); +#endif + #endif /* CAIRO_HAS_FONT_SUBSET */ #endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c index 01bc05bfbcb3..1f0e53577f10 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c +++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c @@ -40,7 +40,7 @@ * Adrian Johnson */ -#define _BSD_SOURCE /* for snprintf(), strdup() */ +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-error-private.h" @@ -70,13 +70,17 @@ 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; cairo_scaled_font_t *scaled_font; unsigned int font_id; int current_subset; int num_glyphs_in_current_subset; + int num_glyphs_in_latin_subset; int max_glyphs_per_subset; + char latin_char_map[256]; cairo_hash_table_t *sub_font_glyphs; struct _cairo_sub_font *next; @@ -84,6 +88,7 @@ typedef struct _cairo_sub_font { struct _cairo_scaled_font_subsets { cairo_subsets_type_t type; + cairo_bool_t use_latin_subset; int max_glyphs_per_unscaled_subset_used; cairo_hash_table_t *unscaled_sub_fonts; @@ -106,6 +111,8 @@ typedef struct _cairo_sub_font_glyph { double x_advance; double y_advance; + cairo_bool_t is_latin; + int latin_character; cairo_bool_t is_mapped; uint32_t unicode; char *utf8; @@ -116,6 +123,8 @@ typedef struct _cairo_sub_font_collection { unsigned long *glyphs; /* scaled_font_glyph_index */ char **utf8; unsigned int glyphs_size; + int *to_latin_char; + unsigned long *latin_to_subset_glyph_index; unsigned int max_glyph; unsigned int num_glyphs; @@ -145,25 +154,20 @@ _cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph, sub_font_glyph->base.hash = scaled_font_glyph_index; } -static cairo_bool_t -_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b) -{ - const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a; - const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b; - - return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash; -} - static cairo_sub_font_glyph_t * _cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, unsigned int subset_id, unsigned int subset_glyph_index, double x_advance, - double y_advance) + double y_advance, + int latin_character, + uint32_t unicode, + char *utf8, + int utf8_len) { cairo_sub_font_glyph_t *sub_font_glyph; - sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t)); + sub_font_glyph = _cairo_malloc (sizeof (cairo_sub_font_glyph_t)); if (unlikely (sub_font_glyph == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; @@ -174,10 +178,12 @@ _cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, sub_font_glyph->subset_glyph_index = subset_glyph_index; sub_font_glyph->x_advance = x_advance; sub_font_glyph->y_advance = y_advance; + sub_font_glyph->is_latin = (latin_character >= 0); + sub_font_glyph->latin_character = latin_character; sub_font_glyph->is_mapped = FALSE; - sub_font_glyph->unicode = -1; - sub_font_glyph->utf8 = NULL; - sub_font_glyph->utf8_len = 0; + sub_font_glyph->unicode = unicode; + sub_font_glyph->utf8 = utf8; + sub_font_glyph->utf8_len = utf8_len; return sub_font_glyph; } @@ -185,8 +191,7 @@ _cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, static void _cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph) { - if (sub_font_glyph->utf8 != NULL) - free (sub_font_glyph->utf8); + free (sub_font_glyph->utf8); free (sub_font_glyph); } @@ -220,6 +225,10 @@ _cairo_sub_font_glyph_collect (void *entry, void *closure) collection->glyphs[subset_glyph_index] = scaled_font_glyph_index; collection->utf8[subset_glyph_index] = sub_font_glyph->utf8; + collection->to_latin_char[subset_glyph_index] = sub_font_glyph->latin_character; + if (sub_font_glyph->is_latin) + collection->latin_to_subset_glyph_index[sub_font_glyph->latin_character] = subset_glyph_index; + if (subset_glyph_index > collection->max_glyph) collection->max_glyph = subset_glyph_index; @@ -266,44 +275,48 @@ _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, cairo_sub_font_t **sub_font_out) { cairo_sub_font_t *sub_font; - cairo_status_t status; - cairo_scaled_font_subsets_glyph_t subset_glyph; + int i; - sub_font = malloc (sizeof (cairo_sub_font_t)); + sub_font = _cairo_malloc (sizeof (cairo_sub_font_t)); if (unlikely (sub_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); 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; _cairo_sub_font_init_key (sub_font, scaled_font); sub_font->parent = parent; sub_font->scaled_font = scaled_font; sub_font->font_id = font_id; - sub_font->current_subset = 0; - sub_font->num_glyphs_in_current_subset = 0; - sub_font->max_glyphs_per_subset = max_glyphs_per_subset; + sub_font->use_latin_subset = parent->use_latin_subset; - sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal); + /* latin subsets of Type 3 and CID CFF fonts are not supported */ + if (sub_font->is_user || sub_font->is_scaled || + _cairo_cff_scaled_font_is_cid_cff (scaled_font) ) + { + sub_font->use_latin_subset = FALSE; + } + + if (sub_font->use_latin_subset) + sub_font->current_subset = 1; /* reserve subset 0 for latin glyphs */ + else + sub_font->current_subset = 0; + + sub_font->num_glyphs_in_current_subset = 0; + sub_font->num_glyphs_in_latin_subset = 0; + sub_font->max_glyphs_per_subset = max_glyphs_per_subset; + for (i = 0; i < 256; i++) + sub_font->latin_char_map[i] = FALSE; + + sub_font->sub_font_glyphs = _cairo_hash_table_create (NULL); if (unlikely (sub_font->sub_font_glyphs == NULL)) { free (sub_font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } sub_font->next = NULL; - - /* Reserve first glyph in subset for the .notdef glyph except for - * Type 3 fonts */ - if (! is_scaled) { - status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph); - if (unlikely (status)) { - _cairo_hash_table_destroy (sub_font->sub_font_glyphs); - free (sub_font); - return status; - } - } - *sub_font_out = sub_font; return CAIRO_STATUS_SUCCESS; } @@ -329,10 +342,39 @@ _cairo_sub_font_pluck (void *entry, void *closure) _cairo_sub_font_destroy (sub_font); } +/* Characters 0x80 to 0x9f in the winansi encoding. + * All other characters in the range 0x00 to 0xff map 1:1 to unicode */ +static unsigned int _winansi_0x80_to_0x9f[] = { + 0x20ac, 0x0000, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017d, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0000, 0x017e, 0x0178 +}; + +int +_cairo_unicode_to_winansi (unsigned long uni) +{ + int i; + + /* exclude the extra "hyphen" at 0xad to avoid duplicate glyphnames */ + if ((uni >= 0x20 && uni <= 0x7e) || + (uni >= 0xa1 && uni <= 0xff && uni != 0xad) || + uni == 0) + return uni; + + for (i = 0; i < 32; i++) + if (_winansi_0x80_to_0x9f[i] == uni) + return i + 0x80; + + return -1; +} + static cairo_status_t -_cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, - cairo_scaled_font_t *scaled_font, - unsigned long scaled_font_glyph_index) +_cairo_sub_font_glyph_lookup_unicode (cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + uint32_t *unicode_out, + char **utf8_out, + int *utf8_len_out) { uint32_t unicode; char buf[8]; @@ -356,19 +398,19 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, return status; } - sub_font_glyph->unicode = unicode; - sub_font_glyph->utf8 = NULL; - sub_font_glyph->utf8_len = 0; + *unicode_out = unicode; + *utf8_out = NULL; + *utf8_len_out = 0; if (unicode != (uint32_t) -1) { len = _cairo_ucs4_to_utf8 (unicode, buf); if (len > 0) { - sub_font_glyph->utf8 = malloc (len + 1); - if (unlikely (sub_font_glyph->utf8 == NULL)) + *utf8_out = _cairo_malloc (len + 1); + if (unlikely (*utf8_out == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - memcpy (sub_font_glyph->utf8, buf, len); - sub_font_glyph->utf8[len] = 0; - sub_font_glyph->utf8_len = len; + memcpy (*utf8_out, buf, len); + (*utf8_out)[len] = 0; + *utf8_len_out = len; } } @@ -399,7 +441,7 @@ _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 = malloc (utf8_len + 1); + sub_font_glyph->utf8 = _cairo_malloc (utf8_len + 1); if (unlikely (sub_font_glyph->utf8 == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -429,9 +471,14 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, if (sub_font_glyph != NULL) { subset_glyph->font_id = sub_font->font_id; subset_glyph->subset_id = sub_font_glyph->subset_id; - subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; + if (sub_font_glyph->is_latin) + subset_glyph->subset_glyph_index = sub_font_glyph->latin_character; + else + subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; + subset_glyph->is_scaled = sub_font->is_scaled; subset_glyph->is_composite = sub_font->is_composite; + subset_glyph->is_latin = sub_font_glyph->is_latin; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, @@ -445,11 +492,90 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_status_t +_cairo_sub_font_add_glyph (cairo_sub_font_t *sub_font, + unsigned long scaled_font_glyph_index, + cairo_bool_t is_latin, + int latin_character, + uint32_t unicode, + char *utf8, + int utf8_len, + cairo_sub_font_glyph_t **sub_font_glyph_out) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_sub_font_glyph_t *sub_font_glyph; + int *num_glyphs_in_subset_ptr; + double x_advance; + double y_advance; + cairo_int_status_t status; + + _cairo_scaled_font_freeze_cache (sub_font->scaled_font); + status = _cairo_scaled_glyph_lookup (sub_font->scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (sub_font->scaled_font); + return status; + } + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + _cairo_scaled_font_thaw_cache (sub_font->scaled_font); + + if (!is_latin && sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) + { + sub_font->current_subset++; + sub_font->num_glyphs_in_current_subset = 0; + } + + if (is_latin) + num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_latin_subset; + else + num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_current_subset; + + if ((*num_glyphs_in_subset_ptr == 0) && sub_font->reserve_notdef) + (*num_glyphs_in_subset_ptr)++; + + sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index, + is_latin ? 0 : sub_font->current_subset, + *num_glyphs_in_subset_ptr, + x_advance, + y_advance, + is_latin ? latin_character : -1, + unicode, + utf8, + utf8_len); + + if (unlikely (sub_font_glyph == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); + if (unlikely (status)) { + _cairo_sub_font_glyph_destroy (sub_font_glyph); + return status; + } + + (*num_glyphs_in_subset_ptr)++; + if (sub_font->is_scaled) { + if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_scaled_subset_used) + sub_font->parent->max_glyphs_per_scaled_subset_used = *num_glyphs_in_subset_ptr; + } else { + if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_unscaled_subset_used) + sub_font->parent->max_glyphs_per_unscaled_subset_used = *num_glyphs_in_subset_ptr; + } + + *sub_font_glyph_out = sub_font_glyph; + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, - const char *utf8, - int utf8_len, + const char *text_utf8, + int text_utf8_len, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_sub_font_glyph_t key, *sub_font_glyph; @@ -459,79 +585,88 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, &key.base); if (sub_font_glyph == NULL) { - cairo_scaled_glyph_t *scaled_glyph; + uint32_t font_unicode; + char *font_utf8; + int font_utf8_len; + cairo_bool_t is_latin; + int latin_character; - if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) - { - cairo_scaled_font_subsets_glyph_t tmp_subset_glyph; + status = _cairo_sub_font_glyph_lookup_unicode (sub_font->scaled_font, + scaled_font_glyph_index, + &font_unicode, + &font_utf8, + &font_utf8_len); + if (unlikely(status)) + return status; - sub_font->current_subset++; - sub_font->num_glyphs_in_current_subset = 0; + /* If the supplied utf8 is a valid single character, use it + * instead of the font lookup */ + if (text_utf8 != NULL && text_utf8_len > 0) { + uint32_t *ucs4; + int ucs4_len; - /* Reserve first glyph in subset for the .notdef glyph - * except for Type 3 fonts */ - if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) { - status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph); - if (unlikely (status)) - return status; + status = _cairo_utf8_to_ucs4 (text_utf8, text_utf8_len, + &ucs4, &ucs4_len); + if (status == CAIRO_STATUS_SUCCESS) { + 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); + } + memcpy (font_utf8, text_utf8, text_utf8_len); + font_utf8[text_utf8_len] = 0; + font_utf8_len = text_utf8_len; + } + free (ucs4); } } - _cairo_scaled_font_freeze_cache (sub_font->scaled_font); - status = _cairo_scaled_glyph_lookup (sub_font->scaled_font, - scaled_font_glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) { - _cairo_scaled_font_thaw_cache (sub_font->scaled_font); - return status; + /* If glyph is in the winansi encoding and font is not a user + * 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))) + { + latin_character = _cairo_unicode_to_winansi (font_unicode); + if (latin_character > 0) + { + if (!sub_font->latin_char_map[latin_character]) { + sub_font->latin_char_map[latin_character] = TRUE; + is_latin = TRUE; + } + } } - sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index, - sub_font->current_subset, - sub_font->num_glyphs_in_current_subset, - scaled_glyph->metrics.x_advance, - scaled_glyph->metrics.y_advance); - _cairo_scaled_font_thaw_cache (sub_font->scaled_font); - - if (unlikely (sub_font_glyph == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph, - sub_font->scaled_font, - scaled_font_glyph_index); - if (unlikely (status)) { - _cairo_sub_font_glyph_destroy (sub_font_glyph); + status = _cairo_sub_font_add_glyph (sub_font, + scaled_font_glyph_index, + is_latin, + latin_character, + font_unicode, + font_utf8, + font_utf8_len, + &sub_font_glyph); + if (unlikely(status)) return status; - } - - status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); - if (unlikely (status)) { - _cairo_sub_font_glyph_destroy (sub_font_glyph); - return status; - } - - sub_font->num_glyphs_in_current_subset++; - - if (sub_font->is_scaled) { - if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used) - sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset; - } else { - if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used) - sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset; - } } subset_glyph->font_id = sub_font->font_id; subset_glyph->subset_id = sub_font_glyph->subset_id; - subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; + if (sub_font_glyph->is_latin) + subset_glyph->subset_glyph_index = sub_font_glyph->latin_character; + else + subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index; + subset_glyph->is_scaled = sub_font->is_scaled; subset_glyph->is_composite = sub_font->is_composite; + subset_glyph->is_latin = sub_font_glyph->is_latin; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, - utf8, utf8_len, + text_utf8, text_utf8_len, &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; @@ -558,14 +693,28 @@ _cairo_sub_font_collect (void *entry, void *closure) collection->subset_id = i; collection->num_glyphs = 0; collection->max_glyph = 0; + memset (collection->latin_to_subset_glyph_index, 0, 256*sizeof(unsigned long)); + + if (sub_font->reserve_notdef) { + // add .notdef + collection->glyphs[0] = 0; + collection->utf8[0] = 0; + collection->to_latin_char[0] = 0; + collection->latin_to_subset_glyph_index[0] = 0; + collection->num_glyphs++; + } _cairo_hash_table_foreach (sub_font->sub_font_glyphs, _cairo_sub_font_glyph_collect, collection); if (collection->status) break; + if (collection->num_glyphs == 0) continue; + if (sub_font->reserve_notdef && collection->num_glyphs == 1) + continue; + /* Ensure the resulting array has no uninitialized holes */ assert (collection->num_glyphs == collection->max_glyph + 1); @@ -578,21 +727,20 @@ _cairo_sub_font_collect (void *entry, void *closure) subset.utf8 = collection->utf8; subset.num_glyphs = collection->num_glyphs; subset.glyph_names = NULL; - /* No need to check for out of memory here. If to_unicode is NULL, the PDF - * surface does not emit an ToUnicode stream */ - subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long)); - if (subset.to_unicode) { - for (j = 0; j < collection->num_glyphs; j++) { - /* default unicode character required when mapping fails */ - subset.to_unicode[j] = 0xfffd; - } - } + + subset.is_latin = FALSE; + if (sub_font->use_latin_subset && i == 0) { + subset.is_latin = TRUE; + subset.to_latin_char = collection->to_latin_char; + subset.latin_to_subset_glyph_index = collection->latin_to_subset_glyph_index; + } else { + subset.to_latin_char = NULL; + subset.latin_to_subset_glyph_index = NULL; + } + collection->status = (collection->font_subset_callback) (&subset, collection->font_subset_callback_closure); - if (subset.to_unicode != NULL) - free (subset.to_unicode); - if (subset.glyph_names != NULL) { for (j = 0; j < collection->num_glyphs; j++) free (subset.glyph_names[j]); @@ -609,13 +757,14 @@ _cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type) { cairo_scaled_font_subsets_t *subsets; - subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); + subsets = _cairo_malloc (sizeof (cairo_scaled_font_subsets_t)); if (unlikely (subsets == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return NULL; } subsets->type = type; + subsets->use_latin_subset = FALSE; subsets->max_glyphs_per_unscaled_subset_used = 0; subsets->max_glyphs_per_scaled_subset_used = 0; subsets->num_sub_fonts = 0; @@ -670,6 +819,13 @@ _cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets) free (subsets); } +void +_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets, + cairo_bool_t use_latin) +{ + font_subsets->use_latin_subset = use_latin; +} + cairo_status_t _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, cairo_scaled_font_t *scaled_font, @@ -684,7 +840,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, cairo_matrix_t identity; cairo_font_options_t font_options; cairo_scaled_font_t *unscaled_font; - cairo_status_t status; + cairo_int_status_t status; int max_glyphs; cairo_bool_t type1_font; @@ -737,10 +893,10 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, &scaled_glyph); _cairo_scaled_font_thaw_cache (scaled_font); } - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; - if (status == CAIRO_STATUS_SUCCESS && + if (status == CAIRO_INT_STATUS_SUCCESS && subsets->type != CAIRO_SUBSETS_SCALED && ! _cairo_font_face_is_user (scaled_font->font_face)) { @@ -763,10 +919,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, return unscaled_font->status; subset_glyph->is_scaled = FALSE; - type1_font = FALSE; -#if CAIRO_HAS_FT_FONT type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font); -#endif if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) { max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT; subset_glyph->is_composite = TRUE; @@ -881,11 +1034,16 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long)); collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *)); - if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) { - if (collection.glyphs != NULL) - free (collection.glyphs); - if (collection.utf8 != NULL) - free (collection.utf8); + collection.to_latin_char = _cairo_malloc_ab (collection.glyphs_size, sizeof(int)); + collection.latin_to_subset_glyph_index = _cairo_malloc_ab (256, sizeof(unsigned long)); + if (unlikely (collection.glyphs == NULL || + collection.utf8 == NULL || + collection.to_latin_char == NULL || + collection.latin_to_subset_glyph_index == NULL)) { + free (collection.glyphs); + free (collection.utf8); + free (collection.to_latin_char); + free (collection.latin_to_subset_glyph_index); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -907,6 +1065,8 @@ _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t } free (collection.utf8); free (collection.glyphs); + free (collection.to_latin_char); + free (collection.latin_to_subset_glyph_index); return collection.status; } @@ -956,6 +1116,87 @@ _cairo_string_equal (const void *key_a, const void *key_b) return FALSE; } +#if DEBUG_SUBSETS + +static void +dump_glyph (void *entry, void *closure) +{ + cairo_sub_font_glyph_t *glyph = entry; + char buf[10]; + int i; + + printf(" font_glyph_index: %ld\n", glyph->base.hash); + printf(" subset_id: %d\n", glyph->subset_id); + printf(" subset_glyph_index: %d\n", glyph->subset_glyph_index); + printf(" x_advance: %f\n", glyph->x_advance); + printf(" y_advance: %f\n", glyph->y_advance); + printf(" is_latin: %d\n", glyph->is_latin); + printf(" latin_character: '%c' (0x%02x)\n", glyph->latin_character, glyph->latin_character); + printf(" is_latin: %d\n", glyph->is_latin); + printf(" is_mapped: %d\n", glyph->is_mapped); + printf(" unicode: U+%04x\n", glyph->unicode); + memset(buf, 0, sizeof(buf)); + memcpy(buf, glyph->utf8, glyph->utf8_len); + printf(" utf8: '%s'\n", buf); + printf(" utf8 (hex):"); + for (i = 0; i < glyph->utf8_len; i++) + printf(" 0x%02x", glyph->utf8[i]); + printf("\n\n"); +} + +static void +dump_subfont (cairo_sub_font_t *sub_font) +{ + while (sub_font) { + printf(" font_id: %d\n", sub_font->font_id); + printf(" current_subset: %d\n", sub_font->current_subset); + printf(" is_scaled: %d\n", sub_font->is_scaled); + printf(" is_composite: %d\n", sub_font->is_composite); + printf(" is_user: %d\n", sub_font->is_user); + printf(" use_latin_subset: %d\n", sub_font->use_latin_subset); + printf(" reserve_notdef: %d\n", sub_font->reserve_notdef); + printf(" num_glyphs_in_current_subset: %d\n", sub_font->num_glyphs_in_current_subset); + printf(" num_glyphs_in_latin_subset: %d\n", sub_font->num_glyphs_in_latin_subset); + printf(" max_glyphs_per_subset: %d\n\n", sub_font->max_glyphs_per_subset); + + _cairo_hash_table_foreach (sub_font->sub_font_glyphs, dump_glyph, NULL); + + printf("\n"); + sub_font = sub_font->next; + } +} + +void +dump_scaled_font_subsets (cairo_scaled_font_subsets_t *font_subsets) +{ + printf("font subsets\n"); + switch (font_subsets->type) + { + case CAIRO_SUBSETS_SCALED: + printf(" type: CAIRO_SUBSETS_SCALED\n"); + break; + case CAIRO_SUBSETS_SIMPLE: + printf(" type: CAIRO_SUBSETS_SIMPLE\n"); + break; + case CAIRO_SUBSETS_COMPOSITE: + printf(" type: CAIRO_SUBSETS_COMPOSITE\n"); + break; + } + printf(" use_latin_subset: %d\n", font_subsets->use_latin_subset); + printf(" max_glyphs_per_unscaled_subset_used: %d\n", font_subsets->max_glyphs_per_unscaled_subset_used); + printf(" max_glyphs_per_scaled_subset_used: %d\n", font_subsets->max_glyphs_per_scaled_subset_used); + printf(" num_sub_fonts: %d\n\n", font_subsets->num_sub_fonts); + + printf(" scaled subsets:\n"); + dump_subfont (font_subsets->scaled_sub_fonts_list); + + printf("\n unscaled subsets:\n"); + dump_subfont (font_subsets->unscaled_sub_fonts_list); +} + +#endif + + static void _cairo_string_init_key (cairo_string_entry_t *key, char *s) { @@ -971,7 +1212,7 @@ _cairo_string_init_key (cairo_string_entry_t *key, char *s) static cairo_status_t create_string_entry (char *s, cairo_string_entry_t **entry) { - *entry = malloc (sizeof (cairo_string_entry_t)); + *entry = _cairo_malloc (sizeof (cairo_string_entry_t)); if (unlikely (*entry == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1035,12 +1276,23 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset utf16_len = 0; if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) + if (status == CAIRO_STATUS_INVALID_STRING) { + utf16 = NULL; + utf16_len = 0; + } else if (unlikely (status)) { goto CLEANUP_HASH; + } } if (utf16_len == 1) { - snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]); + int ch = _cairo_unicode_to_winansi (utf16[0]); + if (ch > 0 && _cairo_winansi_to_glyphname (ch)) { + strncpy (buf, _cairo_winansi_to_glyphname (ch), sizeof (buf)); + buf[sizeof (buf)-1] = '\0'; + } else { + snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]); + } + _cairo_string_init_key (&key, buf); entry = _cairo_hash_table_lookup (names, &key.base); if (entry != NULL) @@ -1048,8 +1300,7 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset } else { snprintf (buf, sizeof (buf), "g%d", i); } - if (utf16) - free (utf16); + free (utf16); subset->glyph_names[i] = strdup (buf); if (unlikely (subset->glyph_names[i] == NULL)) { @@ -1077,8 +1328,7 @@ CLEANUP_HASH: if (subset->glyph_names != NULL) { for (i = 0; i < subset->num_glyphs; i++) { - if (subset->glyph_names[i] != NULL) - free (subset->glyph_names[i]); + free (subset->glyph_names[i]); } free (subset->glyph_names); @@ -1088,4 +1338,44 @@ CLEANUP_HASH: return status; } +cairo_int_status_t +_cairo_escape_ps_name (char **ps_name) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Ensure PS name is a valid PDF/PS name object. In PDF names are + * treated as UTF8 and non ASCII bytes, ' ', and '#' are encoded + * as '#' followed by 2 hex digits that encode the byte. By also + * encoding the characters in the reserved string we ensure the + * name is also PS compatible. */ + if (*ps_name) { + static const char *reserved = "()<>[]{}/%#\\"; + char buf[128]; /* max name length is 127 bytes */ + char *src = *ps_name; + char *dst = buf; + + while (*src && dst < buf + 127) { + unsigned char c = *src; + if (c < 0x21 || c > 0x7e || strchr (reserved, c)) { + if (dst + 4 > buf + 127) + break; + + snprintf (dst, 4, "#%02X", c); + src++; + dst += 3; + } else { + *dst++ = *src++; + } + } + *dst = 0; + free (*ps_name); + *ps_name = strdup (buf); + if (*ps_name == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + return status; +} + #endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/gfx/cairo/cairo/src/cairo-scaled-font.c b/gfx/cairo/cairo/src/cairo-scaled-font.c old mode 100644 new mode 100755 index 7ee51ec5798f..d53915fee037 --- a/gfx/cairo/cairo/src/cairo-scaled-font.c +++ b/gfx/cairo/cairo/src/cairo-scaled-font.c @@ -40,13 +40,11 @@ #include "cairoint.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-pattern-private.h" #include "cairo-scaled-font-private.h" - -#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) -#define ISFINITE(x) isfinite (x) -#else -#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ -#endif +#include "cairo-surface-backend-private.h" /** * SECTION:cairo-scaled-font @@ -56,7 +54,10 @@ * * #cairo_scaled_font_t represents a realization of a font face at a particular * size and transformation and a certain set of font options. - */ + **/ + +static uint32_t +_cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font); /* Global Glyph Cache * @@ -72,13 +73,13 @@ */ /* XXX: This number is arbitrary---we've never done any measurement of this. */ -#define MAX_GLYPH_PAGES_CACHED 256 +#define MAX_GLYPH_PAGES_CACHED 512 static cairo_cache_t cairo_scaled_glyph_page_cache; #define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 struct _cairo_scaled_glyph_page { cairo_cache_entry_t cache_entry; - + cairo_scaled_font_t *scaled_font; cairo_list_t link; unsigned int num_glyphs; @@ -197,10 +198,15 @@ static void _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { - const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend; + while (! cairo_list_is_empty (&scaled_glyph->dev_privates)) { + cairo_scaled_glyph_private_t *private = + cairo_list_first_entry (&scaled_glyph->dev_privates, + cairo_scaled_glyph_private_t, + link); + private->destroy (private, scaled_glyph, scaled_font); + } - if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL) - surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font); + _cairo_image_scaled_glyph_fini (scaled_font, scaled_glyph); if (scaled_glyph->surface != NULL) cairo_surface_destroy (&scaled_glyph->surface->base); @@ -212,6 +218,9 @@ _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, cairo_surface_finish (scaled_glyph->recording_surface); cairo_surface_destroy (scaled_glyph->recording_surface); } + + if (scaled_glyph->color_surface != NULL) + cairo_surface_destroy (&scaled_glyph->color_surface->base); } #define ZOMBIE 0 @@ -241,8 +250,7 @@ static const cairo_scaled_font_t _cairo_scaled_font_nil = { { NULL, NULL }, /* pages */ FALSE, /* cache_frozen */ FALSE, /* global_cache_frozen */ - NULL, /* surface_backend */ - NULL, /* surface_private */ + { NULL, NULL }, /* privates */ NULL /* backend */ }; @@ -309,6 +317,8 @@ cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font) * * Return value: %CAIRO_STATUS_SUCCESS or another error such as * %CAIRO_STATUS_NO_MEMORY. + * + * Since: 1.0 **/ cairo_status_t cairo_scaled_font_status (cairo_scaled_font_t *scaled_font) @@ -358,7 +368,7 @@ _cairo_scaled_font_map_lock (void) CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); if (cairo_scaled_font_map == NULL) { - cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t)); + cairo_scaled_font_map = _cairo_malloc (sizeof (cairo_scaled_font_map_t)); if (unlikely (cairo_scaled_font_map == NULL)) goto CLEANUP_MUTEX_LOCK; @@ -436,14 +446,16 @@ _cairo_scaled_font_map_destroy (void) CLEANUP_MUTEX_LOCK: CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); } + static void -_cairo_scaled_glyph_page_destroy (void *closure) +_cairo_scaled_glyph_page_destroy (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_page_t *page) { - cairo_scaled_glyph_page_t *page = closure; - cairo_scaled_font_t *scaled_font; unsigned int n; - scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + assert (!scaled_font->cache_frozen); + assert (!scaled_font->global_cache_frozen); + for (n = 0; n < page->num_glyphs; n++) { _cairo_hash_table_remove (scaled_font->glyphs, &page->glyphs[n].hash_entry); @@ -451,10 +463,24 @@ _cairo_scaled_glyph_page_destroy (void *closure) } cairo_list_del (&page->link); - free (page); } +static void +_cairo_scaled_glyph_page_pluck (void *closure) +{ + cairo_scaled_glyph_page_t *page = closure; + cairo_scaled_font_t *scaled_font; + + assert (! cairo_list_is_empty (&page->link)); + + scaled_font = page->scaled_font; + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + _cairo_scaled_glyph_page_destroy (scaled_font, page); + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); +} + /* If a scaled font wants to unlock the font map while still being * created (needed for user-fonts), we need to take extra care not * ending up with multiple identical scaled fonts being created. @@ -483,7 +509,7 @@ _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t if (unlikely (status)) return status; - placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t)); + placeholder_scaled_font = _cairo_malloc (sizeof (cairo_scaled_font_t)); if (unlikely (placeholder_scaled_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -499,6 +525,8 @@ _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t placeholder_scaled_font->placeholder = TRUE; + placeholder_scaled_font->hash_entry.hash + = _cairo_scaled_font_compute_hash (placeholder_scaled_font); status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table, &placeholder_scaled_font->hash_entry); if (unlikely (status)) @@ -524,6 +552,9 @@ _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + /* temporary hash value to match the placeholder */ + scaled_font->hash_entry.hash + = _cairo_scaled_font_compute_hash (scaled_font); placeholder_scaled_font = _cairo_hash_table_lookup (cairo_scaled_font_map->hash_table, &scaled_font->hash_entry); @@ -595,6 +626,26 @@ _hash_mix_bits (uint32_t hash) return hash; } +static uint32_t +_cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font) +{ + uint32_t hash = FNV1_32_INIT; + + /* We do a bytewise hash on the font matrices */ + hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash); + hash = _hash_matrix_fnv (&scaled_font->ctm, hash); + hash = _hash_mix_bits (hash); + + hash ^= (unsigned long) scaled_font->original_font_face; + hash ^= cairo_font_options_hash (&scaled_font->options); + + /* final mixing of bits */ + hash = _hash_mix_bits (hash); + assert (hash != ZOMBIE); + + return hash; +} + static void _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, cairo_font_face_t *font_face, @@ -602,11 +653,10 @@ _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, const cairo_matrix_t *ctm, const cairo_font_options_t *options) { - uint32_t hash = FNV1_32_INIT; - scaled_font->status = CAIRO_STATUS_SUCCESS; scaled_font->placeholder = FALSE; scaled_font->font_face = font_face; + scaled_font->original_font_face = font_face; scaled_font->font_matrix = *font_matrix; scaled_font->ctm = *ctm; /* ignore translation values in the ctm */ @@ -614,19 +664,8 @@ _cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font, scaled_font->ctm.y0 = 0.; _cairo_font_options_init_copy (&scaled_font->options, options); - /* We do a bytewise hash on the font matrices */ - hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash); - hash = _hash_matrix_fnv (&scaled_font->ctm, hash); - hash = _hash_mix_bits (hash); - - hash ^= (uintptr_t) scaled_font->font_face; - hash ^= cairo_font_options_hash (&scaled_font->options); - - /* final mixing of bits */ - hash = _hash_mix_bits (hash); - - assert (hash != ZOMBIE); - scaled_font->hash_entry.hash = hash; + scaled_font->hash_entry.hash = + _cairo_scaled_font_compute_hash (scaled_font); } static cairo_bool_t @@ -636,10 +675,7 @@ _cairo_scaled_font_keys_equal (const void *abstract_key_a, const cairo_scaled_font_t *key_a = abstract_key_a; const cairo_scaled_font_t *key_b = abstract_key_b; - if (key_a->hash_entry.hash != key_b->hash_entry.hash) - return FALSE; - - return key_a->font_face == key_b->font_face && + return key_a->original_font_face == key_b->original_font_face && memcmp ((unsigned char *)(&key_a->font_matrix.xx), (unsigned char *)(&key_b->font_matrix.xx), sizeof(cairo_matrix_t)) == 0 && @@ -666,15 +702,6 @@ _cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font, cairo_font_options_equal (&scaled_font->options, options); } -static cairo_bool_t -_cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b) -{ - const cairo_scaled_glyph_t *a = abstract_a; - const cairo_scaled_glyph_t *b = abstract_b; - - return a->hash_entry.hash == b->hash_entry.hash; -} - /* * Basic #cairo_scaled_font_t object management */ @@ -693,8 +720,16 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, if (unlikely (status)) return status; - _cairo_scaled_font_init_key (scaled_font, font_face, - font_matrix, ctm, options); + scaled_font->status = CAIRO_STATUS_SUCCESS; + scaled_font->placeholder = FALSE; + scaled_font->font_face = font_face; + scaled_font->original_font_face = font_face; + scaled_font->font_matrix = *font_matrix; + scaled_font->ctm = *ctm; + /* ignore translation values in the ctm */ + scaled_font->ctm.x0 = 0.; + scaled_font->ctm.y0 = 0.; + _cairo_font_options_init_copy (&scaled_font->options, options); cairo_matrix_multiply (&scaled_font->scale, &scaled_font->font_matrix, @@ -723,7 +758,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, return status; } - scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal); + scaled_font->glyphs = _cairo_hash_table_create (NULL); if (unlikely (scaled_font->glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -743,8 +778,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, CAIRO_MUTEX_INIT (scaled_font->mutex); - scaled_font->surface_backend = NULL; - scaled_font->surface_private = NULL; + cairo_list_init (&scaled_font->dev_privates); scaled_font->backend = backend; cairo_list_init (&scaled_font->link); @@ -765,32 +799,52 @@ _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font) void _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) { - scaled_font->cache_frozen = FALSE; + assert (scaled_font->cache_frozen); if (scaled_font->global_cache_frozen) { CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); _cairo_cache_thaw (&cairo_scaled_glyph_page_cache); CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); - scaled_font->global_cache_frozen = FALSE; } + scaled_font->cache_frozen = FALSE; CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } void _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font) { - assert (! scaled_font->cache_frozen); + cairo_scaled_glyph_page_t *page; + CAIRO_MUTEX_LOCK (scaled_font->mutex); + assert (! scaled_font->cache_frozen); + assert (! scaled_font->global_cache_frozen); CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); - while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { - _cairo_cache_remove (&cairo_scaled_glyph_page_cache, - &cairo_list_first_entry (&scaled_font->glyph_pages, - cairo_scaled_glyph_page_t, - link)->cache_entry); + + cairo_list_foreach_entry (page, + cairo_scaled_glyph_page_t, + &scaled_font->glyph_pages, + link) { + cairo_scaled_glyph_page_cache.size -= page->cache_entry.size; + _cairo_hash_table_remove (cairo_scaled_glyph_page_cache.hash_table, + (cairo_hash_entry_t *) &page->cache_entry); } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + + /* Destroy scaled_font's pages while holding its lock only, and not the + * global page cache lock. The destructor can cause us to recurse and + * end up back here for a different scaled_font. */ + + while (! cairo_list_is_empty (&scaled_font->glyph_pages)) { + page = cairo_list_first_entry (&scaled_font->glyph_pages, + cairo_scaled_glyph_page_t, + link); + _cairo_scaled_glyph_page_destroy (scaled_font, page); + } + + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); } cairo_status_t @@ -825,6 +879,8 @@ _cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, static void _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) { + assert (! scaled_font->cache_frozen); + assert (! scaled_font->global_cache_frozen); scaled_font->finished = TRUE; _cairo_scaled_font_reset_cache (scaled_font); @@ -835,9 +891,13 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) CAIRO_MUTEX_FINI (scaled_font->mutex); - if (scaled_font->surface_backend != NULL && - scaled_font->surface_backend->scaled_font_fini != NULL) - scaled_font->surface_backend->scaled_font_fini (scaled_font); + while (! cairo_list_is_empty (&scaled_font->dev_privates)) { + cairo_scaled_font_private_t *private = + cairo_list_first_entry (&scaled_font->dev_privates, + cairo_scaled_font_private_t, + link); + private->destroy (private, scaled_font); + } if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL) scaled_font->backend->fini (scaled_font); @@ -845,22 +905,6 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) _cairo_user_data_array_fini (&scaled_font->user_data); } -/* XXX: allow multiple backends to share the font */ -void -_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font) -{ - if (scaled_font->surface_backend == NULL) - return; - - _cairo_scaled_font_reset_cache (scaled_font); - - if (scaled_font->surface_backend->scaled_font_fini != NULL) - scaled_font->surface_backend->scaled_font_fini (scaled_font); - - scaled_font->surface_backend = NULL; - scaled_font->surface_private = NULL; -} - void _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) { @@ -871,6 +915,69 @@ _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); } +void +_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font, + cairo_scaled_font_private_t *private, + const void *key, + void (*destroy) (cairo_scaled_font_private_t *, + cairo_scaled_font_t *)) +{ + private->key = key; + private->destroy = destroy; + cairo_list_add (&private->link, &scaled_font->dev_privates); +} + +cairo_scaled_font_private_t * +_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font, + const void *key) +{ + cairo_scaled_font_private_t *priv; + + cairo_list_foreach_entry (priv, cairo_scaled_font_private_t, + &scaled_font->dev_privates, link) + { + if (priv->key == key) { + if (priv->link.prev != &scaled_font->dev_privates) + cairo_list_move (&priv->link, &scaled_font->dev_privates); + return priv; + } + } + + return NULL; +} + +void +_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_private_t *private, + const void *key, + void (*destroy) (cairo_scaled_glyph_private_t *, + cairo_scaled_glyph_t *, + cairo_scaled_font_t *)) +{ + private->key = key; + private->destroy = destroy; + cairo_list_add (&private->link, &scaled_glyph->dev_privates); +} + +cairo_scaled_glyph_private_t * +_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph, + const void *key) +{ + cairo_scaled_glyph_private_t *priv; + + cairo_list_foreach_entry (priv, cairo_scaled_glyph_private_t, + &scaled_glyph->dev_privates, link) + { + if (priv->key == key) { + if (priv->link.prev != &scaled_glyph->dev_privates) + cairo_list_move (&priv->link, &scaled_glyph->dev_privates); + return priv; + } + } + + return NULL; +} + /** * cairo_scaled_font_create: * @font_face: a #cairo_font_face_t @@ -890,6 +997,8 @@ _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) * * Return value: a newly created #cairo_scaled_font_t. Destroy with * cairo_scaled_font_destroy() + * + * Since: 1.0 **/ cairo_scaled_font_t * cairo_scaled_font_create (cairo_font_face_t *font_face, @@ -950,106 +1059,88 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, scaled_font->hash_entry.hash = ZOMBIE; dead = scaled_font; font_map->mru_scaled_font = NULL; - - if (font_face->backend->get_implementation != NULL) { - font_face = font_face->backend->get_implementation (font_face, - font_matrix, - ctm, - options); - if (unlikely (font_face->status)) { - _cairo_scaled_font_map_unlock (); - cairo_scaled_font_destroy (scaled_font); - return _cairo_scaled_font_create_in_error (font_face->status); - } - } - - _cairo_scaled_font_init_key (&key, font_face, - font_matrix, ctm, options); } - else + + _cairo_scaled_font_init_key (&key, font_face, font_matrix, ctm, options); + + while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, + &key.hash_entry))) { - if (font_face->backend->get_implementation != NULL) { - font_face = font_face->backend->get_implementation (font_face, - font_matrix, - ctm, - options); - if (unlikely (font_face->status)) { - _cairo_scaled_font_map_unlock (); - return _cairo_scaled_font_create_in_error (font_face->status); - } - } + if (! scaled_font->placeholder) + break; - _cairo_scaled_font_init_key (&key, font_face, - font_matrix, ctm, options); + /* If the scaled font is being created (happens for user-font), + * just wait until it's done, then retry */ + _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font); + } - while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, - &key.hash_entry))) - { - if (! scaled_font->placeholder) - break; + if (scaled_font != NULL) { + /* If the original reference count is 0, then this font must have + * been found in font_map->holdovers, (which means this caching is + * actually working). So now we remove it from the holdovers + * array, unless we caught the font in the middle of destruction. + */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { + if (scaled_font->holdover) { + int i; - /* If the scaled font is being created (happens for user-font), - * just wait until it's done, then retry */ - _cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font); - } - - /* Return existing scaled_font if it exists in the hash table. */ - if (scaled_font != NULL) { - /* If the original reference count is 0, then this font must have - * been found in font_map->holdovers, (which means this caching is - * actually working). So now we remove it from the holdovers - * array, unless we caught the font in the middle of destruction. - */ - if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { - if (scaled_font->holdover) { - int i; - - for (i = 0; i < font_map->num_holdovers; i++) { - if (font_map->holdovers[i] == scaled_font) { - font_map->num_holdovers--; - memmove (&font_map->holdovers[i], - &font_map->holdovers[i+1], - (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); - break; - } + for (i = 0; i < font_map->num_holdovers; i++) { + if (font_map->holdovers[i] == scaled_font) { + font_map->num_holdovers--; + memmove (&font_map->holdovers[i], + &font_map->holdovers[i+1], + (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*)); + break; } - - scaled_font->holdover = FALSE; } - /* reset any error status */ - scaled_font->status = CAIRO_STATUS_SUCCESS; + scaled_font->holdover = FALSE; } - if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { - /* We increment the reference count manually here, (rather - * than calling into cairo_scaled_font_reference), since we - * must modify the reference count while our lock is still - * held. */ + /* reset any error status */ + scaled_font->status = CAIRO_STATUS_SUCCESS; + } - old = font_map->mru_scaled_font; - font_map->mru_scaled_font = scaled_font; - /* increment reference count for the mru cache */ - _cairo_reference_count_inc (&scaled_font->ref_count); - /* and increment for the returned reference */ - _cairo_reference_count_inc (&scaled_font->ref_count); - _cairo_scaled_font_map_unlock (); + if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { + /* We increment the reference count manually here, (rather + * than calling into cairo_scaled_font_reference), since we + * must modify the reference count while our lock is still + * held. */ - cairo_scaled_font_destroy (old); - if (font_face != original_font_face) - cairo_font_face_destroy (font_face); + old = font_map->mru_scaled_font; + font_map->mru_scaled_font = scaled_font; + /* increment reference count for the mru cache */ + _cairo_reference_count_inc (&scaled_font->ref_count); + /* and increment for the returned reference */ + _cairo_reference_count_inc (&scaled_font->ref_count); + _cairo_scaled_font_map_unlock (); - return scaled_font; - } + cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); - /* the font has been put into an error status - abandon the cache */ - _cairo_hash_table_remove (font_map->hash_table, - &scaled_font->hash_entry); - scaled_font->hash_entry.hash = ZOMBIE; + return scaled_font; + } + + /* the font has been put into an error status - abandon the cache */ + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); + scaled_font->hash_entry.hash = ZOMBIE; + } + + + /* Otherwise create it and insert it into the hash table. */ + if (font_face->backend->get_implementation != NULL) { + font_face = font_face->backend->get_implementation (font_face, + font_matrix, + ctm, + options); + if (unlikely (font_face->status)) { + _cairo_scaled_font_map_unlock (); + return _cairo_scaled_font_create_in_error (font_face->status); } } - /* Otherwise create it and insert it into the hash table. */ status = font_face->backend->scaled_font_create (font_face, font_matrix, ctm, options, &scaled_font); /* Did we leave the backend in an error state? */ @@ -1081,10 +1172,14 @@ cairo_scaled_font_create (cairo_font_face_t *font_face, * ft-font-faces */ assert (scaled_font->font_face == font_face); + assert (! scaled_font->cache_frozen); + assert (! scaled_font->global_cache_frozen); scaled_font->original_font_face = cairo_font_face_reference (original_font_face); + scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash(scaled_font); + status = _cairo_hash_table_insert (font_map->hash_table, &scaled_font->hash_entry); if (likely (status == CAIRO_STATUS_SUCCESS)) { @@ -1131,7 +1226,7 @@ _cairo_scaled_font_create_in_error (cairo_status_t status) CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); scaled_font = _cairo_scaled_font_nil_objects[status]; if (unlikely (scaled_font == NULL)) { - scaled_font = malloc (sizeof (cairo_scaled_font_t)); + scaled_font = _cairo_malloc (sizeof (cairo_scaled_font_t)); if (unlikely (scaled_font == NULL)) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); @@ -1157,10 +1252,8 @@ _cairo_scaled_font_reset_static_data (void) status <= CAIRO_STATUS_LAST_STATUS; status++) { - if (_cairo_scaled_font_nil_objects[status] != NULL) { - free (_cairo_scaled_font_nil_objects[status]); - _cairo_scaled_font_nil_objects[status] = NULL; - } + free (_cairo_scaled_font_nil_objects[status]); + _cairo_scaled_font_nil_objects[status] = NULL; } CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); @@ -1181,10 +1274,12 @@ _cairo_scaled_font_reset_static_data (void) * @scaled_font from being destroyed until a matching call to * cairo_scaled_font_destroy() is made. * - * The number of references to a #cairo_scaled_font_t can be get using - * cairo_scaled_font_get_reference_count(). + * Use cairo_scaled_font_get_reference_count() to get the number of + * references to a #cairo_scaled_font_t. * * Returns: the referenced #cairo_scaled_font_t + * + * Since: 1.0 **/ cairo_scaled_font_t * cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font) @@ -1208,6 +1303,8 @@ slim_hidden_def (cairo_scaled_font_reference); * Decreases the reference count on @font by one. If the result * is zero, then @font and all associated resources are freed. * See cairo_scaled_font_reference(). + * + * Since: 1.0 **/ void cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) @@ -1223,12 +1320,15 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font) assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); - if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count)) - return; - font_map = _cairo_scaled_font_map_lock (); assert (font_map != NULL); + if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count)) + goto unlock; + + assert (! scaled_font->cache_frozen); + assert (! scaled_font->global_cache_frozen); + /* Another thread may have resurrected the font whilst we waited */ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) { if (! scaled_font->placeholder && @@ -1364,6 +1464,8 @@ slim_hidden_def (cairo_scaled_font_set_user_data); * @extents: a #cairo_font_extents_t which to store the retrieved extents. * * Gets the metrics for a #cairo_scaled_font_t. + * + * Since: 1.0 **/ void cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, @@ -1461,6 +1563,8 @@ ZERO_EXTENTS: * * Note that whitespace glyphs do not contribute to the size of the * rectangle (extents.width and extents.height). + * + * Since: 1.0 **/ void cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, @@ -1509,9 +1613,7 @@ cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (unlikely (status)) { - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_scaled_font_set_error (scaled_font, status); - } + status = _cairo_scaled_font_set_error (scaled_font, status); goto UNLOCK; } @@ -1858,7 +1960,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, cairo_text_cluster_flags_t *cluster_flags) { int num_chars = 0; - cairo_status_t status; + cairo_int_status_t status; cairo_glyph_t *orig_glyphs; cairo_text_cluster_t *orig_clusters; @@ -1941,7 +2043,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, clusters, num_clusters, cluster_flags); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - if (status == CAIRO_STATUS_SUCCESS) { + if (status == CAIRO_INT_STATUS_SUCCESS) { /* The checks here are crude; we only should do them in * user-font backend, but they don't hurt here. This stuff * can be hard to get right. */ @@ -1950,7 +2052,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE; } - if (num_glyphs && *glyphs == NULL) { + if (*num_glyphs != 0 && *glyphs == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE; } @@ -1960,7 +2062,7 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); goto DONE; } - if (num_clusters && *clusters == NULL) { + if (*num_clusters != 0 && *clusters == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto DONE; } @@ -2053,12 +2155,53 @@ _range_contains_glyph (const cairo_box_t *extents, cairo_fixed_t right, cairo_fixed_t bottom) { + if (left == right || top == bottom) + return FALSE; + return right > extents->p1.x && left < extents->p2.x && bottom > extents->p1.y && top < extents->p2.y; } +static cairo_status_t +_cairo_scaled_font_single_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyph, + cairo_rectangle_int_t *extents) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph->index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_bool_t round_xy = _cairo_font_options_get_round_glyph_positions (&scaled_font->options) == CAIRO_ROUND_GLYPH_POS_ON; + cairo_box_t box; + cairo_fixed_t v; + + if (round_xy) + v = _cairo_fixed_from_int (_cairo_lround (glyph->x)); + else + v = _cairo_fixed_from_double (glyph->x); + box.p1.x = v + scaled_glyph->bbox.p1.x; + box.p2.x = v + scaled_glyph->bbox.p2.x; + + if (round_xy) + v = _cairo_fixed_from_int (_cairo_lround (glyph->y)); + else + v = _cairo_fixed_from_double (glyph->y); + box.p1.y = v + scaled_glyph->bbox.p1.y; + box.p2.y = v + scaled_glyph->bbox.p2.y; + + _cairo_box_round_to_rectangle (&box, extents); + } + _cairo_scaled_font_thaw_cache (scaled_font); + return status; +} + /* * Compute a device-space bounding box for the glyphs. */ @@ -2079,6 +2222,14 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, if (unlikely (scaled_font->status)) return scaled_font->status; + if (num_glyphs == 1) { + if (overlap_out) + *overlap_out = FALSE; + return _cairo_scaled_font_single_glyph_device_extents (scaled_font, + glyphs, + extents); + } + _cairo_scaled_font_freeze_cache (scaled_font); memset (glyph_cache, 0, sizeof (glyph_cache)); @@ -2142,17 +2293,28 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, return CAIRO_STATUS_SUCCESS; } -void +cairo_bool_t _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, cairo_rectangle_int_t *extents) { - double x0 = HUGE_VAL, x1 = -HUGE_VAL; - double y0 = HUGE_VAL, y1 = -HUGE_VAL; + double x0, x1, y0, y1, pad; int i; - for (i = 0; i < num_glyphs; i++) { + /* If any of the factors are suspect (i.e. the font is broken), bail */ + if (scaled_font->fs_extents.max_x_advance == 0 || + scaled_font->fs_extents.height == 0 || + scaled_font->max_scale == 0) + { + return FALSE; + } + + assert (num_glyphs); + + x0 = x1 = glyphs[0].x; + y0 = y1 = glyphs[0].y; + for (i = 1; i < num_glyphs; i++) { double g; g = glyphs[i].x; @@ -2164,20 +2326,19 @@ _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, if (g > y1) y1 = g; } - if (x0 <= x1 && y0 <= y1) { - extents->x = floor (x0 - scaled_font->extents.max_x_advance); - extents->width = ceil (x1 + scaled_font->extents.max_x_advance); - extents->width -= extents->x; + pad = MAX(scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.height); + pad *= scaled_font->max_scale; - extents->y = floor (y0 - scaled_font->extents.ascent); - extents->height = ceil (y1 + scaled_font->extents.descent); - extents->height -= extents->y; - } else { - extents->x = extents->y = 0; - extents->width = extents->height = 0; - } + extents->x = floor (x0 - pad); + extents->width = ceil (x1 + pad) - extents->x; + extents->y = floor (y0 - pad); + extents->height = ceil (y1 + pad) - extents->y; + return TRUE; } +#if 0 +/* XXX win32 */ cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, @@ -2193,7 +2354,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, int num_glyphs, cairo_region_t *clip_region) { - cairo_status_t status; + cairo_int_status_t status; cairo_surface_t *mask = NULL; cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */ cairo_surface_pattern_t mask_pattern; @@ -2224,7 +2385,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, glyphs += num_glyphs - remaining_glyphs; num_glyphs = remaining_glyphs; if (remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; + status = CAIRO_INT_STATUS_SUCCESS; if (status != CAIRO_INT_STATUS_UNSUPPORTED) return _cairo_scaled_font_set_error (scaled_font, status); } @@ -2274,6 +2435,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, break; case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; @@ -2367,6 +2529,7 @@ CLEANUP_MASK: cairo_surface_destroy (mask); return _cairo_scaled_font_set_error (scaled_font, status); } +#endif /* Add a single-device-unit rectangle to a path. */ static cairo_status_t @@ -2479,7 +2642,7 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, int num_glyphs, cairo_path_fixed_t *path) { - cairo_status_t status; + cairo_int_status_t status; int i; status = scaled_font->status; @@ -2494,9 +2657,9 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); - if (status == CAIRO_STATUS_SUCCESS) { + if (status == CAIRO_INT_STATUS_SUCCESS) { status = _cairo_path_fixed_append (path, - scaled_glyph->path, CAIRO_DIRECTION_FORWARD, + scaled_glyph->path, _cairo_fixed_from_double (glyphs[i].x), _cairo_fixed_from_double (glyphs[i].y)); @@ -2664,13 +2827,31 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; } +void +_cairo_scaled_glyph_set_color_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface) +{ + if (scaled_glyph->color_surface != NULL) + cairo_surface_destroy (&scaled_glyph->color_surface->base); + + /* sanity check the backend glyph contents */ + _cairo_debug_check_image_surface_is_defined (&surface->base); + scaled_glyph->color_surface = surface; + + if (surface != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; + else + scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; +} + static cairo_bool_t _cairo_scaled_glyph_page_can_remove (const void *closure) { const cairo_scaled_glyph_page_t *page = closure; const cairo_scaled_font_t *scaled_font; - scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; + scaled_font = page->scaled_font; return scaled_font->cache_frozen == 0; } @@ -2681,6 +2862,8 @@ _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_page_t *page; cairo_status_t status; + assert (scaled_font->cache_frozen); + /* only the first page in the list may contain available slots */ if (! cairo_list_is_empty (&scaled_font->glyph_pages)) { page = cairo_list_last_entry (&scaled_font->glyph_pages, @@ -2692,11 +2875,12 @@ _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, } } - page = malloc (sizeof (cairo_scaled_glyph_page_t)); + page = _cairo_malloc (sizeof (cairo_scaled_glyph_page_t)); if (unlikely (page == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - page->cache_entry.hash = (uintptr_t) scaled_font; + page->cache_entry.hash = (unsigned long) scaled_font; + page->scaled_font = scaled_font; page->cache_entry.size = 1; /* XXX occupancy weighting? */ page->num_glyphs = 0; @@ -2706,7 +2890,7 @@ _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, status = _cairo_cache_init (&cairo_scaled_glyph_page_cache, NULL, _cairo_scaled_glyph_page_can_remove, - _cairo_scaled_glyph_page_destroy, + _cairo_scaled_glyph_page_pluck, MAX_GLYPH_PAGES_CACHED); if (unlikely (status)) { CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); @@ -2739,6 +2923,7 @@ _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, { cairo_scaled_glyph_page_t *page; + assert (scaled_font->cache_frozen); assert (! cairo_list_is_empty (&scaled_font->glyph_pages)); page = cairo_list_last_entry (&scaled_font->glyph_pages, cairo_scaled_glyph_page_t, @@ -2748,8 +2933,20 @@ _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, _cairo_scaled_glyph_fini (scaled_font, scaled_glyph); if (--page->num_glyphs == 0) { + _cairo_scaled_font_thaw_cache (scaled_font); + CAIRO_MUTEX_LOCK (scaled_font->mutex); + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + /* Temporarily disconnect callback to avoid recursive locking */ + cairo_scaled_glyph_page_cache.entry_destroy = NULL; _cairo_cache_remove (&cairo_scaled_glyph_page_cache, &page->cache_entry); + _cairo_scaled_glyph_page_destroy (scaled_font, page); + cairo_scaled_glyph_page_cache.entry_destroy = _cairo_scaled_glyph_page_pluck; + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + _cairo_scaled_font_freeze_cache (scaled_font); } } @@ -2786,7 +2983,7 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, cairo_scaled_glyph_info_t info, cairo_scaled_glyph_t **scaled_glyph_ret) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; cairo_scaled_glyph_info_t need_info; @@ -2795,6 +2992,9 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, if (unlikely (scaled_font->status)) return scaled_font->status; + assert (CAIRO_MUTEX_IS_LOCKED(scaled_font->mutex)); + assert (scaled_font->cache_frozen); + if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2810,6 +3010,7 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t)); _cairo_scaled_glyph_set_index (scaled_glyph, index); + cairo_list_init (&scaled_glyph->dev_privates); /* ask backend to initialize metrics and shape fields */ status = @@ -2870,11 +3071,13 @@ _cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) * cairo_scaled_font_get_font_face: * @scaled_font: a #cairo_scaled_font_t * - * Gets the font face that this scaled font uses. This is the - * font face passed to cairo_scaled_font_create(). + * Gets the font face that this scaled font uses. This might be the + * font face passed to cairo_scaled_font_create(), but this does not + * hold true for all possible cases. * * Return value: The #cairo_font_face_t with which @scaled_font was - * created. + * created. This object is owned by cairo. To keep a reference to it, + * you must call cairo_scaled_font_reference(). * * Since: 1.2 **/ @@ -2989,22 +3192,11 @@ cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, } slim_hidden_def (cairo_scaled_font_get_font_options); -/** - * cairo_scaled_font_get_hint_metrics: - * @scaled_font: a #cairo_scaled_font_t - * - * Mozilla extension since the required malloc/free to use - * cairo_scaled_font_get_font_options() above is too slow. - **/ -cairo_public cairo_hint_metrics_t -cairo_scaled_font_get_hint_metrics (cairo_scaled_font_t *scaled_font) +cairo_bool_t +_cairo_scaled_font_has_color_glyphs (cairo_scaled_font_t *scaled_font) { - cairo_font_options_t options; - if (scaled_font->status) { - _cairo_font_options_init_default (&options); - } else { - _cairo_font_options_init_copy (&options, &scaled_font->options); - } - return options.hint_metrics; + if (scaled_font->backend != NULL && scaled_font->backend->has_color_glyphs != NULL) + return scaled_font->backend->has_color_glyphs (scaled_font); + else + return FALSE; } -slim_hidden_def (cairo_scaled_font_get_hint_metrics); diff --git a/gfx/cairo/cairo/src/cairo-script-private.h b/gfx/cairo/cairo/src/cairo-script-private.h new file mode 100644 index 000000000000..5b506f500cd0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-script-private.h @@ -0,0 +1,59 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + */ + +#ifndef CAIRO_SCRIPT_PRIVATE_H +#define CAIRO_SCRIPT_PRIVATE_H + +#include "cairo.h" +#include "cairo-script.h" + +#include "cairo-compiler-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +cairo_private cairo_device_t * +_cairo_script_context_create_internal (cairo_output_stream_t *stream); + +cairo_private void +_cairo_script_context_attach_snapshots (cairo_device_t *device, + cairo_bool_t enable); + +slim_hidden_proto (cairo_script_surface_create); + +CAIRO_END_DECLS + +#endif /* CAIRO_SCRIPT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-script-surface.c b/gfx/cairo/cairo/src/cairo-script-surface.c index 2cb427bcbeb1..4d7778b99081 100644 --- a/gfx/cairo/cairo/src/cairo-script-surface.c +++ b/gfx/cairo/cairo/src/cairo-script-surface.c @@ -42,19 +42,45 @@ * without having to copy and hold the data in memory. */ +/** + * SECTION:cairo-script + * @Title: Script Surfaces + * @Short_Description: Rendering to replayable scripts + * @See_Also: #cairo_surface_t + * + * The script surface provides the ability to render to a native + * script that matches the cairo drawing model. The scripts can + * be replayed using tools under the util/cairo-script directory, + * or with cairo-perf-trace. + **/ + +/** + * CAIRO_HAS_SCRIPT_SURFACE: + * + * Defined if the script surface backend is available. + * The script surface backend is always built in since 1.12. + * + * Since: 1.12 + **/ + + #include "cairoint.h" #include "cairo-script.h" +#include "cairo-script-private.h" #include "cairo-analysis-surface-private.h" +#include "cairo-default-context-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" -#include "cairo-list-private.h" -#include "cairo-recording-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-image-surface-private.h" #include "cairo-output-stream-private.h" +#include "cairo-pattern-private.h" +#include "cairo-recording-surface-inline.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-clipper-private.h" -#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-private.h" #include "cairo-surface-wrapper-private.h" @@ -78,7 +104,7 @@ typedef struct _cairo_script_context cairo_script_context_t; typedef struct _cairo_script_surface cairo_script_surface_t; typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; -typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; +typedef struct _cairo_script_font cairo_script_font_t; typedef struct _operand { enum { @@ -98,7 +124,9 @@ struct _cairo_script_context { cairo_device_t base; int active; + int attach_snapshots; + cairo_bool_t owns_stream; cairo_output_stream_t *stream; cairo_script_mode_t mode; @@ -116,8 +144,9 @@ struct _cairo_script_context { cairo_list_t defines; }; -struct _cairo_script_surface_font_private { - cairo_script_context_t *ctx; +struct _cairo_script_font { + cairo_scaled_font_private_t base; + cairo_bool_t has_sfnt; unsigned long id; unsigned long subset_glyph_index; @@ -164,12 +193,12 @@ static const cairo_surface_backend_t _cairo_script_surface_backend; static cairo_script_surface_t * _cairo_script_surface_create_internal (cairo_script_context_t *ctx, cairo_content_t content, - double width, - double height, + cairo_rectangle_t *extents, cairo_surface_t *passthrough); static void -_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); +_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private, + cairo_scaled_font_t *scaled_font); static void _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); @@ -233,8 +262,9 @@ _bitmap_next_id (struct _bitmap *b, prev = &b->next; b = b->next; } while (b != NULL); + assert (prev != NULL); - bb = malloc (sizeof (struct _bitmap)); + bb = _cairo_malloc (sizeof (struct _bitmap)); if (unlikely (bb == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -317,8 +347,8 @@ _extend_to_string (cairo_extend_t extend) { static const char *names[] = { "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ - "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */ - "ExtendMode::REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ }; assert (extend < ARRAY_LENGTH (names)); @@ -330,7 +360,7 @@ _filter_to_string (cairo_filter_t filter) { static const char *names[] = { "FILTER_FAST", /* CAIRO_FILTER_FAST */ - "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ "FILTER_BEST", /* CAIRO_FILTER_BEST */ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ @@ -358,7 +388,10 @@ _antialias_to_string (cairo_antialias_t antialias) "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ - "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + "ANTIALIAS_SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */ + "ANTIALIAS_FAST", /* CAIRO_ANTIALIAS_FAST */ + "ANTIALIAS_GOOD", /* CAIRO_ANTIALIAS_GOOD */ + "ANTIALIAS_BEST" /* CAIRO_ANTIALIAS_BEST */ }; assert (antialias < ARRAY_LENGTH (names)); return names[antialias]; @@ -427,29 +460,32 @@ _get_target (cairo_script_surface_t *surface) { cairo_script_context_t *ctx = to_context (surface); + if (target_is_active (surface)) { + _cairo_output_stream_puts (ctx->stream, "dup "); + return; + } + if (surface->defined) { _cairo_output_stream_printf (ctx->stream, "s%u ", surface->base.unique_id); } else { + int depth = target_depth (surface); + assert (! cairo_list_is_empty (&surface->operand.link)); - if (! target_is_active (surface)) { - int depth = target_depth (surface); - if (ctx->active) { - _cairo_output_stream_printf (ctx->stream, "%d index ", depth); - _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); - } else { - if (depth == 1) { - _cairo_output_stream_puts (ctx->stream, - "exch\n"); - } else { - _cairo_output_stream_printf (ctx->stream, - "%d -1 roll\n", - depth); - } - _cairo_output_stream_puts (ctx->stream, "/target get "); - } + assert (! target_is_active (surface)); + + if (ctx->active) { + _cairo_output_stream_printf (ctx->stream, "%d index ", depth); + _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); } else { - _cairo_output_stream_puts (ctx->stream, "/target get "); + if (depth == 1) { + _cairo_output_stream_puts (ctx->stream, "exch "); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll ", depth); + } + target_push (surface); + _cairo_output_stream_puts (ctx->stream, "dup "); } } } @@ -777,10 +813,8 @@ _emit_dash (cairo_script_surface_t *surface, memcpy (surface->cr.current_style.dash, dash, sizeof (double) * num_dashes); } else { - if (surface->cr.current_style.dash != NULL) { - free (surface->cr.current_style.dash); - surface->cr.current_style.dash = NULL; - } + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; } surface->cr.current_style.num_dashes = num_dashes; @@ -837,7 +871,10 @@ static const char * _format_to_string (cairo_format_t format) { switch (format) { + case CAIRO_FORMAT_RGBA128F: return "RGBA128F"; + case CAIRO_FORMAT_RGB96F: return "RGB96F"; 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_A8: return "A8"; @@ -929,10 +966,8 @@ _emit_linear_pattern (cairo_script_surface_t *surface, _cairo_output_stream_printf (ctx->stream, "%f %f %f %f linear", - _cairo_fixed_to_double (linear->p1.x), - _cairo_fixed_to_double (linear->p1.y), - _cairo_fixed_to_double (linear->p2.x), - _cairo_fixed_to_double (linear->p2.y)); + linear->pd1.x, linear->pd1.y, + linear->pd2.x, linear->pd2.y); return _emit_gradient_color_stops (&linear->base, ctx->stream); } @@ -947,52 +982,197 @@ _emit_radial_pattern (cairo_script_surface_t *surface, _cairo_output_stream_printf (ctx->stream, "%f %f %f %f %f %f radial", - _cairo_fixed_to_double (radial->c1.x), - _cairo_fixed_to_double (radial->c1.y), - _cairo_fixed_to_double (radial->r1), - _cairo_fixed_to_double (radial->c2.x), - _cairo_fixed_to_double (radial->c2.y), - _cairo_fixed_to_double (radial->r2)); + radial->cd1.center.x, + radial->cd1.center.y, + radial->cd1.radius, + radial->cd2.center.x, + radial->cd2.center.y, + radial->cd2.radius); return _emit_gradient_color_stops (&radial->base, ctx->stream); } +static cairo_status_t +_emit_mesh_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_pattern_t *mesh; + cairo_status_t status; + unsigned int i, n; + + mesh = (cairo_pattern_t *) pattern; + status = cairo_mesh_pattern_get_patch_count (mesh, &n); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (ctx->stream, "mesh"); + for (i = 0; i < n; i++) { + cairo_path_t *path; + cairo_path_data_t *data; + int j; + + _cairo_output_stream_printf (ctx->stream, "\n begin-patch"); + + path = cairo_mesh_pattern_get_path (mesh, i); + if (unlikely (path->status)) + return path->status; + + for (j = 0; j < path->num_data; j+=data[0].header.length) { + data = &path->data[j]; + switch (data->header.type) { + case CAIRO_PATH_MOVE_TO: + _cairo_output_stream_printf (ctx->stream, + "\n %f %f m", + data[1].point.x, data[1].point.y); + break; + case CAIRO_PATH_LINE_TO: + _cairo_output_stream_printf (ctx->stream, + "\n %f %f l", + data[1].point.x, data[1].point.y); + break; + case CAIRO_PATH_CURVE_TO: + _cairo_output_stream_printf (ctx->stream, + "\n %f %f %f %f %f %f c", + data[1].point.x, data[1].point.y, + data[2].point.x, data[2].point.y, + data[3].point.x, data[3].point.y); + break; + case CAIRO_PATH_CLOSE_PATH: + break; + } + } + cairo_path_destroy (path); + + for (j = 0; j < 4; j++) { + double x, y; + + status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y); + if (unlikely (status)) + return status; + _cairo_output_stream_printf (ctx->stream, + "\n %d %f %f set-control-point", + j, x, y); + } + + for (j = 0; j < 4; j++) { + double r, g, b, a; + + status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (ctx->stream, + "\n %d %f %f %f %f set-corner-color", + j, r, g, b, a); + } + + _cairo_output_stream_printf (ctx->stream, "\n end-patch"); + } + + return CAIRO_STATUS_SUCCESS; +} + +struct script_snapshot { + cairo_surface_t base; +}; + +static cairo_status_t +script_snapshot_finish (void *abstract_surface) +{ + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t script_snapshot_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + script_snapshot_finish, +}; + +static void +detach_snapshot (cairo_surface_t *abstract_surface) +{ + cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface; + cairo_script_context_t *ctx = to_context (surface); + + _cairo_output_stream_printf (ctx->stream, + "/s%d undef\n", + surface->base.unique_id); +} + +static void +attach_snapshot (cairo_script_context_t *ctx, + cairo_surface_t *source) +{ + struct script_snapshot *surface; + + if (! ctx->attach_snapshots) + return; + + surface = _cairo_malloc (sizeof (*surface)); + if (unlikely (surface == NULL)) + return; + + _cairo_surface_init (&surface->base, + &script_snapshot_backend, + &ctx->base, + source->content, + source->is_vector); + + _cairo_output_stream_printf (ctx->stream, + "dup /s%d exch def ", + surface->base.unique_id); + + _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot); + cairo_surface_destroy (&surface->base); +} + static cairo_status_t _emit_recording_surface_pattern (cairo_script_surface_t *surface, cairo_recording_surface_t *source) { cairo_script_implicit_context_t old_cr; + cairo_script_context_t *ctx = to_context (surface); cairo_script_surface_t *similar; + cairo_surface_t *snapshot; + cairo_rectangle_t r, *extents; cairo_status_t status; - cairo_box_t bbox; - cairo_rectangle_int_t rect; - /* first measure the extents */ - status = _cairo_recording_surface_get_bbox (source, &bbox, NULL); - if (unlikely (status)) - return status; + snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend); + if (snapshot) { + _cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id); + return CAIRO_INT_STATUS_SUCCESS; + } - /* convert to extents so that it matches the public api */ - _cairo_box_round_to_rectangle (&bbox, &rect); + extents = NULL; + if (_cairo_recording_surface_get_bounds (&source->base, &r)) + extents = &r; - similar = _cairo_script_surface_create_internal (to_context (surface), - source->content, - rect.width, - rect.height, + similar = _cairo_script_surface_create_internal (ctx, + source->base.content, + extents, NULL); if (unlikely (similar->base.status)) return similar->base.status; - cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y); similar->base.is_clear = TRUE; - _get_target (surface); - _cairo_output_stream_printf (to_context (surface)->stream, - "%d %d //%s similar dup context\n", - rect.width, rect.height, - _content_to_string (source->content)); + _cairo_output_stream_printf (ctx->stream, "//%s ", + _content_to_string (source->base.content)); + if (extents) { + _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]", + extents->x, extents->y, + extents->width, extents->height); + } else + _cairo_output_stream_puts (ctx->stream, "[]"); + _cairo_output_stream_puts (ctx->stream, " record\n"); + + attach_snapshot (ctx, &source->base); + + _cairo_output_stream_puts (ctx->stream, "dup context\n"); + target_push (similar); similar->emitted = TRUE; + old_cr = surface->cr; _cairo_script_implicit_context_init (&surface->cr); status = _cairo_recording_surface_replay (&source->base, &similar->base); @@ -1006,7 +1186,7 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface, cairo_list_del (&similar->operand.link); assert (target_is_active (surface)); - _cairo_output_stream_puts (to_context (surface)->stream, "pop "); + _cairo_output_stream_puts (ctx->stream, "pop "); cairo_surface_destroy (&similar->base); return CAIRO_STATUS_SUCCESS; @@ -1025,7 +1205,8 @@ static cairo_status_t _write_image_surface (cairo_output_stream_t *output, const cairo_image_surface_t *image) { - int stride, row, width; + int row, width; + ptrdiff_t stride; uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; uint8_t *rowdata; uint8_t *data; @@ -1077,7 +1258,7 @@ _write_image_surface (cairo_output_stream_t *output, } #else if (stride > ARRAY_LENGTH (row_stack)) { - rowdata = malloc (stride); + rowdata = _cairo_malloc (stride); if (unlikely (rowdata == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else @@ -1124,6 +1305,7 @@ _write_image_surface (cairo_output_stream_t *output, data += stride; } break; + case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_ARGB32: for (row = image->height; row--; ) { uint32_t *src = (uint32_t *) data; @@ -1135,6 +1317,18 @@ _write_image_surface (cairo_output_stream_t *output, data += stride; } break; + case CAIRO_FORMAT_RGB96F: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 12*width); + data += stride; + } + break; + case CAIRO_FORMAT_RGBA128F: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 16*width); + data += stride; + } + break; case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; @@ -1182,46 +1376,27 @@ _emit_png_surface (cairo_script_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -struct def { - cairo_script_context_t *ctx; - cairo_user_data_array_t *user_data; - unsigned int tag; - cairo_list_t link; -}; - -static void -_undef (void *data) -{ - struct def *def = data; - - cairo_list_del (&def->link); - _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag); - free (def); -} - -static cairo_status_t +static cairo_int_status_t _emit_image_surface (cairo_script_surface_t *surface, cairo_image_surface_t *image) { cairo_script_context_t *ctx = to_context (surface); cairo_output_stream_t *base85_stream; cairo_output_stream_t *zlib_stream; - cairo_status_t status, status2; + cairo_int_status_t status, status2; + cairo_surface_t *snapshot; const uint8_t *mime_data; unsigned long mime_data_length; - struct def *tag; - if (_cairo_user_data_array_get_data (&image->base.user_data, - (cairo_user_data_key_t *) ctx)) - { - _cairo_output_stream_printf (ctx->stream, - "s%u ", - image->base.unique_id); - return CAIRO_STATUS_SUCCESS; + snapshot = _cairo_surface_has_snapshot (&image->base, + &script_snapshot_backend); + if (snapshot) { + _cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id); + return CAIRO_INT_STATUS_SUCCESS; } status = _emit_png_surface (surface, image); - if (_cairo_status_is_error (status)) { + if (_cairo_int_status_is_error (status)) { return status; } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { cairo_image_surface_t *clone; @@ -1256,11 +1431,20 @@ _emit_image_surface (cairo_script_surface_t *surface, case CAIRO_FORMAT_RGB24: len = clone->width * 3; break; + case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_ARGB32: len = clone->width * 4; break; + case CAIRO_FORMAT_RGB96F: + len = clone->width * 12; + break; + case CAIRO_FORMAT_RGBA128F: + len = clone->width * 16; + break; case CAIRO_FORMAT_INVALID: + default: ASSERT_NOT_REACHED; + len = 0; break; } len *= clone->height; @@ -1277,10 +1461,10 @@ _emit_image_surface (cairo_script_surface_t *surface, status = _write_image_surface (zlib_stream, clone); status2 = _cairo_output_stream_destroy (zlib_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; status2 = _cairo_output_stream_destroy (base85_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; if (unlikely (status)) return status; @@ -1290,7 +1474,7 @@ _emit_image_surface (cairo_script_surface_t *surface, base85_stream = _cairo_base85_stream_create (ctx->stream); status = _write_image_surface (base85_stream, clone); status2 = _cairo_output_stream_destroy (base85_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; if (unlikely (status)) return status; @@ -1300,26 +1484,6 @@ _emit_image_surface (cairo_script_surface_t *surface, cairo_surface_destroy (&clone->base); } - tag = malloc (sizeof (*tag)); - if (unlikely (tag == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - tag->ctx = ctx; - tag->tag = image->base.unique_id; - tag->user_data = &image->base.user_data; - cairo_list_add (&tag->link, &ctx->defines); - status = _cairo_user_data_array_set_data (&image->base.user_data, - (cairo_user_data_key_t *) ctx, - tag, _undef); - if (unlikely (status)) { - free (tag); - return status; - } - - _cairo_output_stream_printf (ctx->stream, - "dup /s%u exch def ", - image->base.unique_id); - cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); if (mime_data != NULL) { @@ -1352,38 +1516,32 @@ _emit_image_surface (cairo_script_surface_t *surface, _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); } - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _emit_image_surface_pattern (cairo_script_surface_t *surface, cairo_surface_t *source) { - cairo_surface_t *snapshot; cairo_image_surface_t *image; cairo_status_t status; void *extra; - /* XXX keeping a copy is nasty, but we want to hook into the surface's - * lifetime. Using a snapshot is a convenient method. - */ - snapshot = _cairo_surface_snapshot (source); - status = _cairo_surface_acquire_source_image (snapshot, &image, &extra); + status = _cairo_surface_acquire_source_image (source, &image, &extra); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _emit_image_surface (surface, image); - _cairo_surface_release_source_image (snapshot, image, extra); + _cairo_surface_release_source_image (source, image, extra); } - cairo_surface_destroy (snapshot); return status; } -static cairo_status_t +static cairo_int_status_t _emit_subsurface_pattern (cairo_script_surface_t *surface, cairo_surface_subsurface_t *sub) { cairo_surface_t *source = sub->target; - cairo_status_t status; + cairo_int_status_t status; switch ((int) source->backend->type) { case CAIRO_SURFACE_TYPE_RECORDING: @@ -1405,22 +1563,36 @@ _emit_subsurface_pattern (cairo_script_surface_t *surface, sub->extents.y, sub->extents.width, sub->extents.height); - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _emit_surface_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { + cairo_script_context_t *ctx = to_context (surface); cairo_surface_pattern_t *surface_pattern; - cairo_surface_t *source; - cairo_status_t status; + cairo_surface_t *source, *snapshot, *free_me = NULL; + cairo_surface_t *take_snapshot = NULL; + cairo_int_status_t status; surface_pattern = (cairo_surface_pattern_t *) pattern; source = surface_pattern->surface; - if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) - source = ((cairo_surface_snapshot_t *) source)->target; + if (_cairo_surface_is_snapshot (source)) { + snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend); + if (snapshot) { + _cairo_output_stream_printf (ctx->stream, + "s%d pattern ", + snapshot->unique_id); + return CAIRO_INT_STATUS_SUCCESS; + } + + if (_cairo_surface_snapshot_is_reused (source)) + take_snapshot = source; + + free_me = source = _cairo_surface_snapshot_get_target (source); + } switch ((int) source->backend->type) { case CAIRO_SURFACE_TYPE_RECORDING: @@ -1436,19 +1608,47 @@ _emit_surface_pattern (cairo_script_surface_t *surface, status = _emit_image_surface_pattern (surface, source); break; } + cairo_surface_destroy (free_me); if (unlikely (status)) return status; - _cairo_output_stream_puts (to_context (surface)->stream, "pattern"); - return CAIRO_STATUS_SUCCESS; + if (take_snapshot) + attach_snapshot (ctx, take_snapshot); + + _cairo_output_stream_puts (ctx->stream, "pattern"); + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t +_emit_raster_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_t *source; + cairo_int_status_t status; + + source = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL); + if (unlikely (source == NULL)) { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (unlikely (source->status)) + return source->status; + + status = _emit_image_surface_pattern (surface, source); + _cairo_raster_source_pattern_release (pattern, source); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (to_context(surface)->stream, "pattern"); + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t _emit_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) { cairo_script_context_t *ctx = to_context (surface); - cairo_status_t status; + cairo_int_status_t status; cairo_bool_t is_default_extend; cairo_bool_t need_newline = TRUE; @@ -1465,10 +1665,18 @@ _emit_pattern (cairo_script_surface_t *surface, status = _emit_radial_pattern (surface, pattern); is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; break; + case CAIRO_PATTERN_TYPE_MESH: + status = _emit_mesh_pattern (surface, pattern); + is_default_extend = TRUE; + break; case CAIRO_PATTERN_TYPE_SURFACE: status = _emit_surface_pattern (surface, pattern); is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + status = _emit_raster_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; + break; default: ASSERT_NOT_REACHED; @@ -1515,17 +1723,17 @@ _emit_pattern (cairo_script_surface_t *surface, if (need_newline) _cairo_output_stream_puts (ctx->stream, "\n "); - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _emit_identity (cairo_script_surface_t *surface, cairo_bool_t *matrix_updated) { assert (target_is_active (surface)); if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; _cairo_output_stream_puts (to_context (surface)->stream, "identity set-matrix\n"); @@ -1533,26 +1741,26 @@ _emit_identity (cairo_script_surface_t *surface, *matrix_updated = TRUE; cairo_matrix_init_identity (&surface->cr.current_ctm); - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _emit_source (cairo_script_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source) { cairo_bool_t matrix_updated = FALSE; - cairo_status_t status; + cairo_int_status_t status; assert (target_is_active (surface)); if (op == CAIRO_OPERATOR_CLEAR) { /* the source is ignored, so don't change it */ - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; _cairo_pattern_fini (&surface->cr.current_source.base); status = _cairo_pattern_init_copy (&surface->cr.current_source.base, @@ -1571,7 +1779,7 @@ _emit_source (cairo_script_surface_t *surface, assert (target_is_active (surface)); _cairo_output_stream_puts (to_context (surface)->stream, " set-source\n"); - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } static cairo_status_t @@ -1625,13 +1833,62 @@ _path_close (void *closure) return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_emit_path_boxes (cairo_script_surface_t *surface, + const cairo_path_fixed_t *path) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_path_fixed_iter_t iter; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + struct _cairo_boxes_chunk *chunk; + cairo_boxes_t boxes; + cairo_box_t box; + int i; + + _cairo_boxes_init (&boxes); + _cairo_path_fixed_iter_init (&iter, path); + while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { + if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) + continue; + + status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box); + if (unlikely (status)) { + _cairo_boxes_fini (&boxes); + return status; + } + } + + if (! _cairo_path_fixed_iter_at_end (&iter)) { + _cairo_boxes_fini (&boxes); + return CAIRO_STATUS_INVALID_PATH_DATA; + } + + for (chunk = &boxes.chunks; chunk; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + const cairo_box_t *b = &chunk->base[i]; + double x1 = _cairo_fixed_to_double (b->p1.x); + double y1 = _cairo_fixed_to_double (b->p1.y); + double x2 = _cairo_fixed_to_double (b->p2.x); + double y2 = _cairo_fixed_to_double (b->p2.y); + + _cairo_output_stream_printf (ctx->stream, + "\n %f %f %f %f rectangle", + x1, y1, x2 - x1, y2 - y1); + } + } + + _cairo_boxes_fini (&boxes); + return status; +} + static cairo_status_t _emit_path (cairo_script_surface_t *surface, - cairo_path_fixed_t *path) + const cairo_path_fixed_t *path, + cairo_bool_t is_fill) { cairo_script_context_t *ctx = to_context (surface); cairo_box_t box; - cairo_status_t status; + cairo_int_status_t status; assert (target_is_active (surface)); assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); @@ -1645,40 +1902,43 @@ _emit_path (cairo_script_surface_t *surface, if (path == NULL) { _cairo_path_fixed_init (&surface->cr.current_path); - } else if (_cairo_path_fixed_is_box (path, &box)) { + _cairo_output_stream_puts (ctx->stream, "\n"); + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (unlikely (status)) + return status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_is_rectangle (path, &box)) { double x1 = _cairo_fixed_to_double (box.p1.x); double y1 = _cairo_fixed_to_double (box.p1.y); double x2 = _cairo_fixed_to_double (box.p2.x); double y2 = _cairo_fixed_to_double (box.p2.y); - status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); - if (unlikely (status)) - return status; + assert (x1 > -9999); _cairo_output_stream_printf (ctx->stream, " %f %f %f %f rectangle", x1, y1, x2 - x1, y2 - y1); - } else { - cairo_status_t status; - - status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); - if (unlikely (status)) - return status; + status = CAIRO_INT_STATUS_SUCCESS; + } else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) { + status = _emit_path_boxes (surface, path); + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _path_move_to, _path_line_to, _path_curve_to, _path_close, ctx->stream); - if (unlikely (status)) - return status; } _cairo_output_stream_puts (ctx->stream, "\n"); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_bool_t _scaling_matrix_equal (const cairo_matrix_t *a, @@ -1765,6 +2025,7 @@ _cairo_script_surface_create_similar (void *abstract_surface, cairo_script_surface_t *surface, *other = abstract_surface; cairo_surface_t *passthrough = NULL; cairo_script_context_t *ctx; + cairo_rectangle_t extents; cairo_status_t status; ctx = to_context (other); @@ -1793,10 +2054,11 @@ _cairo_script_surface_create_similar (void *abstract_surface, } } - surface = _cairo_script_surface_create_internal (ctx, - content, - width, height, - passthrough); + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + surface = _cairo_script_surface_create_internal (ctx, content, + &extents, passthrough); cairo_surface_destroy (passthrough); if (unlikely (surface->base.status)) { @@ -1810,6 +2072,7 @@ _cairo_script_surface_create_similar (void *abstract_surface, width, height, _content_to_string (content), surface->base.unique_id); + surface->emitted = TRUE; surface->defined = TRUE; surface->base.is_clear = TRUE; @@ -1819,13 +2082,12 @@ _cairo_script_surface_create_similar (void *abstract_surface, return &surface->base; } -static void +static cairo_status_t _device_flush (void *abstract_device) { cairo_script_context_t *ctx = abstract_device; - cairo_status_t status; - status = _cairo_output_stream_flush (ctx->stream); + return _cairo_output_stream_flush (ctx->stream); } static void @@ -1835,35 +2097,38 @@ _device_destroy (void *abstract_device) cairo_status_t status; while (! cairo_list_is_empty (&ctx->fonts)) { - cairo_script_surface_font_private_t *font; + cairo_script_font_t *font; - font = cairo_list_first_entry (&ctx->fonts, - cairo_script_surface_font_private_t, - link); + font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link); + cairo_list_del (&font->base.link); cairo_list_del (&font->link); - if (font->parent->surface_private == font) - font->parent->surface_private = NULL; free (font); } - while (! cairo_list_is_empty (&ctx->defines)) { - struct def *def = cairo_list_first_entry (&ctx->defines, - struct def, link); - - status = _cairo_user_data_array_set_data (def->user_data, - (cairo_user_data_key_t *) ctx, - NULL, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - } - _bitmap_fini (ctx->surface_id.next); _bitmap_fini (ctx->font_id.next); - status = _cairo_output_stream_destroy (ctx->stream); + if (ctx->owns_stream) + status = _cairo_output_stream_destroy (ctx->stream); free (ctx); } +static cairo_surface_t * +_cairo_script_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_script_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_script_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, @@ -1902,10 +2167,9 @@ _cairo_script_surface_finish (void *abstract_surface) _cairo_surface_wrapper_fini (&surface->wrapper); - if (surface->cr.current_style.dash != NULL) { - free (surface->cr.current_style.dash); - surface->cr.current_style.dash = NULL; - } + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + _cairo_pattern_fini (&surface->cr.current_source.base); _cairo_path_fixed_fini (&surface->cr.current_path); _cairo_surface_clipper_reset (&surface->clipper); @@ -1935,7 +2199,7 @@ _cairo_script_surface_finish (void *abstract_surface) } cairo_list_del (&surface->operand.link); } else { - struct deferred_finish *link = malloc (sizeof (*link)); + struct deferred_finish *link = _cairo_malloc (sizeof (*link)); if (link == NULL) { status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); if (status == CAIRO_STATUS_SUCCESS) @@ -2035,12 +2299,12 @@ _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip } /* skip the trivial clip covering the surface extents */ - if (surface->width >=0 && surface->height >= 0 && + if (surface->width >= 0 && surface->height >= 0 && _cairo_path_fixed_is_box (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)) + box.p2.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } @@ -2054,19 +2318,19 @@ _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip if (unlikely (status)) return status; - if (! path->is_rectilinear) { + if (path->has_curve_to) { status = _emit_tolerance (surface, tolerance, matrix_updated); if (unlikely (status)) return status; } - if (! path->maybe_fill_region) { + if (! _cairo_path_fixed_fill_maybe_region (path)) { status = _emit_antialias (surface, antialias); if (unlikely (status)) return status; } - status = _emit_path (surface, path); + status = _emit_path (surface, path, TRUE); if (unlikely (status)) return status; @@ -2181,7 +2445,7 @@ static cairo_int_status_t _cairo_script_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_status_t status; @@ -2228,7 +2492,7 @@ _cairo_script_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_status_t status; @@ -2284,13 +2548,13 @@ static cairo_int_status_t _cairo_script_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_bool_t matrix_updated = FALSE; @@ -2312,7 +2576,7 @@ _cairo_script_surface_stroke (void *abstract_surface, if (unlikely (status)) goto BAIL; - status = _emit_path (surface, path); + status = _emit_path (surface, path, FALSE); if (unlikely (status)) goto BAIL; @@ -2343,11 +2607,9 @@ _cairo_script_surface_stroke (void *abstract_surface, if (unlikely (status)) goto BAIL; - if (! path->is_rectilinear) { - status = _emit_tolerance (surface, tolerance, matrix_updated); - if (unlikely (status)) - goto BAIL; - } + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + goto BAIL; status = _emit_antialias (surface, antialias); if (unlikely (status)) @@ -2377,11 +2639,11 @@ static cairo_int_status_t _cairo_script_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_bool_t matrix_updated = FALSE; @@ -2414,19 +2676,19 @@ _cairo_script_surface_fill (void *abstract_surface, goto BAIL; } - if (! path->is_rectilinear) { + if (path->has_curve_to) { status = _emit_tolerance (surface, tolerance, matrix_updated); if (unlikely (status)) goto BAIL; } - if (! path->maybe_fill_region) { + if (! _cairo_path_fixed_fill_maybe_region (path)) { status = _emit_antialias (surface, antialias); if (unlikely (status)) goto BAIL; } - status = _emit_path (surface, path); + status = _emit_path (surface, path, TRUE); if (unlikely (status)) goto BAIL; @@ -2558,31 +2820,39 @@ _emit_font_options (cairo_script_surface_t *surface, } static void -_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private, + cairo_scaled_font_t *scaled_font) { - cairo_script_surface_font_private_t *font_private; + cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private; + cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key; + cairo_status_t status; - font_private = scaled_font->surface_private; - if (font_private != NULL) { - cairo_status_t status; - cairo_device_t *device; + status = cairo_device_acquire (&ctx->base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + _cairo_output_stream_printf (ctx->stream, + "/f%lu undef /sf%lu undef\n", + priv->id, + priv->id); - status = cairo_device_acquire (device = &font_private->ctx->base); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_output_stream_printf (font_private->ctx->stream, - "/f%lu undef /sf%lu undef\n", - font_private->id, - font_private->id); - - _bitmap_release_id (&font_private->ctx->font_id, font_private->id); - cairo_list_del (&font_private->link); - free (font_private); - - cairo_device_release (device); - } - - scaled_font->surface_private = NULL; + _bitmap_release_id (&ctx->font_id, priv->id); + cairo_device_release (&ctx->base); } + + cairo_list_del (&priv->link); + cairo_list_del (&priv->base.link); + free (priv); +} + +static cairo_script_font_t * +_cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font) +{ + return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx); +} + +static long unsigned +_cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font) +{ + return _cairo_script_font_get (ctx, font)->id; } static cairo_status_t @@ -2591,7 +2861,6 @@ _emit_type42_font (cairo_script_surface_t *surface, { cairo_script_context_t *ctx = to_context (surface); const cairo_scaled_font_backend_t *backend; - cairo_script_surface_font_private_t *font_private; cairo_output_stream_t *base85_stream; cairo_output_stream_t *zlib_stream; cairo_status_t status, status2; @@ -2609,7 +2878,7 @@ _emit_type42_font (cairo_script_surface_t *surface, if (unlikely (status)) return status; - buf = malloc (size); + buf = _cairo_malloc (size); if (unlikely (buf == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2649,27 +2918,29 @@ _emit_type42_font (cairo_script_surface_t *surface, if (status == CAIRO_STATUS_SUCCESS) status = status2; - font_private = scaled_font->surface_private; _cairo_output_stream_printf (ctx->stream, "~> >> font dup /f%lu exch def set-font-face", - font_private->id); + _cairo_script_font_id (ctx, scaled_font)); return status; } static cairo_status_t _emit_scaled_font_init (cairo_script_surface_t *surface, - cairo_scaled_font_t *scaled_font) + cairo_scaled_font_t *scaled_font, + cairo_script_font_t **font_out) { cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; - cairo_status_t status; + cairo_script_font_t *font_private; + cairo_int_status_t status; - font_private = malloc (sizeof (cairo_script_surface_font_private_t)); + font_private = _cairo_malloc (sizeof (cairo_script_font_t)); if (unlikely (font_private == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font_private->ctx = ctx; + _cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx, + _cairo_script_scaled_font_fini); + font_private->parent = scaled_font; font_private->subset_glyph_index = 0; font_private->has_sfnt = TRUE; @@ -2683,16 +2954,17 @@ _emit_scaled_font_init (cairo_script_surface_t *surface, return status; } - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &_cairo_script_surface_backend; - status = _emit_context (surface); - if (unlikely (status)) + if (unlikely (status)) { + free (font_private); return status; + } status = _emit_type42_font (surface, scaled_font); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *font_out = font_private; return status; + } font_private->has_sfnt = FALSE; _cairo_output_stream_printf (ctx->stream, @@ -2708,6 +2980,7 @@ _emit_scaled_font_init (cairo_script_surface_t *surface, scaled_font->fs_extents.max_y_advance, font_private->id); + *font_out = font_private; return CAIRO_STATUS_SUCCESS; } @@ -2720,7 +2993,7 @@ _emit_scaled_font (cairo_script_surface_t *surface, cairo_font_options_t options; cairo_bool_t matrix_updated = FALSE; cairo_status_t status; - cairo_script_surface_font_private_t *font_private; + cairo_script_font_t *font_private; cairo_scaled_font_get_ctm (scaled_font, &matrix); status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); @@ -2732,13 +3005,7 @@ _emit_scaled_font (cairo_script_surface_t *surface, surface->cr.current_scaled_font = scaled_font; - if (! (scaled_font->surface_backend == NULL || - scaled_font->surface_backend == &_cairo_script_surface_backend)) - { - _cairo_scaled_font_revoke_ownership (scaled_font); - } - - font_private = scaled_font->surface_private; + font_private = _cairo_script_font_get (ctx, scaled_font); if (font_private == NULL) { cairo_scaled_font_get_font_matrix (scaled_font, &matrix); status = _emit_font_matrix (surface, &matrix); @@ -2750,13 +3017,10 @@ _emit_scaled_font (cairo_script_surface_t *surface, if (unlikely (status)) return status; - status = _emit_scaled_font_init (surface, scaled_font); + status = _emit_scaled_font_init (surface, scaled_font, &font_private); if (unlikely (status)) return status; - font_private = scaled_font->surface_private; - assert (font_private != NULL); - assert (target_is_active (surface)); _cairo_output_stream_printf (ctx->stream, " /scaled-font get /sf%lu exch def\n", @@ -2773,17 +3037,17 @@ _emit_scaled_font (cairo_script_surface_t *surface, static cairo_status_t _emit_scaled_glyph_vector (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font, + cairo_script_font_t *font_private, cairo_scaled_glyph_t *scaled_glyph) { cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; cairo_script_implicit_context_t old_cr; cairo_status_t status; unsigned long index; - font_private = scaled_font->surface_private; index = ++font_private->subset_glyph_index; - scaled_glyph->surface_private = (void *) index; + scaled_glyph->dev_private_key = ctx; + scaled_glyph->dev_private = (void *) index; _cairo_output_stream_printf (ctx->stream, "%lu <<\n" @@ -2822,16 +3086,16 @@ _emit_scaled_glyph_vector (cairo_script_surface_t *surface, static cairo_status_t _emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font, + cairo_script_font_t *font_private, cairo_scaled_glyph_t *scaled_glyph) { cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; cairo_status_t status; unsigned long index; - font_private = scaled_font->surface_private; index = ++font_private->subset_glyph_index; - scaled_glyph->surface_private = (void *) index; + scaled_glyph->dev_private_key = ctx; + scaled_glyph->dev_private = (void *) index; _cairo_output_stream_printf (ctx->stream, "%lu <<\n" @@ -2874,15 +3138,10 @@ static cairo_status_t _emit_scaled_glyph_prologue (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font) { - cairo_script_surface_font_private_t *font_private; + cairo_script_context_t *ctx = to_context (surface); - assert (scaled_font->surface_backend == &_cairo_script_surface_backend); - - font_private = scaled_font->surface_private; - - _cairo_output_stream_printf (to_context (surface)->stream, - "f%lu /glyphs get\n", - font_private->id); + _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n", + _cairo_script_font_id (ctx, scaled_font)); return CAIRO_STATUS_SUCCESS; } @@ -2893,7 +3152,8 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, cairo_glyph_t *glyphs, unsigned int num_glyphs) { - cairo_script_surface_font_private_t *font_private; + cairo_script_context_t *ctx = to_context (surface); + cairo_script_font_t *font_private; cairo_status_t status; unsigned int n; cairo_bool_t have_glyph_prologue = FALSE; @@ -2901,7 +3161,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, if (num_glyphs == 0) return CAIRO_STATUS_SUCCESS; - font_private = scaled_font->surface_private; + font_private = _cairo_script_font_get (ctx, scaled_font); if (font_private->has_sfnt) return CAIRO_STATUS_SUCCESS; @@ -2916,7 +3176,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, if (unlikely (status)) break; - if (scaled_glyph->surface_private != NULL) + if (scaled_glyph->dev_private_key == ctx) continue; status = _cairo_scaled_glyph_lookup (scaled_font, @@ -2936,7 +3196,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, } status = _emit_scaled_glyph_vector (surface, - scaled_font, + scaled_font, font_private, scaled_glyph); if (unlikely (status)) break; @@ -2962,6 +3222,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, status = _emit_scaled_glyph_bitmap (surface, scaled_font, + font_private, scaled_glyph); if (unlikely (status)) break; @@ -3055,11 +3316,11 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t backward, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_script_surface_t *surface = abstract_surface; cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; + cairo_script_font_t *font_private; cairo_scaled_glyph_t *scaled_glyph; cairo_matrix_t matrix; cairo_status_t status; @@ -3114,7 +3375,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, iy -= scaled_font->font_matrix.y0; _cairo_scaled_font_freeze_cache (scaled_font); - font_private = scaled_font->surface_private; + font_private = _cairo_script_font_get (ctx, scaled_font); _cairo_output_stream_printf (ctx->stream, "[%f %f ", @@ -3134,7 +3395,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, goto BAIL; } - if ((long unsigned) scaled_glyph->surface_private > 256) + if ((long unsigned) scaled_glyph->dev_private > 256) break; } } @@ -3152,8 +3413,10 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, glyphs[n].index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); - if (unlikely (status)) + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (scaled_font); goto BAIL; + } if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { if (fabs (glyphs[n].y - y) < 1e-5) { @@ -3203,7 +3466,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, if (font_private->has_sfnt) c = glyphs[n].index; else - c = (uint8_t) (long unsigned) scaled_glyph->surface_private; + c = (uint8_t) (long unsigned) scaled_glyph->dev_private; _cairo_output_stream_write (base85_stream, &c, 1); } else { @@ -3212,7 +3475,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, glyphs[n].index); else _cairo_output_stream_printf (ctx->stream, " %lu", - (long unsigned) scaled_glyph->surface_private); + (long unsigned) scaled_glyph->dev_private); } dx = scaled_glyph->metrics.x_advance; @@ -3326,44 +3589,35 @@ _cairo_script_surface_get_extents (void *abstract_surface, static const cairo_surface_backend_t _cairo_script_surface_backend = { CAIRO_SURFACE_TYPE_SCRIPT, - _cairo_script_surface_create_similar, _cairo_script_surface_finish, + + _cairo_default_context_create, + + _cairo_script_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_script_surface_source, _cairo_script_surface_acquire_source_image, _cairo_script_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + _cairo_script_surface_snapshot, + _cairo_script_surface_copy_page, _cairo_script_surface_show_page, + _cairo_script_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - _cairo_script_surface_scaled_font_fini, - NULL, /* scaled_glyph_fini */ - /* The 5 high level operations */ _cairo_script_surface_paint, _cairo_script_surface_mask, _cairo_script_surface_stroke, _cairo_script_surface_fill, - NULL, - - _cairo_script_surface_snapshot, - - NULL, /* is_similar */ - /* XXX need fill-stroke for passthrough */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - /* The alternate high-level text operation */ + NULL, /* fill/stroke */ + NULL, /* glyphs */ _cairo_script_surface_has_show_text_glyphs, _cairo_script_surface_show_text_glyphs }; @@ -3390,10 +3644,9 @@ _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) static void _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) { - if (cr->current_style.dash != NULL) { - free (cr->current_style.dash); - cr->current_style.dash = NULL; - } + free (cr->current_style.dash); + cr->current_style.dash = NULL; + _cairo_pattern_fini (&cr->current_source.base); _cairo_path_fixed_fini (&cr->current_path); @@ -3403,8 +3656,7 @@ _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) static cairo_script_surface_t * _cairo_script_surface_create_internal (cairo_script_context_t *ctx, cairo_content_t content, - double width, - double height, + cairo_rectangle_t *extents, cairo_surface_t *passthrough) { cairo_script_surface_t *surface; @@ -3412,22 +3664,28 @@ _cairo_script_surface_create_internal (cairo_script_context_t *ctx, if (unlikely (ctx == NULL)) return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); - surface = malloc (sizeof (cairo_script_surface_t)); + surface = _cairo_malloc (sizeof (cairo_script_surface_t)); if (unlikely (surface == NULL)) return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_script_surface_backend, &ctx->base, - content); + content, + TRUE); /* is_vector */ _cairo_surface_wrapper_init (&surface->wrapper, passthrough); _cairo_surface_clipper_init (&surface->clipper, _cairo_script_surface_clipper_intersect_clip_path); - surface->width = width; - surface->height = height; + surface->width = surface->height = -1; + if (extents) { + surface->width = extents->width; + surface->height = extents->height; + cairo_surface_set_device_offset (&surface->base, + -extents->x, -extents->y); + } surface->emitted = FALSE; surface->defined = FALSE; @@ -3450,12 +3708,12 @@ static const cairo_device_backend_t _cairo_script_device_backend = { _device_destroy }; -static cairo_device_t * +cairo_device_t * _cairo_script_context_create_internal (cairo_output_stream_t *stream) { cairo_script_context_t *ctx; - ctx = malloc (sizeof (cairo_script_context_t)); + ctx = _cairo_malloc (sizeof (cairo_script_context_t)); if (unlikely (ctx == NULL)) return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -3471,11 +3729,53 @@ _cairo_script_context_create_internal (cairo_output_stream_t *stream) cairo_list_init (&ctx->fonts); cairo_list_init (&ctx->defines); - _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); + ctx->attach_snapshots = TRUE; return &ctx->base; } +void +_cairo_script_context_attach_snapshots (cairo_device_t *device, + cairo_bool_t enable) +{ + cairo_script_context_t *ctx; + + ctx = (cairo_script_context_t *) device; + ctx->attach_snapshots = enable; +} + +static cairo_device_t * +_cairo_script_context_create (cairo_output_stream_t *stream) +{ + cairo_script_context_t *ctx; + + ctx = (cairo_script_context_t *) + _cairo_script_context_create_internal (stream); + if (unlikely (ctx->base.status)) + return &ctx->base; + + ctx->owns_stream = TRUE; + _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); + return &ctx->base; +} + +/** + * cairo_script_create: + * @filename: the name (path) of the file to write the script to + * + * Creates a output device for emitting the script, used when + * creating the individual surfaces. + * + * Return value: a pointer to the newly created device. The caller + * owns the surface and should call cairo_device_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" device if an error such as out of memory + * occurs. You can use cairo_device_status() to check for this. + * + * Since: 1.12 + **/ cairo_device_t * cairo_script_create (const char *filename) { @@ -3486,9 +3786,27 @@ cairo_script_create (const char *filename) if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); - return _cairo_script_context_create_internal (stream); + return _cairo_script_context_create (stream); } +/** + * cairo_script_create_for_stream: + * @write_func: callback function passed the bytes written to the script + * @closure: user data to be passed to the callback + * + * Creates a output device for emitting the script, used when + * creating the individual surfaces. + * + * Return value: a pointer to the newly created device. The caller + * owns the surface and should call cairo_device_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" device if an error such as out of memory + * occurs. You can use cairo_device_status() to check for this. + * + * Since: 1.12 + **/ cairo_device_t * cairo_script_create_for_stream (cairo_write_func_t write_func, void *closure) @@ -3500,15 +3818,25 @@ cairo_script_create_for_stream (cairo_write_func_t write_func, if ((status = _cairo_output_stream_get_status (stream))) return _cairo_device_create_in_error (status); - return _cairo_script_context_create_internal (stream); + return _cairo_script_context_create (stream); } +/** + * cairo_script_write_comment: + * @script: the script (output device) + * @comment: the string to emit + * @len:the length of the string to write, or -1 to use strlen() + * + * Emit a string verbatim into the script. + * + * Since: 1.12 + **/ void -cairo_script_write_comment (cairo_device_t *device, +cairo_script_write_comment (cairo_device_t *script, const char *comment, int len) { - cairo_script_context_t *context = (cairo_script_context_t *) device; + cairo_script_context_t *context = (cairo_script_context_t *) script; if (len < 0) len = strlen (comment); @@ -3518,80 +3846,158 @@ cairo_script_write_comment (cairo_device_t *device, _cairo_output_stream_puts (context->stream, "\n"); } +/** + * cairo_script_set_mode: + * @script: The script (output device) + * @mode: the new mode + * + * Change the output mode of the script + * + * Since: 1.12 + **/ void -cairo_script_set_mode (cairo_device_t *device, +cairo_script_set_mode (cairo_device_t *script, cairo_script_mode_t mode) { - cairo_script_context_t *context = (cairo_script_context_t *) device; + cairo_script_context_t *context = (cairo_script_context_t *) script; context->mode = mode; } +/** + * cairo_script_get_mode: + * @script: The script (output device) to query + * + * Queries the script for its current output mode. + * + * Return value: the current output mode of the script + * + * Since: 1.12 + **/ cairo_script_mode_t -cairo_script_get_mode (cairo_device_t *device) +cairo_script_get_mode (cairo_device_t *script) { - cairo_script_context_t *context = (cairo_script_context_t *) device; + cairo_script_context_t *context = (cairo_script_context_t *) script; return context->mode; } +/** + * cairo_script_surface_create: + * @script: the script (output device) + * @content: the content of the surface + * @width: width in pixels + * @height: height in pixels + * + * Create a new surface that will emit its rendering through @script + * + * 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: 1.12 + **/ cairo_surface_t * -cairo_script_surface_create (cairo_device_t *device, +cairo_script_surface_create (cairo_device_t *script, cairo_content_t content, double width, double height) { - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + cairo_rectangle_t *extents, r; + + if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); + if (unlikely (script->status)) + return _cairo_surface_create_in_error (script->status); - return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, - content, - width, height, + extents = NULL; + if (width > 0 && height > 0) { + r.x = r.y = 0; + r.width = width; + r.height = height; + extents = &r; + } + return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, + content, extents, NULL)->base; } +slim_hidden_def (cairo_script_surface_create); +/** + * cairo_script_surface_create_for_target: + * @script: the script (output device) + * @target: a target surface to wrap + * + * Create a pxoy surface that will render to @target and record + * the operations to @device. + * + * 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: 1.12 + **/ cairo_surface_t * -cairo_script_surface_create_for_target (cairo_device_t *device, +cairo_script_surface_create_for_target (cairo_device_t *script, cairo_surface_t *target) { cairo_rectangle_int_t extents; + cairo_rectangle_t rect, *r; - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - if (unlikely (device->status)) - return _cairo_surface_create_in_error (device->status); + if (unlikely (script->status)) + return _cairo_surface_create_in_error (script->status); if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); - if (! _cairo_surface_get_extents (target, &extents)) - extents.width = extents.height = -1; - - return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, - target->content, - extents.width, - extents.height, + r = NULL; + if (_cairo_surface_get_extents (target, &extents)) { + rect.x = rect.y = 0; + rect.width = extents.width; + rect.height = extents.height; + r= ▭ + } + return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, + target->content, r, target)->base; } +/** + * cairo_script_from_recording_surface: + * @script: the script (output device) + * @recording_surface: the recording surface to replay + * + * Converts the record operations in @recording_surface into a script. + * + * Return value: #CAIRO_STATUS_SUCCESS on successful completion or an error code. + * + * Since: 1.12 + **/ cairo_status_t -cairo_script_from_recording_surface (cairo_device_t *device, +cairo_script_from_recording_surface (cairo_device_t *script, cairo_surface_t *recording_surface) { - cairo_box_t bbox; - cairo_rectangle_int_t extents; + cairo_rectangle_t r, *extents; cairo_surface_t *surface; cairo_status_t status; - if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); - if (unlikely (device->status)) - return _cairo_error (device->status); + if (unlikely (script->status)) + return _cairo_error (script->status); if (unlikely (recording_surface->status)) return recording_surface->status; @@ -3599,22 +4005,17 @@ cairo_script_from_recording_surface (cairo_device_t *device, 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; + extents = NULL; + if (_cairo_recording_surface_get_bounds (recording_surface, &r)) + extents = &r; - _cairo_box_round_to_rectangle (&bbox, &extents); - - surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, + surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, recording_surface->content, - extents.width, - extents.height, + extents, NULL)->base; if (unlikely (surface->status)) return surface->status; - cairo_surface_set_device_offset (surface, -extents.x, -extents.y); status = _cairo_recording_surface_replay (recording_surface, surface); cairo_surface_destroy (surface); diff --git a/gfx/cairo/cairo/src/cairo-script.h b/gfx/cairo/cairo/src/cairo-script.h index b82230f2f475..b5a8cf32d0b6 100644 --- a/gfx/cairo/cairo/src/cairo-script.h +++ b/gfx/cairo/cairo/src/cairo-script.h @@ -42,9 +42,18 @@ CAIRO_BEGIN_DECLS +/** + * cairo_script_mode_t: + * @CAIRO_SCRIPT_MODE_ASCII: the output will be in readable text (default). (Since 1.12) + * @CAIRO_SCRIPT_MODE_BINARY: the output will use byte codes. (Since 1.12) + * + * A set of script output variants. + * + * Since: 1.12 + **/ typedef enum { - CAIRO_SCRIPT_MODE_BINARY, - CAIRO_SCRIPT_MODE_ASCII + CAIRO_SCRIPT_MODE_ASCII, + CAIRO_SCRIPT_MODE_BINARY } cairo_script_mode_t; cairo_public cairo_device_t * diff --git a/gfx/cairo/cairo/src/cairo-shape-mask-compositor.c b/gfx/cairo/cairo/src/cairo-shape-mask-compositor.c new file mode 100644 index 000000000000..3117267cccf1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-shape-mask-compositor.c @@ -0,0 +1,340 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2012 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" +#include "cairo-clip-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-private.h" +#include "cairo-surface-offset-private.h" + +static cairo_int_status_t +_cairo_shape_mask_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status; + cairo_clip_t *clip; + + if (! extents->is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + mask = _cairo_surface_create_scratch (extents->surface, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (mask->status)) + return mask->status; + + clip = extents->clip; + if (! _cairo_clip_is_region (clip)) + clip = _cairo_clip_copy_region (clip); + + if (! mask->is_clear) { + status = _cairo_surface_offset_paint (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + clip); + if (unlikely (status)) + goto error; + } + + status = _cairo_surface_offset_stroke (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, style, ctm, ctm_inverse, + tolerance, antialias, + clip); + if (unlikely (status)) + goto error; + + if (clip != extents->clip) { + status = _cairo_clip_combine_with_surface (extents->clip, mask, + extents->bounded.x, + extents->bounded.y); + if (unlikely (status)) + goto error; + } + + _cairo_pattern_init_for_surface (&pattern, mask); + cairo_matrix_init_translate (&pattern.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + pattern.base.extend = CAIRO_EXTEND_NONE; + if (extents->op == CAIRO_OPERATOR_SOURCE) { + status = _cairo_surface_mask (extents->surface, + CAIRO_OPERATOR_DEST_OUT, + &_cairo_pattern_white.base, + &pattern.base, + clip); + if ((status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_surface_mask (extents->surface, + CAIRO_OPERATOR_ADD, + &extents->source_pattern.base, + &pattern.base, + clip); + } + } else { + status = _cairo_surface_mask (extents->surface, + extents->op, + &extents->source_pattern.base, + &pattern.base, + clip); + } + _cairo_pattern_fini (&pattern.base); + +error: + cairo_surface_destroy (mask); + if (clip != extents->clip) + _cairo_clip_destroy (clip); + return status; +} + +static cairo_int_status_t +_cairo_shape_mask_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status; + cairo_clip_t *clip; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (! extents->is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + mask = _cairo_surface_create_scratch (extents->surface, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (mask->status)) + return mask->status; + + clip = extents->clip; + if (! _cairo_clip_is_region (clip)) + clip = _cairo_clip_copy_region (clip); + + if (! mask->is_clear) { + status = _cairo_surface_offset_paint (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + clip); + if (unlikely (status)) + goto error; + } + + status = _cairo_surface_offset_fill (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, fill_rule, tolerance, antialias, + clip); + if (unlikely (status)) + goto error; + + if (clip != extents->clip) { + status = _cairo_clip_combine_with_surface (extents->clip, mask, + extents->bounded.x, + extents->bounded.y); + if (unlikely (status)) + goto error; + } + + _cairo_pattern_init_for_surface (&pattern, mask); + cairo_matrix_init_translate (&pattern.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + pattern.base.extend = CAIRO_EXTEND_NONE; + if (extents->op == CAIRO_OPERATOR_SOURCE) { + status = _cairo_surface_mask (extents->surface, + CAIRO_OPERATOR_DEST_OUT, + &_cairo_pattern_white.base, + &pattern.base, + clip); + if ((status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_surface_mask (extents->surface, + CAIRO_OPERATOR_ADD, + &extents->source_pattern.base, + &pattern.base, + clip); + } + } else { + status = _cairo_surface_mask (extents->surface, + extents->op, + &extents->source_pattern.base, + &pattern.base, + clip); + } + _cairo_pattern_fini (&pattern.base); + +error: + if (clip != extents->clip) + _cairo_clip_destroy (clip); + cairo_surface_destroy (mask); + return status; +} + +static cairo_int_status_t +_cairo_shape_mask_compositor_glyphs (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + cairo_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status; + cairo_clip_t *clip; + + if (! extents->is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + mask = _cairo_surface_create_scratch (extents->surface, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (mask->status)) + return mask->status; + + clip = extents->clip; + if (! _cairo_clip_is_region (clip)) + clip = _cairo_clip_copy_region (clip); + + if (! mask->is_clear) { + status = _cairo_surface_offset_paint (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + clip); + if (unlikely (status)) + goto error; + } + + status = _cairo_surface_offset_glyphs (mask, + extents->bounded.x, + extents->bounded.y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + scaled_font, glyphs, num_glyphs, + clip); + if (unlikely (status)) + goto error; + + if (clip != extents->clip) { + status = _cairo_clip_combine_with_surface (extents->clip, mask, + extents->bounded.x, + extents->bounded.y); + if (unlikely (status)) + goto error; + } + + _cairo_pattern_init_for_surface (&pattern, mask); + cairo_matrix_init_translate (&pattern.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + pattern.base.extend = CAIRO_EXTEND_NONE; + if (extents->op == CAIRO_OPERATOR_SOURCE) { + status = _cairo_surface_mask (extents->surface, + CAIRO_OPERATOR_DEST_OUT, + &_cairo_pattern_white.base, + &pattern.base, + clip); + if ((status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_surface_mask (extents->surface, + CAIRO_OPERATOR_ADD, + &extents->source_pattern.base, + &pattern.base, + clip); + } + } else { + status = _cairo_surface_mask (extents->surface, + extents->op, + &extents->source_pattern.base, + &pattern.base, + clip); + } + _cairo_pattern_fini (&pattern.base); + +error: + if (clip != extents->clip) + _cairo_clip_destroy (clip); + cairo_surface_destroy (mask); + return status; +} + +void +_cairo_shape_mask_compositor_init (cairo_compositor_t *compositor, + const cairo_compositor_t *delegate) +{ + compositor->delegate = delegate; + + compositor->paint = NULL; + compositor->mask = NULL; + compositor->fill = _cairo_shape_mask_compositor_fill; + compositor->stroke = _cairo_shape_mask_compositor_stroke; + compositor->glyphs = _cairo_shape_mask_compositor_glyphs; +} diff --git a/gfx/cairo/cairo/src/cairo-slope.c b/gfx/cairo/cairo/src/cairo-slope.c index 827037f76d52..cc5f30cb0b39 100644 --- a/gfx/cairo/cairo/src/cairo-slope.c +++ b/gfx/cairo/cairo/src/cairo-slope.c @@ -89,9 +89,9 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b) */ if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) { if (a->dx > 0 || (a->dx == 0 && a->dy > 0)) - return +1; - else return -1; + else + return +1; } /* Finally, for identical slopes, we obviously return 0. */ diff --git a/gfx/cairo/cairo/src/cairo-spans-compositor-private.h b/gfx/cairo/cairo/src/cairo-spans-compositor-private.h new file mode 100644 index 000000000000..0babebd26b58 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-spans-compositor-private.h @@ -0,0 +1,111 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SPANS_COMPOSITOR_PRIVATE_H +#define CAIRO_SPANS_COMPOSITOR_PRIVATE_H + +#include "cairo-compositor-private.h" +#include "cairo-types-private.h" +#include "cairo-spans-private.h" + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_abstract_span_renderer { + cairo_span_renderer_t base; + char data[4096]; +} cairo_abstract_span_renderer_t; + +struct cairo_spans_compositor { + cairo_compositor_t base; + + unsigned int flags; +#define CAIRO_SPANS_COMPOSITOR_HAS_LERP 0x1 + + /* pixel-aligned fast paths */ + cairo_int_status_t (*fill_boxes) (void *surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes); + + cairo_int_status_t (*draw_image_boxes) (void *surface, + cairo_image_surface_t *image, + cairo_boxes_t *boxes, + int dx, int dy); + + cairo_int_status_t (*copy_boxes) (void *surface, + cairo_surface_t *src, + cairo_boxes_t *boxes, + const cairo_rectangle_int_t *extents, + int dx, int dy); + + cairo_surface_t * (*pattern_to_surface) (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_int_status_t (*composite_boxes) (void *surface, + cairo_operator_t op, + cairo_surface_t *source, + cairo_surface_t *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); + + /* general shape masks using a span renderer */ + cairo_int_status_t (*renderer_init) (cairo_abstract_span_renderer_t *renderer, + const cairo_composite_rectangles_t *extents, + cairo_antialias_t antialias, + cairo_bool_t needs_clip); + + void (*renderer_fini) (cairo_abstract_span_renderer_t *renderer, + cairo_int_status_t status); +}; + +cairo_private void +_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor, + const cairo_compositor_t *delegate); + +CAIRO_END_DECLS + +#endif /* CAIRO_SPANS_COMPOSITOR_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-spans-compositor.c b/gfx/cairo/cairo/src/cairo-spans-compositor.c new file mode 100644 index 000000000000..50c92b25c5d3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-spans-compositor.c @@ -0,0 +1,1201 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" +#include "cairo-clip-inline.h" +#include "cairo-clip-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-inline.h" +#include "cairo-region-private.h" +#include "cairo-recording-surface-inline.h" +#include "cairo-spans-compositor-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-observer-private.h" + +typedef struct { + cairo_polygon_t *polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_spans_info_t; + +static cairo_int_status_t +composite_polygon (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias); + +static cairo_int_status_t +composite_boxes (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes); + +static cairo_int_status_t +clip_and_composite_polygon (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias); +static cairo_surface_t * +get_clip_surface (const cairo_spans_compositor_t *compositor, + cairo_surface_t *dst, + const cairo_clip_t *clip, + const cairo_rectangle_int_t *extents) +{ + cairo_composite_rectangles_t composite; + cairo_surface_t *surface; + cairo_box_t box; + cairo_polygon_t polygon; + const cairo_clip_path_t *clip_path; + cairo_antialias_t antialias; + cairo_fill_rule_t fill_rule; + cairo_int_status_t status; + + assert (clip->path); + + surface = _cairo_surface_create_scratch (dst, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_TRANSPARENT); + + _cairo_box_from_rectangle (&box, extents); + _cairo_polygon_init (&polygon, &box, 1); + + clip_path = clip->path; + status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, + clip_path->tolerance, + &polygon); + if (unlikely (status)) + goto cleanup_polygon; + + polygon.num_limits = 0; + + antialias = clip_path->antialias; + fill_rule = clip_path->fill_rule; + + if (clip->boxes) { + cairo_polygon_t intersect; + cairo_boxes_t tmp; + + _cairo_boxes_init_for_array (&tmp, clip->boxes, clip->num_boxes); + status= _cairo_polygon_init_boxes (&intersect, &tmp); + if (unlikely (status)) + goto cleanup_polygon; + + status = _cairo_polygon_intersect (&polygon, fill_rule, + &intersect, CAIRO_FILL_RULE_WINDING); + _cairo_polygon_fini (&intersect); + + if (unlikely (status)) + goto cleanup_polygon; + + fill_rule = CAIRO_FILL_RULE_WINDING; + } + + polygon.limits = NULL; + polygon.num_limits = 0; + + clip_path = clip_path->prev; + while (clip_path) { + if (clip_path->antialias == antialias) { + cairo_polygon_t next; + + _cairo_polygon_init (&next, NULL, 0); + status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, + clip_path->tolerance, + &next); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = _cairo_polygon_intersect (&polygon, fill_rule, + &next, clip_path->fill_rule); + _cairo_polygon_fini (&next); + if (unlikely (status)) + goto cleanup_polygon; + + fill_rule = CAIRO_FILL_RULE_WINDING; + } + + clip_path = clip_path->prev; + } + + _cairo_polygon_translate (&polygon, -extents->x, -extents->y); + status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &polygon, + NULL); + if (unlikely (status)) + goto cleanup_polygon; + + status = composite_polygon (compositor, &composite, + &polygon, fill_rule, antialias); + _cairo_composite_rectangles_fini (&composite); + _cairo_polygon_fini (&polygon); + if (unlikely (status)) + goto error; + + _cairo_polygon_init (&polygon, &box, 1); + + clip_path = clip->path; + antialias = clip_path->antialias == CAIRO_ANTIALIAS_DEFAULT ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT; + clip_path = clip_path->prev; + while (clip_path) { + if (clip_path->antialias == antialias) { + if (polygon.num_edges == 0) { + status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, + clip_path->tolerance, + &polygon); + + fill_rule = clip_path->fill_rule; + polygon.limits = NULL; + polygon.num_limits = 0; + } else { + cairo_polygon_t next; + + _cairo_polygon_init (&next, NULL, 0); + status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, + clip_path->tolerance, + &next); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = _cairo_polygon_intersect (&polygon, fill_rule, + &next, clip_path->fill_rule); + _cairo_polygon_fini (&next); + fill_rule = CAIRO_FILL_RULE_WINDING; + } + if (unlikely (status)) + goto error; + } + + clip_path = clip_path->prev; + } + + if (polygon.num_edges) { + _cairo_polygon_translate (&polygon, -extents->x, -extents->y); + status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, + CAIRO_OPERATOR_IN, + &_cairo_pattern_white.base, + &polygon, + NULL); + if (unlikely (status)) + goto cleanup_polygon; + + status = composite_polygon (compositor, &composite, + &polygon, fill_rule, antialias); + _cairo_composite_rectangles_fini (&composite); + _cairo_polygon_fini (&polygon); + if (unlikely (status)) + goto error; + } + + return surface; + +cleanup_polygon: + _cairo_polygon_fini (&polygon); +error: + cairo_surface_destroy (surface); + return _cairo_int_surface_create_in_error (status); +} + +static cairo_int_status_t +fixup_unbounded_mask (const cairo_spans_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_composite_rectangles_t composite; + cairo_surface_t *clip; + cairo_int_status_t status; + + TRACE((stderr, "%s\n", __FUNCTION__)); + + clip = get_clip_surface (compositor, extents->surface, extents->clip, + &extents->unbounded); + if (unlikely (clip->status)) { + if ((cairo_int_status_t)clip->status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + + return clip->status; + } + + status = _cairo_composite_rectangles_init_for_boxes (&composite, + extents->surface, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + boxes, + NULL); + if (unlikely (status)) + goto cleanup_clip; + + _cairo_pattern_init_for_surface (&composite.mask_pattern.surface, clip); + composite.mask_pattern.base.filter = CAIRO_FILTER_NEAREST; + composite.mask_pattern.base.extend = CAIRO_EXTEND_NONE; + + status = composite_boxes (compositor, &composite, boxes); + + _cairo_pattern_fini (&composite.mask_pattern.base); + _cairo_composite_rectangles_fini (&composite); + +cleanup_clip: + cairo_surface_destroy (clip); + return status; +} + +static cairo_int_status_t +fixup_unbounded_polygon (const cairo_spans_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_polygon_t polygon, intersect; + cairo_composite_rectangles_t composite; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_int_status_t status; + + TRACE((stderr, "%s\n", __FUNCTION__)); + + /* Can we treat the clip as a regular clear-polygon and use it to fill? */ + status = _cairo_clip_get_polygon (extents->clip, &polygon, + &fill_rule, &antialias); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status= _cairo_polygon_init_boxes (&intersect, boxes); + if (unlikely (status)) + goto cleanup_polygon; + + status = _cairo_polygon_intersect (&polygon, fill_rule, + &intersect, CAIRO_FILL_RULE_WINDING); + _cairo_polygon_fini (&intersect); + + if (unlikely (status)) + goto cleanup_polygon; + + status = _cairo_composite_rectangles_init_for_polygon (&composite, + extents->surface, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + &polygon, + NULL); + if (unlikely (status)) + goto cleanup_polygon; + + status = composite_polygon (compositor, &composite, + &polygon, fill_rule, antialias); + + _cairo_composite_rectangles_fini (&composite); +cleanup_polygon: + _cairo_polygon_fini (&polygon); + + return status; +} + +static cairo_int_status_t +fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_boxes_t tmp, clear; + cairo_box_t box; + cairo_int_status_t status; + + assert (boxes->is_pixel_aligned); + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (extents->bounded.width == extents->unbounded.width && + extents->bounded.height == extents->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + + /* subtract the drawn boxes from the unbounded area */ + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (boxes->num_boxes) { + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + tmp.chunks.next = NULL; + if (unlikely (status)) + goto error; + } else { + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + + status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + /* If we have a clip polygon, we need to intersect with that as well */ + if (extents->clip->path) { + status = fixup_unbounded_polygon (compositor, extents, &clear); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = fixup_unbounded_mask (compositor, extents, &clear); + } else { + /* Otherwise just intersect with the clip boxes */ + if (extents->clip->num_boxes) { + _cairo_boxes_init_for_array (&tmp, + extents->clip->boxes, + extents->clip->num_boxes); + status = _cairo_boxes_intersect (&clear, &tmp, &clear); + if (unlikely (status)) + goto error; + } + + if (clear.is_pixel_aligned) { + status = compositor->fill_boxes (extents->surface, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + } else { + cairo_composite_rectangles_t composite; + + status = _cairo_composite_rectangles_init_for_boxes (&composite, + extents->surface, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + &clear, + NULL); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = composite_boxes (compositor, &composite, &clear); + _cairo_composite_rectangles_fini (&composite); + } + } + } + +error: + _cairo_boxes_fini (&clear); + return status; +} + +static cairo_surface_t * +unwrap_source (const cairo_pattern_t *pattern) +{ + cairo_rectangle_int_t limit; + + return _cairo_pattern_get_source ((cairo_surface_pattern_t *)pattern, + &limit); +} + +static cairo_bool_t +is_recording_pattern (const cairo_pattern_t *pattern) +{ + cairo_surface_t *surface; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return FALSE; + + surface = ((const cairo_surface_pattern_t *) pattern)->surface; + return _cairo_surface_is_recording (surface); +} + +static cairo_bool_t +recording_pattern_contains_sample (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *sample) +{ + cairo_recording_surface_t *surface; + + if (! is_recording_pattern (pattern)) + return FALSE; + + if (pattern->extend == CAIRO_EXTEND_NONE) + return TRUE; + + surface = (cairo_recording_surface_t *) unwrap_source (pattern); + if (surface->unbounded) + return TRUE; + + return _cairo_rectangle_contains_rectangle (&surface->extents, sample); +} + +static cairo_bool_t +op_reduces_to_source (const cairo_composite_rectangles_t *extents, + cairo_bool_t no_mask) +{ + if (extents->op == CAIRO_OPERATOR_SOURCE) + return TRUE; + + if (extents->surface->is_clear) + return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD; + + if (no_mask && extents->op == CAIRO_OPERATOR_OVER) + return _cairo_pattern_is_opaque (&extents->source_pattern.base, + &extents->source_sample_area); + + return FALSE; +} + +static cairo_status_t +upload_boxes (const cairo_spans_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + const cairo_surface_pattern_t *source = &extents->source_pattern.surface; + cairo_surface_t *src; + cairo_rectangle_int_t limit; + cairo_int_status_t status; + int tx, ty; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + src = _cairo_pattern_get_source(source, &limit); + if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->base.matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that the data is entirely within the image */ + if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || + extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + tx += limit.x; + ty += limit.y; + + if (src->type == CAIRO_SURFACE_TYPE_IMAGE) + status = compositor->draw_image_boxes (dst, + (cairo_image_surface_t *)src, + boxes, tx, ty); + else + status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, + tx, ty); + + return status; +} + +static cairo_bool_t +_clip_is_region (const cairo_clip_t *clip) +{ + int i; + + if (clip->is_region) + return TRUE; + + if (clip->path) + return FALSE; + + for (i = 0; i < clip->num_boxes; i++) { + const cairo_box_t *b = &clip->boxes[i]; + if (!_cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y)) + return FALSE; + } + + return TRUE; +} + +static cairo_int_status_t +composite_aligned_boxes (const cairo_spans_compositor_t *compositor, + const cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + cairo_operator_t op = extents->op; + const cairo_pattern_t *source = &extents->source_pattern.base; + cairo_int_status_t status; + cairo_bool_t need_clip_mask = ! _clip_is_region (extents->clip); + cairo_bool_t op_is_source; + cairo_bool_t no_mask; + cairo_bool_t inplace; + + TRACE ((stderr, "%s: need_clip_mask=%d, is-bounded=%d\n", + __FUNCTION__, need_clip_mask, extents->is_bounded)); + if (need_clip_mask && ! extents->is_bounded) { + TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && + CAIRO_COLOR_IS_OPAQUE (&extents->mask_pattern.solid.color); + op_is_source = op_reduces_to_source (extents, no_mask); + inplace = ! need_clip_mask && op_is_source && no_mask; + + TRACE ((stderr, "%s: op-is-source=%d [op=%d], no-mask=%d, inplace=%d\n", + __FUNCTION__, op_is_source, op, no_mask, inplace)); + + if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) { + /* SOURCE with a mask is actually a LERP in cairo semantics */ + if ((compositor->flags & CAIRO_SPANS_COMPOSITOR_HAS_LERP) == 0) { + TRACE ((stderr, "%s: unsupported lerp\n", __FUNCTION__)); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + /* Are we just copying a recording surface? */ + if (inplace && + recording_pattern_contains_sample (&extents->source_pattern.base, + &extents->source_sample_area)) + { + cairo_clip_t *recording_clip; + const cairo_pattern_t *source = &extents->source_pattern.base; + const cairo_matrix_t *m; + cairo_matrix_t matrix; + + /* XXX could also do tiling repeat modes... */ + + /* first clear the area about to be overwritten */ + if (! dst->is_clear) { + status = compositor->fill_boxes (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + boxes); + if (unlikely (status)) + return status; + + dst->is_clear = TRUE; + } + + m = &source->matrix; + if (_cairo_surface_has_device_transform (dst)) { + cairo_matrix_multiply (&matrix, + &source->matrix, + &dst->device_transform); + m = &matrix; + } + + recording_clip = _cairo_clip_from_boxes (boxes); + status = _cairo_recording_surface_replay_with_clip (unwrap_source (source), + m, dst, recording_clip); + _cairo_clip_destroy (recording_clip); + + return status; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (! need_clip_mask && no_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_color_t *color; + + color = &((cairo_solid_pattern_t *) source)->color; + if (op_is_source) + op = CAIRO_OPERATOR_SOURCE; + status = compositor->fill_boxes (dst, op, color, boxes); + } else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = upload_boxes (compositor, extents, boxes); + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_surface_t *src; + cairo_surface_t *mask = NULL; + int src_x, src_y; + int mask_x = 0, mask_y = 0; + + /* All typical cases will have been resolved before now... */ + if (need_clip_mask) { + mask = get_clip_surface (compositor, dst, extents->clip, + &extents->bounded); + if (unlikely (mask->status)) + return mask->status; + + mask_x = -extents->bounded.x; + mask_y = -extents->bounded.y; + } + + /* XXX but this is still ugly */ + if (! no_mask) { + src = compositor->pattern_to_surface (dst, + &extents->mask_pattern.base, + TRUE, + &extents->bounded, + &extents->mask_sample_area, + &src_x, &src_y); + if (unlikely (src->status)) { + cairo_surface_destroy (mask); + return src->status; + } + + if (mask != NULL) { + status = compositor->composite_boxes (mask, CAIRO_OPERATOR_IN, + src, NULL, + src_x, src_y, + 0, 0, + mask_x, mask_y, + boxes, &extents->bounded); + + cairo_surface_destroy (src); + } else { + mask = src; + mask_x = src_x; + mask_y = src_y; + } + } + + src = compositor->pattern_to_surface (dst, source, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (likely (src->status == CAIRO_STATUS_SUCCESS)) { + status = compositor->composite_boxes (dst, op, src, mask, + src_x, src_y, + mask_x, mask_y, + 0, 0, + boxes, &extents->bounded); + cairo_surface_destroy (src); + } else + status = src->status; + + cairo_surface_destroy (mask); + } + + if (status == CAIRO_INT_STATUS_SUCCESS && ! extents->is_bounded) + status = fixup_unbounded_boxes (compositor, extents, boxes); + + return status; +} + +static cairo_bool_t +composite_needs_clip (const cairo_composite_rectangles_t *composite, + const cairo_box_t *extents) +{ + return !_cairo_clip_contains_box (composite->clip, extents); +} + +static cairo_int_status_t +composite_boxes (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_abstract_span_renderer_t renderer; + cairo_rectangular_scan_converter_t converter; + const struct _cairo_boxes_chunk *chunk; + cairo_int_status_t status; + cairo_box_t box; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_box_from_rectangle (&box, &extents->unbounded); + if (composite_needs_clip (extents, &box)) { + TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + _cairo_rectangular_scan_converter_init (&converter, &extents->unbounded); + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto cleanup_converter; + } + } + + status = compositor->renderer_init (&renderer, extents, + CAIRO_ANTIALIAS_DEFAULT, FALSE); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = converter.base.generate (&converter.base, &renderer.base); + compositor->renderer_fini (&renderer, status); + +cleanup_converter: + converter.base.destroy (&converter.base); + return status; +} + +static cairo_int_status_t +composite_polygon (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) +{ + cairo_abstract_span_renderer_t renderer; + cairo_scan_converter_t *converter; + cairo_bool_t needs_clip; + cairo_int_status_t status; + + if (extents->is_bounded) + needs_clip = extents->clip->path != NULL; + else + needs_clip = !_clip_is_region (extents->clip) || extents->clip->num_boxes > 1; + TRACE ((stderr, "%s - needs_clip=%d\n", __FUNCTION__, needs_clip)); + if (needs_clip) { + TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); + return CAIRO_INT_STATUS_UNSUPPORTED; + converter = _cairo_clip_tor_scan_converter_create (extents->clip, + polygon, + fill_rule, antialias); + } else { + const cairo_rectangle_int_t *r = &extents->unbounded; + + if (antialias == CAIRO_ANTIALIAS_FAST) { + converter = _cairo_tor22_scan_converter_create (r->x, r->y, + r->x + r->width, + r->y + r->height, + fill_rule, antialias); + status = _cairo_tor22_scan_converter_add_polygon (converter, polygon); + } else if (antialias == CAIRO_ANTIALIAS_NONE) { + converter = _cairo_mono_scan_converter_create (r->x, r->y, + r->x + r->width, + r->y + r->height, + fill_rule); + status = _cairo_mono_scan_converter_add_polygon (converter, polygon); + } else { + converter = _cairo_tor_scan_converter_create (r->x, r->y, + r->x + r->width, + r->y + r->height, + fill_rule, antialias); + status = _cairo_tor_scan_converter_add_polygon (converter, polygon); + } + } + if (unlikely (status)) + goto cleanup_converter; + + status = compositor->renderer_init (&renderer, extents, + antialias, needs_clip); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = converter->generate (converter, &renderer.base); + compositor->renderer_fini (&renderer, status); + +cleanup_converter: + converter->destroy (converter); + return status; +} + +static cairo_int_status_t +trim_extents_to_boxes (cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_box_t box; + + _cairo_boxes_extents (boxes, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_int_status_t +trim_extents_to_polygon (cairo_composite_rectangles_t *extents, + cairo_polygon_t *polygon) +{ + return _cairo_composite_rectangles_intersect_mask_extents (extents, + &polygon->extents); +} + +static cairo_int_status_t +clip_and_composite_boxes (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_int_status_t status; + cairo_polygon_t polygon; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + status = trim_extents_to_boxes (extents, boxes); + if (unlikely (status)) + return status; + + if (boxes->num_boxes == 0) { + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + return fixup_unbounded_boxes (compositor, extents, boxes); + } + + /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ + if (extents->clip->path != NULL && extents->is_bounded) { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_clip_t *clip; + + clip = _cairo_clip_copy (extents->clip); + clip = _cairo_clip_intersect_boxes (clip, boxes); + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &antialias); + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *saved_clip = extents->clip; + extents->clip = clip; + + status = clip_and_composite_polygon (compositor, extents, &polygon, + fill_rule, antialias); + + clip = extents->clip; + extents->clip = saved_clip; + + _cairo_polygon_fini (&polygon); + } + _cairo_clip_destroy (clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + if (boxes->is_pixel_aligned) { + status = composite_aligned_boxes (compositor, extents, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + status = composite_boxes (compositor, extents, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_polygon_init_boxes (&polygon, boxes); + if (unlikely (status)) + return status; + + status = composite_polygon (compositor, extents, &polygon, + CAIRO_FILL_RULE_WINDING, + CAIRO_ANTIALIAS_DEFAULT); + _cairo_polygon_fini (&polygon); + + return status; +} + +static cairo_int_status_t +clip_and_composite_polygon (const cairo_spans_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) +{ + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* XXX simply uses polygon limits.point extemities, tessellation? */ + status = trim_extents_to_polygon (extents, polygon); + if (unlikely (status)) + return status; + + if (_cairo_polygon_is_empty (polygon)) { + cairo_boxes_t boxes; + + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + _cairo_boxes_init (&boxes); + extents->bounded.width = extents->bounded.height = 0; + return fixup_unbounded_boxes (compositor, extents, &boxes); + } + + if (extents->is_bounded && extents->clip->path) { + cairo_polygon_t clipper; + cairo_antialias_t clip_antialias; + cairo_fill_rule_t clip_fill_rule; + + TRACE((stderr, "%s - combining shape with clip polygon\n", + __FUNCTION__)); + + status = _cairo_clip_get_polygon (extents->clip, + &clipper, + &clip_fill_rule, + &clip_antialias); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *old_clip; + + if (clip_antialias == antialias) { + status = _cairo_polygon_intersect (polygon, fill_rule, + &clipper, clip_fill_rule); + _cairo_polygon_fini (&clipper); + if (unlikely (status)) + return status; + + old_clip = extents->clip; + extents->clip = _cairo_clip_copy_region (extents->clip); + _cairo_clip_destroy (old_clip); + + status = trim_extents_to_polygon (extents, polygon); + if (unlikely (status)) + return status; + + fill_rule = CAIRO_FILL_RULE_WINDING; + } else { + _cairo_polygon_fini (&clipper); + } + } + } + + return composite_polygon (compositor, extents, + polygon, fill_rule, antialias); +} + +/* high-level compositor interface */ + +static cairo_int_status_t +_cairo_spans_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; + cairo_boxes_t boxes; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_clip_steal_boxes (extents->clip, &boxes); + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_clip_unsteal_boxes (extents->clip, &boxes); + + return status; +} + +static cairo_int_status_t +_cairo_spans_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; + cairo_int_status_t status; + cairo_boxes_t boxes; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_clip_steal_boxes (extents->clip, &boxes); + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_clip_unsteal_boxes (extents->clip, &boxes); + + return status; +} + +static cairo_int_status_t +_cairo_spans_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + TRACE_ (_cairo_debug_print_path (stderr, path)); + TRACE_ (_cairo_debug_print_clip (stderr, extents->clip)); + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask)) + _cairo_boxes_limit (&boxes, + extents->clip->boxes, + extents->clip->num_boxes); + + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + cairo_box_t limits; + cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING; + + if (! _cairo_rectangle_contains_rectangle (&extents->unbounded, + &extents->mask)) + { + if (extents->clip->num_boxes == 1) { + _cairo_polygon_init (&polygon, extents->clip->boxes, 1); + } else { + _cairo_box_from_rectangle (&limits, &extents->unbounded); + _cairo_polygon_init (&polygon, &limits, 1); + } + } + else + { + _cairo_polygon_init (&polygon, NULL, 0); + } + status = _cairo_path_fixed_stroke_to_polygon (path, + style, + ctm, ctm_inverse, + tolerance, + &polygon); + TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); + polygon.num_limits = 0; + + if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) { + status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, + extents->clip->boxes, + extents->clip->num_boxes); + } + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *saved_clip = extents->clip; + + if (extents->is_bounded) { + extents->clip = _cairo_clip_copy_path (extents->clip); + extents->clip = _cairo_clip_intersect_box(extents->clip, + &polygon.extents); + } + + status = clip_and_composite_polygon (compositor, extents, &polygon, + fill_rule, antialias); + + if (extents->is_bounded) { + _cairo_clip_destroy (extents->clip); + extents->clip = saved_clip; + } + } + _cairo_polygon_fini (&polygon); + } + + return status; +} + +static cairo_int_status_t +_cairo_spans_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; + cairo_int_status_t status; + + TRACE((stderr, "%s op=%d, antialias=%d\n", __FUNCTION__, extents->op, antialias)); + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + TRACE((stderr, "%s - rectilinear\n", __FUNCTION__)); + + _cairo_boxes_init (&boxes); + if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask)) + _cairo_boxes_limit (&boxes, + extents->clip->boxes, + extents->clip->num_boxes); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_boxes_fini (&boxes); + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + cairo_box_t limits; + + TRACE((stderr, "%s - polygon\n", __FUNCTION__)); + + if (! _cairo_rectangle_contains_rectangle (&extents->unbounded, + &extents->mask)) + { + TRACE((stderr, "%s - clipping to bounds\n", __FUNCTION__)); + if (extents->clip->num_boxes == 1) { + _cairo_polygon_init (&polygon, extents->clip->boxes, 1); + } else { + _cairo_box_from_rectangle (&limits, &extents->unbounded); + _cairo_polygon_init (&polygon, &limits, 1); + } + } + else + { + _cairo_polygon_init (&polygon, NULL, 0); + } + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); + polygon.num_limits = 0; + + if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) { + TRACE((stderr, "%s - polygon intersect with %d clip boxes\n", + __FUNCTION__, extents->clip->num_boxes)); + status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, + extents->clip->boxes, + extents->clip->num_boxes); + } + TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *saved_clip = extents->clip; + + if (extents->is_bounded) { + TRACE((stderr, "%s - polygon discard clip boxes\n", + __FUNCTION__)); + extents->clip = _cairo_clip_copy_path (extents->clip); + extents->clip = _cairo_clip_intersect_box(extents->clip, + &polygon.extents); + } + + status = clip_and_composite_polygon (compositor, extents, &polygon, + fill_rule, antialias); + + if (extents->is_bounded) { + _cairo_clip_destroy (extents->clip); + extents->clip = saved_clip; + } + } + _cairo_polygon_fini (&polygon); + + TRACE((stderr, "%s - polygon status=%d\n", __FUNCTION__, status)); + } + + return status; +} + +void +_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor, + const cairo_compositor_t *delegate) +{ + compositor->base.delegate = delegate; + + compositor->base.paint = _cairo_spans_compositor_paint; + compositor->base.mask = _cairo_spans_compositor_mask; + compositor->base.fill = _cairo_spans_compositor_fill; + compositor->base.stroke = _cairo_spans_compositor_stroke; + compositor->base.glyphs = NULL; +} diff --git a/gfx/cairo/cairo/src/cairo-spans-private.h b/gfx/cairo/cairo/src/cairo-spans-private.h index 00a4df868471..653183fb1c8e 100644 --- a/gfx/cairo/cairo/src/cairo-spans-private.h +++ b/gfx/cairo/cairo/src/cairo-spans-private.h @@ -36,11 +36,9 @@ /* A structure representing an open-ended horizontal span of constant * pixel coverage. */ typedef struct _cairo_half_open_span { - /* The inclusive x-coordinate of the start of the span. */ - int x; - - /* The pixel coverage for the pixels to the right. */ - int coverage; + int32_t x; /* The inclusive x-coordinate of the start of the span. */ + uint8_t coverage; /* The pixel coverage for the pixels to the right. */ + uint8_t inverse; /* between regular mask and clip */ } cairo_half_open_span_t; /* Span renderer interface. Instances of renderers are provided by @@ -55,7 +53,7 @@ struct _cairo_span_renderer { /* Render the spans on row y of the destination by whatever compositing * method is required. */ - cairo_warn cairo_status_t + cairo_status_t (*render_rows) (void *abstract_renderer, int y, int height, const cairo_half_open_span_t *coverages, @@ -73,17 +71,6 @@ struct _cairo_scan_converter { /* Destroy this scan converter. */ cairo_destroy_func_t destroy; - /* Add a single edge to the converter. */ - cairo_status_t (*add_edge) (void *abstract_converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir); - - /* Add a polygon (set of edges) to the converter. */ - cairo_status_t (*add_polygon) (void *abstract_converter, - const cairo_polygon_t *polygon); - /* Generates coverage spans for rows for the added edges and calls * the renderer function for each row. After generating spans the * only valid thing to do with the converter is to destroy it. */ @@ -101,13 +88,43 @@ _cairo_tor_scan_converter_create (int xmin, int ymin, int xmax, int ymax, - cairo_fill_rule_t fill_rule); + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias); +cairo_private cairo_status_t +_cairo_tor_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon); + +cairo_private cairo_scan_converter_t * +_cairo_tor22_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias); +cairo_private cairo_status_t +_cairo_tor22_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon); + +cairo_private cairo_scan_converter_t * +_cairo_mono_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule); +cairo_private cairo_status_t +_cairo_mono_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon); + +cairo_private cairo_scan_converter_t * +_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip, + cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias); typedef struct _cairo_rectangular_scan_converter { cairo_scan_converter_t base; - int xmin, xmax; - int ymin, ymax; + cairo_box_t extents; struct _cairo_rectangular_scan_converter_chunk { struct _cairo_rectangular_scan_converter_chunk *next; @@ -151,6 +168,10 @@ _cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, const cairo_box_t *extents, cairo_fill_rule_t fill_rule); +cairo_private cairo_status_t +_cairo_botor_scan_converter_add_polygon (cairo_botor_scan_converter_t *converter, + const cairo_polygon_t *polygon); + /* cairo-spans.c: */ cairo_private cairo_scan_converter_t * diff --git a/gfx/cairo/cairo/src/cairo-spans.c b/gfx/cairo/cairo/src/cairo-spans.c index a187b899807f..59452c0ba69f 100644 --- a/gfx/cairo/cairo/src/cairo-spans.c +++ b/gfx/cairo/cairo/src/cairo-spans.c @@ -27,62 +27,10 @@ #include "cairoint.h" #include "cairo-composite-rectangles-private.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" #include "cairo-fixed-private.h" - -static cairo_scan_converter_t * -_create_scan_converter (cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects) -{ - if (antialias == CAIRO_ANTIALIAS_NONE) { - ASSERT_NOT_REACHED; - return NULL; - } - - return _cairo_tor_scan_converter_create (rects->bounded.x, - rects->bounded.y, - rects->bounded.x + rects->bounded.width, - rects->bounded.y + rects->bounded.height, - fill_rule); -} - -/* XXX Add me to the compositor interface. Ok, first create the compositor - * interface, and then add this with associated fallback! - */ -cairo_status_t -_cairo_surface_composite_polygon (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_fill_rule_t fill_rule, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_polygon_t *polygon, - cairo_region_t *clip_region) -{ - cairo_span_renderer_t *renderer; - cairo_scan_converter_t *converter; - cairo_status_t status; - - converter = _create_scan_converter (fill_rule, antialias, rects); - status = converter->add_polygon (converter, polygon); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - - renderer = _cairo_surface_create_span_renderer (op, pattern, surface, - antialias, rects, - clip_region); - status = converter->generate (converter, renderer); - if (unlikely (status)) - goto CLEANUP_RENDERER; - - status = renderer->finish (renderer); - - CLEANUP_RENDERER: - renderer->destroy (renderer); - CLEANUP_CONVERTER: - converter->destroy (converter); - return status; -} +#include "cairo-types-private.h" static void _cairo_nil_destroy (void *abstract) @@ -90,31 +38,6 @@ _cairo_nil_destroy (void *abstract) (void) abstract; } -static cairo_status_t -_cairo_nil_scan_converter_add_polygon (void *abstract_converter, - const cairo_polygon_t *polygon) -{ - (void) abstract_converter; - (void) polygon; - return _cairo_scan_converter_status (abstract_converter); -} - -static cairo_status_t -_cairo_nil_scan_converter_add_edge (void *abstract_converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir) -{ - (void) abstract_converter; - (void) p1; - (void) p2; - (void) top; - (void) bottom; - (void) dir; - return _cairo_scan_converter_status (abstract_converter); -} - static cairo_status_t _cairo_nil_scan_converter_generate (void *abstract_converter, cairo_span_renderer_t *renderer) @@ -139,8 +62,6 @@ _cairo_scan_converter_set_error (void *abstract_converter, if (error == CAIRO_STATUS_SUCCESS) ASSERT_NOT_REACHED; if (converter->status == CAIRO_STATUS_SUCCESS) { - converter->add_polygon = _cairo_nil_scan_converter_add_polygon; - converter->add_edge = _cairo_nil_scan_converter_add_edge; converter->generate = _cairo_nil_scan_converter_generate; converter->status = error; } @@ -204,6 +125,13 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL; + case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: default: break; } @@ -314,6 +242,13 @@ _cairo_span_renderer_create_in_error (cairo_status_t status) case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL; case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL; case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL; + case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: RETURN_NIL; + case CAIRO_STATUS_PNG_ERROR: RETURN_NIL; + case CAIRO_STATUS_FREETYPE_ERROR: RETURN_NIL; + case CAIRO_STATUS_WIN32_GDI_ERROR: RETURN_NIL; + case CAIRO_STATUS_TAG_ERROR: RETURN_NIL; default: break; } diff --git a/gfx/cairo/cairo/src/cairo-spline.c b/gfx/cairo/cairo/src/cairo-spline.c index ca2e2dc64c1a..44634faec859 100644 --- a/gfx/cairo/cairo/src/cairo-spline.c +++ b/gfx/cairo/cairo/src/cairo-spline.c @@ -36,8 +36,50 @@ #include "cairoint.h" +#include "cairo-box-inline.h" #include "cairo-slope-private.h" +cairo_bool_t +_cairo_spline_intersects (const cairo_point_t *a, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d, + const cairo_box_t *box) +{ + cairo_box_t bounds; + + if (_cairo_box_contains_point (box, a) || + _cairo_box_contains_point (box, b) || + _cairo_box_contains_point (box, c) || + _cairo_box_contains_point (box, d)) + { + return TRUE; + } + + bounds.p2 = bounds.p1 = *a; + _cairo_box_add_point (&bounds, b); + _cairo_box_add_point (&bounds, c); + _cairo_box_add_point (&bounds, d); + + if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || + bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) + { + return FALSE; + } + +#if 0 /* worth refining? */ + bounds.p2 = bounds.p1 = *a; + _cairo_box_add_curve_to (&bounds, b, c, d); + if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || + bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) + { + return FALSE; + } +#endif + + return TRUE; +} + cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, cairo_spline_add_point_func_t add_point_func, @@ -45,6 +87,10 @@ _cairo_spline_init (cairo_spline_t *spline, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { + /* If both tangents are zero, this is just a straight line */ + if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y) + return FALSE; + spline->add_point_func = add_point_func; spline->closure = closure; @@ -67,22 +113,29 @@ _cairo_spline_init (cairo_spline_t *spline, else if (b->x != d->x || b->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); else - _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d); + return FALSE; /* just treat this as a straight-line from a -> d */ + + /* XXX if the initial, final and vector are all equal, this is just a line */ return TRUE; } static cairo_status_t -_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point) +_cairo_spline_add_point (cairo_spline_t *spline, + const cairo_point_t *point, + const cairo_point_t *knot) { cairo_point_t *prev; + cairo_slope_t slope; prev = &spline->last_point; if (prev->x == point->x && prev->y == point->y) return CAIRO_STATUS_SUCCESS; + _cairo_slope_init (&slope, point, knot); + spline->last_point = *point; - return spline->add_point_func (spline->closure, point); + return spline->add_point_func (spline->closure, point, &slope); } static void @@ -184,24 +237,23 @@ _cairo_spline_error_squared (const cairo_spline_knots_t *knots) } static cairo_status_t -_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result) +_cairo_spline_decompose_into (cairo_spline_knots_t *s1, + double tolerance_squared, + cairo_spline_t *result) { cairo_spline_knots_t s2; cairo_status_t status; - if (_cairo_spline_error_squared (s1) < tolerance_squared) { - return _cairo_spline_add_point (result, &s1->a); - } + if (_cairo_spline_error_squared (s1) < tolerance_squared) + return _cairo_spline_add_point (result, &s1->a, &s1->b); _de_casteljau (s1, &s2); status = _cairo_spline_decompose_into (s1, tolerance_squared, result); - if (unlikely (status)) { + if (unlikely (status)) return status; - } - status = _cairo_spline_decompose_into (&s2, tolerance_squared, result); - return status; + return _cairo_spline_decompose_into (&s2, tolerance_squared, result); } cairo_status_t @@ -216,7 +268,8 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance) if (unlikely (status)) return status; - return _cairo_spline_add_point (spline, &spline->knots.d); + return spline->add_point_func (spline->closure, + &spline->knots.d, &spline->final_slope); } /* Note: this function is only good for computing bounds in device space. */ @@ -328,7 +381,7 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, c = -y0 + y1; FIND_EXTREMES (a, b, c); - status = add_point_func (closure, p0); + status = add_point_func (closure, p0, NULL); if (unlikely (status)) return status; @@ -362,10 +415,10 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, p.x = _cairo_fixed_from_double (x); p.y = _cairo_fixed_from_double (y); - status = add_point_func (closure, &p); + status = add_point_func (closure, &p, NULL); if (unlikely (status)) return status; } - return add_point_func (closure, p3); + return add_point_func (closure, p3, NULL); } diff --git a/gfx/cairo/cairo/src/cairo-skia.h b/gfx/cairo/cairo/src/cairo-stroke-dash-private.h similarity index 62% rename from gfx/cairo/cairo/src/cairo-skia.h rename to gfx/cairo/cairo/src/cairo-stroke-dash-private.h index f62823522478..75c000cd73a7 100644 --- a/gfx/cairo/cairo/src/cairo-skia.h +++ b/gfx/cairo/cairo/src/cairo-stroke-dash-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 @@ -32,53 +33,38 @@ * * Contributor(s): * Carl D. Worth + * Chris Wilson */ -#ifndef CAIRO_SKIA_H -#define CAIRO_SKIA_H +#ifndef CAIRO_STROKE_DASH_PRIVATE_H +#define CAIRO_STROKE_DASH_PRIVATE_H -#include "cairo.h" - -#if CAIRO_HAS_SKIA_SURFACE +#include "cairoint.h" CAIRO_BEGIN_DECLS -cairo_public cairo_surface_t * -cairo_skia_surface_create (cairo_format_t format, - int width, - int height); +typedef struct _cairo_stroker_dash { + cairo_bool_t dashed; + unsigned int dash_index; + cairo_bool_t dash_on; + cairo_bool_t dash_starts_on; + double dash_remain; -cairo_public cairo_surface_t * -cairo_skia_surface_create_for_data (unsigned char *data, - cairo_format_t format, - int width, - int height, - int stride); + double dash_offset; + const double *dashes; + unsigned int num_dashes; +} cairo_stroker_dash_t; -cairo_public unsigned char * -cairo_skia_surface_get_data (cairo_surface_t *surface); +cairo_private void +_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, + const cairo_stroke_style_t *style); -cairo_public cairo_format_t -cairo_skia_surface_get_format (cairo_surface_t *surface); +cairo_private void +_cairo_stroker_dash_start (cairo_stroker_dash_t *dash); -cairo_public int -cairo_skia_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_height (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_stride (cairo_surface_t *surface); - -cairo_public cairo_surface_t * -cairo_skia_surface_get_image (cairo_surface_t *surface); +cairo_private void +_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step); CAIRO_END_DECLS -#else - -# error Cairo was not compiled with support for the Skia backend - -#endif - -#endif +#endif /* CAIRO_STROKE_DASH_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-stroke-dash.c b/gfx/cairo/cairo/src/cairo-stroke-dash.c new file mode 100644 index 000000000000..9494010f5613 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-stroke-dash.c @@ -0,0 +1,96 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-stroke-dash-private.h" + +void +_cairo_stroker_dash_start (cairo_stroker_dash_t *dash) +{ + double offset; + cairo_bool_t on = TRUE; + unsigned int i = 0; + + if (! dash->dashed) + return; + + offset = dash->dash_offset; + + /* We stop searching for a starting point as soon as the + offset reaches zero. Otherwise when an initial dash + segment shrinks to zero it will be skipped over. */ + while (offset > 0.0 && offset >= dash->dashes[i]) { + offset -= dash->dashes[i]; + on = !on; + if (++i == dash->num_dashes) + i = 0; + } + + dash->dash_index = i; + dash->dash_on = dash->dash_starts_on = on; + dash->dash_remain = dash->dashes[i] - offset; +} + +void +_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step) +{ + dash->dash_remain -= step; + if (dash->dash_remain < CAIRO_FIXED_ERROR_DOUBLE) { + if (++dash->dash_index == dash->num_dashes) + dash->dash_index = 0; + + dash->dash_on = ! dash->dash_on; + dash->dash_remain += dash->dashes[dash->dash_index]; + } +} + +void +_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, + const cairo_stroke_style_t *style) +{ + dash->dashed = style->dash != NULL; + if (! dash->dashed) + return; + + dash->dashes = style->dash; + dash->num_dashes = style->num_dashes; + dash->dash_offset = style->dash_offset; + + _cairo_stroker_dash_start (dash); +} diff --git a/gfx/cairo/cairo/src/cairo-stroke-style.c b/gfx/cairo/cairo/src/cairo-stroke-style.c index 1513d1f358ed..9c373c33327a 100644 --- a/gfx/cairo/cairo/src/cairo-stroke-style.c +++ b/gfx/cairo/cairo/src/cairo-stroke-style.c @@ -86,13 +86,12 @@ _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, void _cairo_stroke_style_fini (cairo_stroke_style_t *style) { - if (style->dash) { - free (style->dash); - style->dash = NULL; - } + free (style->dash); + style->dash = NULL; + style->num_dashes = 0; - VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); } /* @@ -102,6 +101,7 @@ _cairo_stroke_style_fini (cairo_stroke_style_t *style) */ void _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, + const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy) { @@ -111,6 +111,7 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, style_expansion = M_SQRT1_2; if (style->line_join == CAIRO_LINE_JOIN_MITER && + ! path->stroke_is_rectilinear && style_expansion < M_SQRT2 * style->miter_limit) { style_expansion = M_SQRT2 * style->miter_limit; @@ -118,10 +119,53 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, style_expansion *= style->line_width; - *dx = style_expansion * hypot (ctm->xx, ctm->xy); - *dy = style_expansion * hypot (ctm->yy, ctm->yx); + if (_cairo_matrix_has_unity_scale (ctm)) { + *dx = *dy = style_expansion; + } else { + *dx = style_expansion * hypot (ctm->xx, ctm->xy); + *dy = style_expansion * hypot (ctm->yy, ctm->yx); + } } +void +_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style, + const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm, + double *dx, double *dy) +{ + double style_expansion = 0.5 * style->line_width; + if (_cairo_matrix_has_unity_scale (ctm)) { + *dx = *dy = style_expansion; + } else { + *dx = style_expansion * hypot (ctm->xx, ctm->xy); + *dy = style_expansion * hypot (ctm->yy, ctm->yx); + } +} + +void +_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style, + const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm, + double *dx, double *dy) +{ + double style_expansion = 0.5; + + if (style->line_join == CAIRO_LINE_JOIN_MITER && + ! path->stroke_is_rectilinear && + style_expansion < M_SQRT2 * style->miter_limit) + { + style_expansion = M_SQRT2 * style->miter_limit; + } + + style_expansion *= style->line_width; + + if (_cairo_matrix_has_unity_scale (ctm)) { + *dx = *dy = style_expansion; + } else { + *dx = style_expansion * hypot (ctm->xx, ctm->xy); + *dy = style_expansion * hypot (ctm->yy, ctm->yx); + } +} /* * Computes the period of a dashed stroke style. * Returns 0 for non-dashed styles. @@ -191,7 +235,7 @@ _cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) } else { /* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus * their coverage is approximated based on the area covered by the caps of adjacent on dases. */ - for (i = 0; i < style->num_dashes; i+=2) + for (i = 0; i + 1 < style->num_dashes; i += 2) stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width); } diff --git a/gfx/cairo/cairo/src/cairo-supported-features.h b/gfx/cairo/cairo/src/cairo-supported-features.h deleted file mode 100644 index aacbab78181d..000000000000 --- a/gfx/cairo/cairo/src/cairo-supported-features.h +++ /dev/null @@ -1,25 +0,0 @@ -/* Generated by configure. Do not edit. */ -#ifndef CAIRO_SUPPORTED_FEATURES_H -#define CAIRO_SUPPORTED_FEATURES_H - -/* This is a dummy header, to trick gtk-doc only */ - -#define CAIRO_HAS_XLIB_SURFACE 1 -#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1 -#define CAIRO_HAS_QUARTZ_SURFACE 1 -#define CAIRO_HAS_QUARTZ_FONT 1 -#define CAIRO_HAS_WIN32_SURFACE 1 -#define CAIRO_HAS_WIN32_FONT 1 -#define CAIRO_HAS_PNG_FUNCTIONS 1 -#define CAIRO_HAS_EGL_FUNCTIONS 1 -#define CAIRO_HAS_GLX_FUNCTIONS 1 -#define CAIRO_HAS_FT_FONT 1 -#define CAIRO_HAS_FC_FONT 1 -#define CAIRO_HAS_PS_SURFACE 1 -#define CAIRO_HAS_PDF_SURFACE 1 -#define CAIRO_HAS_SVG_SURFACE 1 -#define CAIRO_HAS_IMAGE_SURFACE 1 -#define CAIRO_HAS_META_SURFACE 1 -#define CAIRO_HAS_USER_FONT 1 - -#endif diff --git a/gfx/cairo/cairo/src/cairo-surface-backend-private.h b/gfx/cairo/cairo/src/cairo-surface-backend-private.h new file mode 100644 index 000000000000..bcda9aed155b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-backend-private.h @@ -0,0 +1,233 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Carl D. Worth + */ + +#ifndef CAIRO_SURFACE_BACKEND_PRIVATE_H +#define CAIRO_SURFACE_BACKEND_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-error-private.h" + +CAIRO_BEGIN_DECLS + +struct _cairo_surface_backend { + cairo_surface_type_t type; + + cairo_warn cairo_status_t + (*finish) (void *surface); + + cairo_t * + (*create_context) (void *surface); + + cairo_surface_t * + (*create_similar) (void *surface, + cairo_content_t content, + int width, + int height); + cairo_surface_t * + (*create_similar_image) (void *surface, + cairo_format_t format, + int width, + int height); + + cairo_image_surface_t * + (*map_to_image) (void *surface, + const cairo_rectangle_int_t *extents); + cairo_int_status_t + (*unmap_image) (void *surface, + cairo_image_surface_t *image); + + cairo_surface_t * + (*source) (void *abstract_surface, + cairo_rectangle_int_t *extents); + + cairo_warn cairo_status_t + (*acquire_source_image) (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra); + + cairo_warn void + (*release_source_image) (void *abstract_surface, + cairo_image_surface_t *image_out, + void *image_extra); + + cairo_surface_t * + (*snapshot) (void *surface); + + cairo_warn cairo_int_status_t + (*copy_page) (void *surface); + + cairo_warn cairo_int_status_t + (*show_page) (void *surface); + + /* Get the extents of the current surface. For many surface types + * this will be as simple as { x=0, y=0, width=surface->width, + * height=surface->height}. + * + * If this function is not implemented, or if it returns + * FALSE the surface is considered to be + * boundless and infinite bounds are used for it. + */ + cairo_bool_t + (*get_extents) (void *surface, + cairo_rectangle_int_t *extents); + + void + (*get_font_options) (void *surface, + cairo_font_options_t *options); + + cairo_warn cairo_status_t + (*flush) (void *surface, + unsigned flags); + + cairo_warn cairo_status_t + (*mark_dirty_rectangle) (void *surface, + int x, + int y, + int width, + int height); + + cairo_warn cairo_int_status_t + (*paint) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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); + + cairo_warn cairo_int_status_t + (*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); + + cairo_warn cairo_int_status_t + (*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); + + cairo_warn cairo_int_status_t + (*fill_stroke) (void *surface, + 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_warn cairo_int_status_t + (*show_glyphs) (void *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_bool_t + (*has_show_text_glyphs) (void *surface); + + cairo_warn cairo_int_status_t + (*show_text_glyphs) (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip); + + const char ** + (*get_supported_mime_types) (void *surface); + + cairo_warn cairo_int_status_t + (*tag) (void *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip); + +}; + +cairo_private cairo_status_t +_cairo_surface_default_acquire_source_image (void *surface, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_surface_default_release_source_image (void *surface, + cairo_image_surface_t *image, + void *image_extra); + +cairo_private cairo_surface_t * +_cairo_surface_default_source (void *surface, + cairo_rectangle_int_t *extents); + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_BACKEND_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-clipper-private.h b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h index b9ca3cb1cb55..e5b00af7c66a 100644 --- a/gfx/cairo/cairo/src/cairo-surface-clipper-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h @@ -51,14 +51,13 @@ typedef cairo_status_t double, cairo_antialias_t); struct _cairo_surface_clipper { - cairo_clip_t clip; - cairo_bool_t is_clipped; + cairo_clip_t *clip; cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path; }; cairo_private cairo_status_t _cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private void _cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, diff --git a/gfx/cairo/cairo/src/cairo-surface-clipper.c b/gfx/cairo/cairo/src/cairo-surface-clipper.c index 9487300477d4..5309362c68f3 100644 --- a/gfx/cairo/cairo/src/cairo-surface-clipper.c +++ b/gfx/cairo/cairo/src/cairo-surface-clipper.c @@ -35,20 +35,83 @@ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-surface-clipper-private.h" /* A collection of routines to facilitate vector surface clipping */ +/* XXX Eliminate repeated paths and nested clips */ + static cairo_status_t -_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper, - cairo_clip_path_t *clip_path) +_cairo_path_fixed_add_box (cairo_path_fixed_t *path, + const cairo_box_t *box) { cairo_status_t status; - if (clip_path->prev != NULL) { + status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_close_path (path); +} + +static cairo_status_t +_cairo_surface_clipper_intersect_clip_boxes (cairo_surface_clipper_t *clipper, + const cairo_clip_t *clip) +{ + cairo_path_fixed_t path; + cairo_status_t status; + int i; + + if (clip->num_boxes == 0) + return CAIRO_STATUS_SUCCESS; + + /* Reconstruct the path for the clip boxes. + * XXX maybe a new clipper callback? + */ + + _cairo_path_fixed_init (&path); + for (i = 0; i < clip->num_boxes; i++) { + status = _cairo_path_fixed_add_box (&path, &clip->boxes[i]); + if (unlikely (status)) { + _cairo_path_fixed_fini (&path); + return status; + } + } + + status = clipper->intersect_clip_path (clipper, &path, + CAIRO_FILL_RULE_WINDING, + 0., + CAIRO_ANTIALIAS_DEFAULT); + _cairo_path_fixed_fini (&path); + + return status; +} + +static cairo_status_t +_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper, + cairo_clip_path_t *clip_path, + cairo_clip_path_t *end) +{ + cairo_status_t status; + + if (clip_path->prev != end) { status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, - clip_path->prev); + clip_path->prev, + end); if (unlikely (status)) return status; } @@ -62,57 +125,56 @@ _cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *c cairo_status_t _cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_bool_t clear; + cairo_bool_t incremental = FALSE; - /* XXX as we cache a reference to the path, and compare every time, - * we may in future need to install a notification if the clip->path - * is every modified (e.g. cairo_clip_translate). - */ - - if (clip == NULL && clipper->clip.path == NULL) + if (_cairo_clip_equal (clip, clipper->clip)) return CAIRO_STATUS_SUCCESS; - if (clip != NULL && clipper->clip.path != NULL && - _cairo_clip_equal (clip, &clipper->clip)) - { - return CAIRO_STATUS_SUCCESS; - } - /* all clipped out state should never propagate this far */ - assert (clip == NULL || clip->path != NULL); + assert (!_cairo_clip_is_all_clipped (clip)); - /* Check whether this clip is a continuation of the previous. - * If not, we have to remove the current clip and rebuild. - */ - clear = clip == NULL || clip->path->prev != clipper->clip.path; + /* XXX Is this an incremental clip? */ + if (clipper->clip && clip && + clip->num_boxes == clipper->clip->num_boxes && + memcmp (clip->boxes, clipper->clip->boxes, + sizeof (cairo_box_t) * clip->num_boxes) == 0) + { + cairo_clip_path_t *clip_path = clip->path; + while (clip_path != NULL && clip_path != clipper->clip->path) + clip_path = clip_path->prev; - _cairo_clip_reset (&clipper->clip); - _cairo_clip_init_copy (&clipper->clip, clip); - - if (clear) { - clipper->is_clipped = FALSE; - status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0); - if (unlikely (status)) - return status; - - if (clip != NULL && clip->path != NULL) { - status = - _cairo_surface_clipper_intersect_clip_path_recursive (clipper, - clip->path); - clipper->is_clipped = TRUE; + if (clip_path) { + incremental = TRUE; + status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip->path, + clipper->clip->path); } - } else { - cairo_clip_path_t *path = clip->path; + } - clipper->is_clipped = TRUE; - status = clipper->intersect_clip_path (clipper, - &path->path, - path->fill_rule, - path->tolerance, - path->antialias); + _cairo_clip_destroy (clipper->clip); + clipper->clip = _cairo_clip_copy (clip); + + if (incremental) + return status; + + status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0); + if (unlikely (status)) + return status; + + if (clip == NULL) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_intersect_clip_boxes (clipper, clip); + if (unlikely (status)) + return status; + + if (clip->path != NULL) { + status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip->path, + NULL); } return status; @@ -122,14 +184,13 @@ void _cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, cairo_surface_clipper_intersect_clip_path_func_t func) { - _cairo_clip_init (&clipper->clip); - clipper->is_clipped = FALSE; + clipper->clip = NULL; clipper->intersect_clip_path = func; } void _cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper) { - _cairo_clip_reset (&clipper->clip); - clipper->is_clipped = FALSE; + _cairo_clip_destroy (clipper->clip); + clipper->clip = NULL; } diff --git a/gfx/cairo/cairo/src/cairo-surface-fallback-private.h b/gfx/cairo/cairo/src/cairo-surface-fallback-private.h index e993de62eb1b..ecf7b0edf2bf 100644 --- a/gfx/cairo/cairo/src/cairo-surface-fallback-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-fallback-private.h @@ -3,6 +3,7 @@ * * Copyright © 2002 University of Southern California * Copyright © 2005 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 @@ -34,6 +35,8 @@ * * Contributor(s): * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson */ #ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H @@ -41,99 +44,52 @@ #include "cairoint.h" -cairo_private cairo_status_t -_cairo_surface_fallback_paint (cairo_surface_t *surface, +CAIRO_BEGIN_DECLS + +cairo_private cairo_int_status_t +_cairo_surface_fallback_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip); + const cairo_clip_t *clip); -cairo_private cairo_status_t -_cairo_surface_fallback_mask (cairo_surface_t *surface, +cairo_private cairo_int_status_t +_cairo_surface_fallback_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip); + const cairo_clip_t *clip); -cairo_private cairo_status_t -_cairo_surface_fallback_stroke (cairo_surface_t *surface, - cairo_operator_t op, +cairo_private cairo_int_status_t +_cairo_surface_fallback_stroke (void *abstract_surface, + cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, + 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_clip_t *clip); + const cairo_clip_t *clip); -cairo_private cairo_status_t -_cairo_surface_fallback_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); +cairo_private cairo_int_status_t +_cairo_surface_fallback_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_private cairo_status_t -_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); +cairo_private cairo_int_status_t +_cairo_surface_fallback_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_private cairo_surface_t * -_cairo_surface_fallback_snapshot (cairo_surface_t *surface); +CAIRO_END_DECLS -cairo_private cairo_status_t -_cairo_surface_fallback_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - -cairo_private cairo_status_t -_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *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_private cairo_status_t -_cairo_surface_fallback_clone_similar (cairo_surface_t *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); - -#endif +#endif /* CAIRO_SURFACE_FALLBACK_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-fallback.c b/gfx/cairo/cairo/src/cairo-surface-fallback.c index 51893ee65881..a0af1596921e 100644 --- a/gfx/cairo/cairo/src/cairo-surface-fallback.c +++ b/gfx/cairo/cairo/src/cairo-surface-fallback.c @@ -3,6 +3,7 @@ * * Copyright © 2002 University of Southern California * Copyright © 2005 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 @@ -40,1599 +41,75 @@ #include "cairoint.h" -#include "cairo-boxes-private.h" -#include "cairo-clip-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-region-private.h" -#include "cairo-spans-private.h" +#include "cairo-compositor-private.h" #include "cairo-surface-fallback-private.h" -typedef struct { - cairo_surface_t *dst; - cairo_rectangle_int_t extents; - cairo_image_surface_t *image; - cairo_rectangle_int_t image_rect; - void *image_extra; -} fallback_state_t; - -/** - * _fallback_init: - * - * Acquire destination image surface needed for an image-based - * fallback. - * - * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not - * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all - * went well, or some error status otherwise. - **/ -static cairo_int_status_t -_fallback_init (fallback_state_t *state, - cairo_surface_t *dst, - int x, - int y, - int width, - int height) -{ - cairo_status_t status; - - state->extents.x = x; - state->extents.y = y; - state->extents.width = width; - state->extents.height = height; - - state->dst = dst; - - status = _cairo_surface_acquire_dest_image (dst, &state->extents, - &state->image, &state->image_rect, - &state->image_extra); - if (unlikely (status)) - return status; - - - /* XXX: This NULL value tucked away in state->image is a rather - * ugly interface. Cleaner would be to push the - * CAIRO_INT_STATUS_NOTHING_TO_DO value down into - * _cairo_surface_acquire_dest_image and its backend - * counterparts. */ - assert (state->image != NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_fallback_fini (fallback_state_t *state) -{ - _cairo_surface_release_dest_image (state->dst, &state->extents, - state->image, &state->image_rect, - state->image_extra); -} - -typedef cairo_status_t -(*cairo_draw_func_t) (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region); - -static cairo_status_t -_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, - cairo_clip_t *clip, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *mask; - cairo_region_t *clip_region = NULL, *fallback_region = NULL; - cairo_status_t status; - cairo_bool_t clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with - * a mask (as called via _cairo_surface_mask) triggers assertion failures. - */ - mask = _cairo_surface_create_similar_solid (dst, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height, - CAIRO_COLOR_TRANSPARENT, - TRUE); - if (unlikely (mask->status)) - return mask->status; - - if (clip_region && (extents->x || extents->y)) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto CLEANUP_SURFACE; - - cairo_region_translate (fallback_region, - -extents->x, - -extents->y); - clip_region = fallback_region; - } - - status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, mask, - extents->x, extents->y, - extents, - clip_region); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - if (clip_surface) - status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); - - _cairo_pattern_init_for_surface (mask_pattern, mask); - - CLEANUP_SURFACE: - if (fallback_region) - cairo_region_destroy (fallback_region); - cairo_surface_destroy (mask); - - return status; -} - -/* Handles compositing with a clip surface when the operator allows - * us to combine the clip with the mask - */ -static cairo_status_t -_clip_and_composite_with_mask (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_status_t status; - - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_surface_composite (op, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - - _cairo_pattern_fini (&mask_pattern.base); - } - - return status; -} - -/* Handles compositing with a clip surface when we have to do the operation - * in two pieces and combine them together. - */ -static cairo_status_t -_clip_and_composite_combine (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *intermediate; - cairo_surface_pattern_t pattern; - cairo_surface_pattern_t clip_pattern; - cairo_surface_t *clip_surface; - int clip_x, clip_y; - cairo_status_t status; - - /* We'd be better off here creating a surface identical in format - * to dst, but we have no way of getting that information. Instead - * we ask the backend to create a similar surface of identical content, - * in the belief that the backend will do something useful - like use - * an identical format. For example, the xlib backend will endeavor to - * use a compatible depth to enable core protocol routines. - */ - intermediate = - _cairo_surface_create_similar_scratch (dst, dst->content, - extents->width, - extents->height); - if (intermediate == NULL) { - intermediate = - _cairo_image_surface_create_with_content (dst->content, - extents->width, - extents->width); - } - if (unlikely (intermediate->status)) - return intermediate->status; - - /* Initialize the intermediate surface from the destination surface */ - _cairo_pattern_init_for_surface (&pattern, dst); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL, intermediate, - extents->x, extents->y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - status = (*draw_func) (draw_closure, op, - src, intermediate, - extents->x, extents->y, - extents, - NULL); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - assert (clip->path != NULL); - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - goto CLEANUP_SURFACE; - - _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); - - /* Combine that with the clip */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, - &clip_pattern.base, NULL, intermediate, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Punch the clip out of the destination */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &clip_pattern.base, NULL, dst, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Now add the two results together */ - _cairo_pattern_init_for_surface (&pattern, intermediate); - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - - CLEANUP_CLIP: - _cairo_pattern_fini (&clip_pattern.base); - CLEANUP_SURFACE: - cairo_surface_destroy (intermediate); - - return status; -} - -/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's - * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) - */ -static cairo_status_t -_clip_and_composite_source (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - } - - /* Create a surface that is mask IN clip */ - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (unlikely (status)) - return status; - - /* Compute dest' = dest OUT (mask IN clip) */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &mask_pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - if (unlikely (status)) - goto CLEANUP_MASK_PATTERN; - - /* Now compute (src IN (mask IN clip)) ADD dest' */ - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - CLEANUP_MASK_PATTERN: - _cairo_pattern_fini (&mask_pattern.base); - return status; -} - -static int -_cairo_rectangle_empty (const cairo_rectangle_int_t *rect) -{ - return rect->width == 0 || rect->height == 0; -} - -/** - * _clip_and_composite: - * @clip: a #cairo_clip_t - * @op: the operator to draw with - * @src: source pattern - * @draw_func: function that can be called to draw with the mask onto a surface. - * @draw_closure: data to pass to @draw_func. - * @dst: destination surface - * @extents: rectangle holding a bounding box for the operation; this - * rectangle will be used as the size for the temporary - * surface. - * - * When there is a surface clip, we typically need to create an intermediate - * surface. This function handles the logic of creating a temporary surface - * drawing to it, then compositing the result onto the target surface. - * - * @draw_func is to called to draw the mask; it will be called no more - * than once. - * - * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. - **/ -static cairo_status_t -_clip_and_composite (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - - if (_cairo_rectangle_empty (extents)) - /* Nothing to do */ - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (op == CAIRO_OPERATOR_SOURCE) { - status = _clip_and_composite_source (clip, - src, - draw_func, draw_closure, - dst, extents); - } else { - cairo_bool_t clip_surface = FALSE; - cairo_region_t *clip_region = NULL; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (clip_surface) { - if (_cairo_operator_bounded_by_mask (op)) { - status = _clip_and_composite_with_mask (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } else { - status = _clip_and_composite_combine (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } - } else { - status = draw_func (draw_closure, op, - src, dst, - 0, 0, - extents, - clip_region); - } - } - - return status; -} - -/* Composites a region representing a set of trapezoids. - */ -static cairo_status_t -_composite_trap_region (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_surface_pattern_t mask_pattern; - cairo_pattern_t *mask = NULL; - int mask_x = 0, mask_y =0; - - if (clip != NULL) { - cairo_surface_t *clip_surface = NULL; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); - mask_x = extents->x - clip_x; - mask_y = extents->y - clip_y; - mask = &mask_pattern.base; - } - - status = _cairo_surface_composite (op, src, mask, dst, - extents->x, extents->y, - mask_x, mask_y, - extents->x, extents->y, - extents->width, extents->height, - trap_region); - - if (mask != NULL) - _cairo_pattern_fini (mask); - - return status; -} - -typedef struct { - cairo_traps_t *traps; - cairo_antialias_t antialias; -} cairo_composite_traps_info_t; - -static cairo_status_t -_composite_traps_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_traps_info_t *info = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (dst_x != 0 || dst_y != 0) - _cairo_traps_translate (info->traps, - dst_x, - dst_y); - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - status = _cairo_surface_composite_trapezoids (op, - src, dst, info->antialias, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - info->traps->traps, - info->traps->num_traps, - clip_region); - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -enum { - HAS_CLEAR_REGION = 0x1, -}; - -static cairo_status_t -_clip_and_composite_region (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_region_t clear_region; - unsigned int has_region = 0; - cairo_status_t status; - - if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { - /* If we optimize drawing with an unbounded operator to - * _cairo_surface_fill_rectangles() or to drawing with a - * clip region, then we have an additional region to clear. - */ - _cairo_region_init_rectangle (&clear_region, extents); - status = cairo_region_subtract (&clear_region, trap_region); - if (unlikely (status)) - return status; - - if (! cairo_region_is_empty (&clear_region)) - has_region |= HAS_CLEAR_REGION; - } - - if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && - clip == NULL) - { - const cairo_color_t *color; - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, trap_region); - } else { - /* For a simple rectangle, we can just use composite(), for more - * rectangles, we have to set a clip region. The cost of rasterizing - * trapezoids is pretty high for most backends currently, so it's - * worthwhile even if a region is needed. - * - * If we have a clip surface, we set it as the mask; this only works - * for bounded operators other than SOURCE; for unbounded operators, - * clip and mask cannot be interchanged. For SOURCE, the operator - * as implemented by the backends is different in its handling - * of the mask then what we want. - * - * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has - * more than rectangle and the destination doesn't support clip - * regions. In that case, we fall through. - */ - status = _composite_trap_region (clip, src, op, dst, - trap_region, extents); - } - - if (has_region & HAS_CLEAR_REGION) { - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_surface_fill_region (dst, - CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - &clear_region); - } - _cairo_region_fini (&clear_region); - } - - return status; -} - -/* avoid using region code to re-validate boxes */ -static cairo_status_t -_fill_rectangles (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - const cairo_color_t *color; - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_status_t status; - int i; - - if (! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: convert clip region to geometric boxes? */ - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: fallback for the region_subtract() operation */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->has_intersections) { - if (traps->is_rectangular) { - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - } else { - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - } - if (unlikely (status)) - return status; - } - - for (i = 0; i < traps->num_traps; i++) { - if (! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (traps->num_traps, - sizeof (cairo_rectangle_int_t)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part (traps->traps[i].top); - int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); - - rects[i].x = x1; - rects[i].y = y1; - rects[i].width = x2 - x1; - rects[i].height = y2 - y1; - } - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); - - if (rects != stack_rects) - free (rects); - - return status; -} - -/* fast-path for very common composite of a single rectangle */ -static cairo_status_t -_composite_rectangle (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - cairo_rectangle_int_t rect; - - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_fixed_is_integer (traps->traps[0].top) || - ! _cairo_fixed_is_integer (traps->traps[0].bottom) || - ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); - rect.y = _cairo_fixed_integer_part (traps->traps[0].top); - rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; - rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; - - return _cairo_surface_composite (op, src, NULL, dst, - rect.x, rect.y, - 0, 0, - rect.x, rect.y, - rect.width, rect.height, - NULL); -} - -/* Warning: This call modifies the coordinates of traps */ -static cairo_status_t -_clip_and_composite_trapezoids (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_composite_traps_info_t traps_info; - cairo_region_t *clip_region = NULL; - cairo_bool_t clip_surface = FALSE; - cairo_status_t status; - - if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* Use a fast path if the trapezoids consist of a simple region, - * but we can only do this if we do not have a clip surface, or can - * substitute the mask with the clip. - */ - if (! clip_surface || - (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) - { - cairo_region_t *trap_region = NULL; - - if (_cairo_operator_bounded_by_source (op)) { - status = _fill_rectangles (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _composite_rectangle (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_traps_extract_region (traps, &trap_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - - if (trap_region != NULL) { - status = cairo_region_intersect_rectangle (trap_region, extents); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - - if (clip_region != NULL) { - status = cairo_region_intersect (trap_region, clip_region); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - } - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t trap_extents; - - cairo_region_get_extents (trap_region, &trap_extents); - if (! _cairo_rectangle_intersect (extents, &trap_extents)) { - cairo_region_destroy (trap_region); - return CAIRO_STATUS_SUCCESS; - } - } - - status = _clip_and_composite_region (src, op, dst, - trap_region, - clip_surface ? clip : NULL, - extents); - cairo_region_destroy (trap_region); - - if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) - return status; - } - } - - /* No fast path, exclude self-intersections and clip trapezoids. */ - if (traps->has_intersections) { - if (traps->is_rectangular) - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - else if (traps->is_rectilinear) - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - else - status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - return status; - } - - /* Otherwise render the trapezoids to a mask and composite in the usual - * fashion. - */ - traps_info.traps = traps; - traps_info.antialias = antialias; - - return _clip_and_composite (clip, op, src, - _composite_traps_draw_func, - &traps_info, dst, extents); -} - -cairo_status_t -_cairo_surface_fallback_paint (cairo_surface_t *surface, +cairo_int_status_t +_cairo_surface_fallback_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_clip_path_t *clip_path = clip ? clip->path : NULL; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_boxes_t boxes; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - cairo_traps_t traps; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - /* If the clip cannot be reduced to a set of boxes, we will need to - * use a clipmask. Paint is special as it is the only operation that - * does not implicitly use a mask, so we may be able to reduce this - * operation to a fill... - */ - if (clip != NULL && clip_path->prev == NULL && - _cairo_operator_bounded_by_mask (op)) - { - return _cairo_surface_fill (surface, op, source, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - } - - /* meh, surface-fallback is dying anyway... */ - _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); - status = _cairo_traps_init_boxes (&traps, &boxes); - if (unlikely (status)) - goto CLEANUP_BOXES; - - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, CAIRO_ANTIALIAS_DEFAULT, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - _cairo_traps_fini (&traps); - -CLEANUP_BOXES: - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; + return _cairo_compositor_paint (&_cairo_fallback_compositor, + surface, op, source, clip); } -static cairo_status_t -_cairo_surface_mask_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_pattern_t *mask = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - if (src) { - status = _cairo_surface_composite (op, - src, mask, dst, - extents->x, extents->y, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } else { - status = _cairo_surface_composite (op, - mask, NULL, dst, - extents->x, extents->y, - 0, 0, /* unused */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_mask (cairo_surface_t *surface, +cairo_int_status_t +_cairo_surface_fallback_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - } - - return _clip_and_composite (clip, op, source, - _cairo_surface_mask_draw_func, - (void *) mask, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); + return _cairo_compositor_mask (&_cairo_fallback_compositor, + surface, op, source, mask, clip); } -cairo_status_t -_cairo_surface_fallback_stroke (cairo_surface_t *surface, +cairo_int_status_t +_cairo_surface_fallback_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, + 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_clip_t *clip) + const cairo_clip_t *clip) { - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, stroke_style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - if (path->is_rectilinear) { - status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, - stroke_style, - ctm, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_stroke_to_polygon (path, - stroke_style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; + return _cairo_compositor_stroke (&_cairo_fallback_compositor, + surface, op, source, path, + style, ctm,ctm_inverse, + tolerance, antialias, clip); } -cairo_status_t -_cairo_surface_fallback_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) +cairo_int_status_t +_cairo_surface_fallback_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) { - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_bool_t is_rectilinear; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - if (path->is_empty_fill) - goto DO_TRAPS; - - is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); - if (is_rectilinear) { - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - if (is_rectilinear) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (unlikely (_cairo_status_is_error (status))) - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - fill_rule); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; + return _cairo_compositor_fill (&_cairo_fallback_compositor, + surface, op, source, path, + fill_rule, tolerance, antialias, + clip); } -typedef struct { - cairo_scaled_font_t *font; - cairo_glyph_t *glyphs; - int num_glyphs; -} cairo_show_glyphs_info_t; - -static cairo_status_t -_cairo_surface_old_show_glyphs_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) +cairo_int_status_t +_cairo_surface_fallback_glyphs (void *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_show_glyphs_info_t *glyph_info = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - /* Modifying the glyph array is fine because we know that this function - * will be called only once, and we've already made a copy of the - * glyphs in the wrapper. - */ - if (dst_x != 0 || dst_y != 0) { - int i; - - for (i = 0; i < glyph_info->num_glyphs; ++i) { - ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x; - ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y; - } - } - - status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, - dst, - extents->x, extents->y, - extents->x - dst_x, - extents->y - dst_y, - extents->width, - extents->height, - glyph_info->glyphs, - glyph_info->num_glyphs, - clip_region); - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_scaled_font_show_glyphs (glyph_info->font, - op, - src, dst, - extents->x, extents->y, - extents->x - dst_x, - extents->y - dst_y, - extents->width, extents->height, - glyph_info->glyphs, - glyph_info->num_glyphs, - clip_region); - } - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) -{ - cairo_show_glyphs_info_t glyph_info; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - NULL); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_rectangle (clip, &extents.mask)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - } - - glyph_info.font = scaled_font; - glyph_info.glyphs = glyphs; - glyph_info.num_glyphs = num_glyphs; - - return _clip_and_composite (clip, op, source, - _cairo_surface_old_show_glyphs_draw_func, - &glyph_info, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); -} - -cairo_surface_t * -_cairo_surface_fallback_snapshot (cairo_surface_t *surface) -{ - cairo_surface_t *snapshot; - cairo_status_t status; - cairo_format_t format; - cairo_surface_pattern_t pattern; - cairo_image_surface_t *image; - void *image_extra; - - status = _cairo_surface_acquire_source_image (surface, - &image, &image_extra); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); - - format = image->format; - if (format == CAIRO_FORMAT_INVALID) { - /* Non-standard images formats can be generated when retrieving - * images from unusual xservers, for example. - */ - format = _cairo_format_from_content (image->base.content); - } - snapshot = cairo_image_surface_create (format, - image->width, - image->height); - if (cairo_surface_status (snapshot)) { - _cairo_surface_release_source_image (surface, image, image_extra); - return snapshot; - } - - _cairo_pattern_init_for_surface (&pattern, &image->base); - status = _cairo_surface_paint (snapshot, - CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - _cairo_surface_release_source_image (surface, image, image_extra); - if (unlikely (status)) { - cairo_surface_destroy (snapshot); - return _cairo_surface_create_in_error (status); - } - - return snapshot; -} - -cairo_status_t -_cairo_surface_fallback_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - fallback_state_t state; - cairo_region_t *fallback_region = NULL; - cairo_status_t status; - - status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (unlikely (status)) - return status; - - /* We know this will never fail with the image backend; but - * instead of calling into it directly, we call - * _cairo_surface_composite so that we get the correct device - * offset handling. - */ - - if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto FAIL; - - cairo_region_translate (fallback_region, - -state.image_rect.x, - -state.image_rect.y); - clip_region = fallback_region; - } - - status = _cairo_surface_composite (op, src, mask, - &state.image->base, - src_x, src_y, mask_x, mask_y, - dst_x - state.image_rect.x, - dst_y - state.image_rect.y, - width, height, - clip_region); - FAIL: - if (fallback_region != NULL) - cairo_region_destroy (fallback_region); - _fallback_fini (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - fallback_state_t state; - cairo_rectangle_int_t *offset_rects = NULL; - cairo_status_t status; - int x1, y1, x2, y2; - int i; - - assert (surface->snapshot_of == NULL); - - if (num_rects <= 0) - return CAIRO_STATUS_SUCCESS; - - /* Compute the bounds of the rectangles, so that we know what area of the - * destination surface to fetch - */ - x1 = rects[0].x; - y1 = rects[0].y; - x2 = rects[0].x + rects[0].width; - y2 = rects[0].y + rects[0].height; - - for (i = 1; i < num_rects; i++) { - if (rects[i].x < x1) - x1 = rects[i].x; - if (rects[i].y < y1) - y1 = rects[i].y; - - if ((int) (rects[i].x + rects[i].width) > x2) - x2 = rects[i].x + rects[i].width; - if ((int) (rects[i].y + rects[i].height) > y2) - y2 = rects[i].y + rects[i].height; - } - - status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); - if (unlikely (status)) - return status; - - /* If the fetched image isn't at 0,0, we need to offset the rectangles */ - - if (state.image_rect.x != 0 || state.image_rect.y != 0) { - offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); - if (unlikely (offset_rects == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto DONE; - } - - for (i = 0; i < num_rects; i++) { - offset_rects[i].x = rects[i].x - state.image_rect.x; - offset_rects[i].y = rects[i].y - state.image_rect.y; - offset_rects[i].width = rects[i].width; - offset_rects[i].height = rects[i].height; - } - - rects = offset_rects; - } - - status = _cairo_surface_fill_rectangles (&state.image->base, - op, color, - rects, num_rects); - - free (offset_rects); - - DONE: - _fallback_fini (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *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) -{ - fallback_state_t state; - cairo_region_t *fallback_region = NULL; - cairo_trapezoid_t *offset_traps = NULL; - cairo_status_t status; - - status = _fallback_init (&state, dst, dst_x, dst_y, width, height); - if (unlikely (status)) - return status; - - /* If the destination image isn't at 0,0, we need to offset the trapezoids */ - - if (state.image_rect.x != 0 || state.image_rect.y != 0) { - offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t)); - if (offset_traps == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps, - - state.image_rect.x, - state.image_rect.y, - 1.0, 1.0); - traps = offset_traps; - - /* similarly we need to adjust the region */ - if (clip_region != NULL) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto FAIL; - - cairo_region_translate (fallback_region, - -state.image_rect.x, - -state.image_rect.y); - clip_region = fallback_region; - } - } - - status = _cairo_surface_composite_trapezoids (op, pattern, - &state.image->base, - antialias, - src_x, src_y, - dst_x - state.image_rect.x, - dst_y - state.image_rect.y, - width, height, - traps, num_traps, - clip_region); - FAIL: - if (offset_traps != NULL) - free (offset_traps); - - if (fallback_region != NULL) - cairo_region_destroy (fallback_region); - - _fallback_fini (&state); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_clone_similar (cairo_surface_t *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_surface_t *new_surface; - cairo_surface_pattern_t pattern; - cairo_status_t status; - - new_surface = _cairo_surface_create_similar_scratch (surface, - src->content, - width, height); - if (new_surface == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (new_surface->status)) - return new_surface->status; - - /* We have to copy these here, so that the coordinate spaces are correct */ - new_surface->device_transform = src->device_transform; - new_surface->device_transform_inverse = src->device_transform_inverse; - - _cairo_pattern_init_for_surface (&pattern, src); - cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y); - pattern.base.filter = CAIRO_FILTER_NEAREST; - - status = _cairo_surface_paint (new_surface, - CAIRO_OPERATOR_SOURCE, - &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - if (unlikely (status)) { - cairo_surface_destroy (new_surface); - return status; - } - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = new_surface; - return CAIRO_STATUS_SUCCESS; + return _cairo_compositor_glyphs (&_cairo_fallback_compositor, + surface, op, source, + glyphs, num_glyphs, scaled_font, + clip); } diff --git a/gfx/cairo/cairo/src/cairo-xcb-xrender.h b/gfx/cairo/cairo/src/cairo-surface-inline.h similarity index 67% rename from gfx/cairo/cairo/src/cairo-xcb-xrender.h rename to gfx/cairo/cairo/src/cairo-surface-inline.h index 09c609738b97..85fbc9192bd9 100644 --- a/gfx/cairo/cairo/src/cairo-xcb-xrender.h +++ b/gfx/cairo/cairo/src/cairo-surface-inline.h @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -12,7 +13,7 @@ * * 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 + * 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 * @@ -34,30 +35,26 @@ * Carl D. Worth */ -#ifndef CAIRO_XCB_XRENDER_H -#define CAIRO_XCB_XRENDER_H +#ifndef CAIRO_SURFACE_INLINE_H +#define CAIRO_SURFACE_INLINE_H -#include "cairo.h" +#include "cairo-surface-private.h" -#if CAIRO_HAS_XCB_SURFACE +static inline cairo_status_t +__cairo_surface_flush (cairo_surface_t *surface, unsigned flags) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + if (surface->backend->flush) + status = surface->backend->flush (surface, flags); + return status; +} -#include -#include +static inline cairo_surface_t * +_cairo_surface_reference (cairo_surface_t *surface) +{ + if (!CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + _cairo_reference_count_inc (&surface->ref_count); + return surface; +} -CAIRO_BEGIN_DECLS - -cairo_public cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c, - xcb_drawable_t drawable, - xcb_screen_t *screen, - xcb_render_pictforminfo_t *format, - int width, - int height); - -CAIRO_END_DECLS - -#else /* CAIRO_HAS_XCB_SURFACE */ -# error Cairo was not compiled with support for the xcb backend -#endif /* CAIRO_HAS_XCB_SURFACE */ - -#endif /* CAIRO_XCB_XRENDER_H */ +#endif /* CAIRO_SURFACE_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-observer-inline.h b/gfx/cairo/cairo/src/cairo-surface-observer-inline.h new file mode 100644 index 000000000000..07b94770d4be --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-observer-inline.h @@ -0,0 +1,59 @@ +/* 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 Intel Corporation. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_OBSERVER_INLINE_H +#define CAIRO_SURFACE_OBSERVER_INLINE_H + +#include "cairo-surface-observer-private.h" + +static inline cairo_surface_t * +_cairo_surface_observer_get_target (cairo_surface_t *surface) +{ + return ((cairo_surface_observer_t *) surface)->target; +} + +static inline cairo_bool_t +_cairo_surface_is_observer (cairo_surface_t *surface) +{ + return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER; +} + +static inline cairo_bool_t +_cairo_device_is_observer (cairo_device_t *device) +{ + return device->backend->type == (cairo_device_type_t)CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER; +} + +#endif /* CAIRO_SURFACE_OBSERVER_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-observer-private.h b/gfx/cairo/cairo/src/cairo-surface-observer-private.h new file mode 100644 index 000000000000..6ed0c18d17c2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-observer-private.h @@ -0,0 +1,208 @@ +/* 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 Intel Corporation. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_OBSERVER_PRIVATE_H +#define CAIRO_SURFACE_OBSERVER_PRIVATE_H + +#include "cairoint.h" + +#include "cairo-device-private.h" +#include "cairo-list-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" +#include "cairo-time-private.h" + +struct stat { + double min, max, sum, sum_sq; + unsigned count; +}; + +#define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1) +#define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1) +#define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1) +#define NUM_ANTIALIAS (CAIRO_ANTIALIAS_BEST+1) +#define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1) + +struct extents { + struct stat area; + unsigned int bounded, unbounded; +}; + +struct pattern { + unsigned int type[8]; /* native/record/other surface/gradients */ +}; + +struct path { + unsigned int type[5]; /* empty/pixel/rectilinear/straight/curved */ +}; + +struct clip { + unsigned int type[6]; /* none, region, boxes, single path, polygon, general */ +}; + +typedef struct _cairo_observation cairo_observation_t; +typedef struct _cairo_observation_record cairo_observation_record_t; +typedef struct _cairo_device_observer cairo_device_observer_t; + +struct _cairo_observation_record { + cairo_content_t target_content; + int target_width; + int target_height; + + int index; + cairo_operator_t op; + int source; + int mask; + int num_glyphs; + int path; + int fill_rule; + double tolerance; + int antialias; + int clip; + cairo_time_t elapsed; +}; + +struct _cairo_observation { + int num_surfaces; + int num_contexts; + int num_sources_acquired; + + /* XXX put interesting stats here! */ + + struct paint { + cairo_time_t elapsed; + unsigned int count; + struct extents extents; + unsigned int operators[NUM_OPERATORS]; + struct pattern source; + struct clip clip; + unsigned int noop; + + cairo_observation_record_t slowest; + } paint; + + struct mask { + cairo_time_t elapsed; + unsigned int count; + struct extents extents; + unsigned int operators[NUM_OPERATORS]; + struct pattern source; + struct pattern mask; + struct clip clip; + unsigned int noop; + + cairo_observation_record_t slowest; + } mask; + + struct fill { + cairo_time_t elapsed; + unsigned int count; + struct extents extents; + unsigned int operators[NUM_OPERATORS]; + struct pattern source; + struct path path; + unsigned int antialias[NUM_ANTIALIAS]; + unsigned int fill_rule[NUM_FILL_RULE]; + struct clip clip; + unsigned int noop; + + cairo_observation_record_t slowest; + } fill; + + struct stroke { + cairo_time_t elapsed; + unsigned int count; + struct extents extents; + unsigned int operators[NUM_OPERATORS]; + unsigned int caps[NUM_CAPS]; + unsigned int joins[NUM_CAPS]; + unsigned int antialias[NUM_ANTIALIAS]; + struct pattern source; + struct path path; + struct stat line_width; + struct clip clip; + unsigned int noop; + + cairo_observation_record_t slowest; + } stroke; + + struct glyphs { + cairo_time_t elapsed; + unsigned int count; + struct extents extents; + unsigned int operators[NUM_OPERATORS]; + struct pattern source; + struct clip clip; + unsigned int noop; + + cairo_observation_record_t slowest; + } glyphs; + + cairo_array_t timings; + cairo_recording_surface_t *record; +}; + +struct _cairo_device_observer { + cairo_device_t base; + cairo_device_t *target; + + cairo_observation_t log; +}; + +struct callback_list { + cairo_list_t link; + + cairo_surface_observer_callback_t func; + void *data; +}; + +struct _cairo_surface_observer { + cairo_surface_t base; + cairo_surface_t *target; + + cairo_observation_t log; + + cairo_list_t paint_callbacks; + cairo_list_t mask_callbacks; + cairo_list_t fill_callbacks; + cairo_list_t stroke_callbacks; + cairo_list_t glyphs_callbacks; + + cairo_list_t flush_callbacks; + cairo_list_t finish_callbacks; +}; + +#endif /* CAIRO_SURFACE_OBSERVER_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-observer.c b/gfx/cairo/cairo/src/cairo-surface-observer.c new file mode 100644 index 000000000000..9c4432e246f5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-observer.c @@ -0,0 +1,2105 @@ +/* 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 Intel Corporation. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-surface-observer-private.h" +#include "cairo-surface-observer-inline.h" + +#include "cairo-array-private.h" +#include "cairo-combsort-inline.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-pattern-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-subsurface-inline.h" +#include "cairo-reference-count-private.h" + +#if CAIRO_HAS_SCRIPT_SURFACE +#include "cairo-script-private.h" +#endif + +static const cairo_surface_backend_t _cairo_surface_observer_backend; + +/* observation/stats */ + +static void init_stats (struct stat *s) +{ + s->min = HUGE_VAL; + s->max = -HUGE_VAL; +} + +static void init_extents (struct extents *e) +{ + init_stats (&e->area); +} + +static void init_pattern (struct pattern *p) +{ +} + +static void init_path (struct path *p) +{ +} + +static void init_clip (struct clip *c) +{ +} + +static void init_paint (struct paint *p) +{ + init_extents (&p->extents); + init_pattern (&p->source); + init_clip (&p->clip); +} + +static void init_mask (struct mask *m) +{ + init_extents (&m->extents); + init_pattern (&m->source); + init_pattern (&m->mask); + init_clip (&m->clip); +} + +static void init_fill (struct fill *f) +{ + init_extents (&f->extents); + init_pattern (&f->source); + init_path (&f->path); + init_clip (&f->clip); +} + +static void init_stroke (struct stroke *s) +{ + init_extents (&s->extents); + init_pattern (&s->source); + init_path (&s->path); + init_clip (&s->clip); +} + +static void init_glyphs (struct glyphs *g) +{ + init_extents (&g->extents); + init_pattern (&g->source); + init_clip (&g->clip); +} + +static cairo_status_t +log_init (cairo_observation_t *log, + cairo_bool_t record) +{ + memset (log, 0, sizeof(*log)); + + init_paint (&log->paint); + init_mask (&log->mask); + init_fill (&log->fill); + init_stroke (&log->stroke); + init_glyphs (&log->glyphs); + + _cairo_array_init (&log->timings, sizeof (cairo_observation_record_t)); + + if (record) { + log->record = (cairo_recording_surface_t *) + cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); + if (unlikely (log->record->base.status)) + return log->record->base.status; + + log->record->optimize_clears = FALSE; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +log_fini (cairo_observation_t *log) +{ + _cairo_array_fini (&log->timings); + cairo_surface_destroy (&log->record->base); +} + +static cairo_surface_t* +get_pattern_surface (const cairo_pattern_t *pattern) +{ + return ((cairo_surface_pattern_t *)pattern)->surface; +} + +static int +classify_pattern (const cairo_pattern_t *pattern, + const cairo_surface_t *target) +{ + int classify; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: + if (get_pattern_surface (pattern)->type == target->type) + classify = 0; + else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING) + classify = 1; + else + classify = 2; + break; + default: + case CAIRO_PATTERN_TYPE_SOLID: + classify = 3; + break; + case CAIRO_PATTERN_TYPE_LINEAR: + classify = 4; + break; + case CAIRO_PATTERN_TYPE_RADIAL: + classify = 5; + break; + case CAIRO_PATTERN_TYPE_MESH: + classify = 6; + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + classify = 7; + break; + } + return classify; +} + +static void +add_pattern (struct pattern *stats, + const cairo_pattern_t *pattern, + const cairo_surface_t *target) +{ + stats->type[classify_pattern(pattern, target)]++; +} + +static int +classify_path (const cairo_path_fixed_t *path, + cairo_bool_t is_fill) +{ + int classify; + + /* XXX improve for stroke */ + classify = -1; + if (is_fill) { + if (path->fill_is_empty) + classify = 0; + else if (_cairo_path_fixed_fill_is_rectilinear (path)) + classify = path->fill_maybe_region ? 1 : 2; + } else { + if (_cairo_path_fixed_stroke_is_rectilinear (path)) + classify = 2; + } + if (classify == -1) + classify = 3 + (path->has_curve_to != 0); + + return classify; +} + +static void +add_path (struct path *stats, + const cairo_path_fixed_t *path, + cairo_bool_t is_fill) +{ + stats->type[classify_path(path, is_fill)]++; +} + +static int +classify_clip (const cairo_clip_t *clip) +{ + int classify; + + if (clip == NULL) + classify = 0; + else if (_cairo_clip_is_region (clip)) + classify = 1; + else if (clip->path == NULL) + classify = 2; + else if (clip->path->prev == NULL) + classify = 3; + else if (_cairo_clip_is_polygon (clip)) + classify = 4; + else + classify = 5; + + return classify; +} + +static void +add_clip (struct clip *stats, + const cairo_clip_t *clip) +{ + stats->type[classify_clip (clip)]++; +} + +static void +stats_add (struct stat *s, double v) +{ + if (v < s->min) + s->min = v; + if (v > s->max) + s->max = v; + s->sum += v; + s->sum_sq += v*v; + s->count++; +} + +static void +add_extents (struct extents *stats, + const cairo_composite_rectangles_t *extents) +{ + const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded; + stats_add (&stats->area, r->width * r->height); + stats->bounded += extents->is_bounded != 0; + stats->unbounded += extents->is_bounded == 0; +} + +/* device interface */ + +static void +_cairo_device_observer_lock (void *_device) +{ + cairo_device_observer_t *device = (cairo_device_observer_t *) _device; + cairo_status_t ignored; + + /* cairo_device_acquire() can fail for nil and finished + * devices. We don't care about observing them. */ + ignored = cairo_device_acquire (device->target); +} + +static void +_cairo_device_observer_unlock (void *_device) +{ + cairo_device_observer_t *device = (cairo_device_observer_t *) _device; + cairo_device_release (device->target); +} + +static cairo_status_t +_cairo_device_observer_flush (void *_device) +{ + cairo_device_observer_t *device = (cairo_device_observer_t *) _device; + + if (device->target == NULL) + return CAIRO_STATUS_SUCCESS; + + cairo_device_flush (device->target); + return device->target->status; +} + +static void +_cairo_device_observer_finish (void *_device) +{ + cairo_device_observer_t *device = (cairo_device_observer_t *) _device; + log_fini (&device->log); + cairo_device_finish (device->target); +} + +static void +_cairo_device_observer_destroy (void *_device) +{ + cairo_device_observer_t *device = (cairo_device_observer_t *) _device; + cairo_device_destroy (device->target); + free (device); +} + +static const cairo_device_backend_t _cairo_device_observer_backend = { + CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER, + + _cairo_device_observer_lock, + _cairo_device_observer_unlock, + + _cairo_device_observer_flush, + _cairo_device_observer_finish, + _cairo_device_observer_destroy, +}; + +static cairo_device_t * +_cairo_device_create_observer_internal (cairo_device_t *target, + cairo_bool_t record) +{ + cairo_device_observer_t *device; + cairo_status_t status; + + device = _cairo_malloc (sizeof (cairo_device_observer_t)); + if (unlikely (device == NULL)) + return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_device_init (&device->base, &_cairo_device_observer_backend); + status = log_init (&device->log, record); + if (unlikely (status)) { + free (device); + return _cairo_device_create_in_error (status); + } + + device->target = cairo_device_reference (target); + + return &device->base; +} + +/* surface interface */ + +static cairo_device_observer_t * +to_device (cairo_surface_observer_t *suface) +{ + return (cairo_device_observer_t *)suface->base.device; +} + +static cairo_surface_t * +_cairo_surface_create_observer_internal (cairo_device_t *device, + cairo_surface_t *target) +{ + cairo_surface_observer_t *surface; + cairo_status_t status; + + surface = _cairo_malloc (sizeof (cairo_surface_observer_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_surface_observer_backend, device, + target->content, + target->is_vector); + + status = log_init (&surface->log, + ((cairo_device_observer_t *)device)->log.record != NULL); + if (unlikely (status)) { + free (surface); + return _cairo_surface_create_in_error (status); + } + + surface->target = cairo_surface_reference (target); + surface->base.type = surface->target->type; + surface->base.is_clear = surface->target->is_clear; + + cairo_list_init (&surface->paint_callbacks); + cairo_list_init (&surface->mask_callbacks); + cairo_list_init (&surface->fill_callbacks); + cairo_list_init (&surface->stroke_callbacks); + cairo_list_init (&surface->glyphs_callbacks); + + cairo_list_init (&surface->flush_callbacks); + cairo_list_init (&surface->finish_callbacks); + + surface->log.num_surfaces++; + to_device (surface)->log.num_surfaces++; + + return &surface->base; +} + +static inline void +do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head) +{ + struct callback_list *cb; + + cairo_list_foreach_entry (cb, struct callback_list, head, link) + cb->func (&surface->base, surface->target, cb->data); +} + + +static cairo_status_t +_cairo_surface_observer_finish (void *abstract_surface) +{ + cairo_surface_observer_t *surface = abstract_surface; + + do_callbacks (surface, &surface->finish_callbacks); + + cairo_surface_destroy (surface->target); + log_fini (&surface->log); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_surface_observer_create_similar (void *abstract_other, + cairo_content_t content, + int width, int height) +{ + cairo_surface_observer_t *other = abstract_other; + cairo_surface_t *target, *surface; + + target = NULL; + if (other->target->backend->create_similar) + target = other->target->backend->create_similar (other->target, content, + width, height); + if (target == NULL) + target = _cairo_image_surface_create_with_content (content, + width, height); + + surface = _cairo_surface_create_observer_internal (other->base.device, + target); + cairo_surface_destroy (target); + + return surface; +} + +static cairo_surface_t * +_cairo_surface_observer_create_similar_image (void *other, + cairo_format_t format, + int width, int height) +{ + cairo_surface_observer_t *surface = other; + + if (surface->target->backend->create_similar_image) + return surface->target->backend->create_similar_image (surface->target, + format, + width, height); + + return NULL; +} + +static cairo_image_surface_t * +_cairo_surface_observer_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_observer_t *surface = abstract_surface; + return _cairo_surface_map_to_image (surface->target, extents); +} + +static cairo_int_status_t +_cairo_surface_observer_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_surface_observer_t *surface = abstract_surface; + return _cairo_surface_unmap_image (surface->target, image); +} + +static void +record_target (cairo_observation_record_t *r, + cairo_surface_t *target) +{ + cairo_rectangle_int_t extents; + + r->target_content = target->content; + if (_cairo_surface_get_extents (target, &extents)) { + r->target_width = extents.width; + r->target_height = extents.height; + } else { + r->target_width = -1; + r->target_height = -1; + } +} + +static cairo_observation_record_t * +record_paint (cairo_observation_record_t *r, + cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip, + cairo_time_t elapsed) +{ + record_target (r, target); + + r->op = op; + r->source = classify_pattern (source, target); + r->mask = -1; + r->num_glyphs = -1; + r->path = -1; + r->fill_rule = -1; + r->tolerance = -1; + r->antialias = -1; + r->clip = classify_clip (clip); + r->elapsed = elapsed; + + return r; +} + +static cairo_observation_record_t * +record_mask (cairo_observation_record_t *r, + cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip, + cairo_time_t elapsed) +{ + record_target (r, target); + + r->op = op; + r->source = classify_pattern (source, target); + r->mask = classify_pattern (mask, target); + r->num_glyphs = -1; + r->path = -1; + r->fill_rule = -1; + r->tolerance = -1; + r->antialias = -1; + r->clip = classify_clip (clip); + r->elapsed = elapsed; + + return r; +} + +static cairo_observation_record_t * +record_fill (cairo_observation_record_t *r, + cairo_surface_t *target, + 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_time_t elapsed) +{ + record_target (r, target); + + r->op = op; + r->source = classify_pattern (source, target); + r->mask = -1; + r->num_glyphs = -1; + r->path = classify_path (path, TRUE); + r->fill_rule = fill_rule; + r->tolerance = tolerance; + r->antialias = antialias; + r->clip = classify_clip (clip); + r->elapsed = elapsed; + + return r; +} + +static cairo_observation_record_t * +record_stroke (cairo_observation_record_t *r, + cairo_surface_t *target, + 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_time_t elapsed) +{ + record_target (r, target); + + r->op = op; + r->source = classify_pattern (source, target); + r->mask = -1; + r->num_glyphs = -1; + r->path = classify_path (path, FALSE); + r->fill_rule = -1; + r->tolerance = tolerance; + r->antialias = antialias; + r->clip = classify_clip (clip); + r->elapsed = elapsed; + + return r; +} + +static cairo_observation_record_t * +record_glyphs (cairo_observation_record_t *r, + cairo_surface_t *target, + 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_time_t elapsed) +{ + record_target (r, target); + + r->op = op; + r->source = classify_pattern (source, target); + r->mask = -1; + r->path = -1; + r->num_glyphs = num_glyphs; + r->fill_rule = -1; + r->tolerance = -1; + r->antialias = -1; + r->clip = classify_clip (clip); + r->elapsed = elapsed; + + return r; +} + +static void +add_record (cairo_observation_t *log, + cairo_observation_record_t *r) +{ + cairo_int_status_t status; + + r->index = log->record ? log->record->commands.num_elements : 0; + + status = _cairo_array_append (&log->timings, r); + assert (status == CAIRO_INT_STATUS_SUCCESS); +} + +static void +_cairo_surface_sync (cairo_surface_t *target, int x, int y) +{ + cairo_rectangle_int_t extents; + + extents.x = x; + extents.y = y; + extents.width = 1; + extents.height = 1; + + _cairo_surface_unmap_image (target, + _cairo_surface_map_to_image (target, + &extents)); +} + +static void +midpt (const cairo_composite_rectangles_t *extents, int *x, int *y) +{ + *x = extents->bounded.x + extents->bounded.width / 2; + *y = extents->bounded.y + extents->bounded.height / 2; +} + +static void +add_record_paint (cairo_observation_t *log, + cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip, + cairo_time_t elapsed) +{ + cairo_observation_record_t record; + cairo_int_status_t status; + + add_record (log, + record_paint (&record, target, op, source, clip, elapsed)); + + /* We have to bypass the high-level surface layer in case it tries to be + * too smart and discard operations; we need to record exactly what just + * happened on the target. + */ + if (log->record) { + status = log->record->base.backend->paint (&log->record->base, + op, source, clip); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed)) + log->paint.slowest = record; + log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed); +} + +static cairo_int_status_t +_cairo_surface_observer_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_surface_observer_t *surface = abstract_surface; + cairo_device_observer_t *device = to_device (surface); + cairo_composite_rectangles_t composite; + cairo_int_status_t status; + cairo_time_t t; + int x, y; + + /* XXX device locking */ + + surface->log.paint.count++; + surface->log.paint.operators[op]++; + add_pattern (&surface->log.paint.source, source, surface->target); + add_clip (&surface->log.paint.clip, clip); + + device->log.paint.count++; + device->log.paint.operators[op]++; + add_pattern (&device->log.paint.source, source, surface->target); + add_clip (&device->log.paint.clip, clip); + + status = _cairo_composite_rectangles_init_for_paint (&composite, + surface->target, + op, source, + clip); + if (unlikely (status)) { + surface->log.paint.noop++; + device->log.paint.noop++; + return status; + } + + midpt (&composite, &x, &y); + + add_extents (&surface->log.paint.extents, &composite); + add_extents (&device->log.paint.extents, &composite); + _cairo_composite_rectangles_fini (&composite); + + t = _cairo_time_get (); + status = _cairo_surface_paint (surface->target, + op, source, + clip); + if (unlikely (status)) + return status; + + _cairo_surface_sync (surface->target, x, y); + t = _cairo_time_get_delta (t); + + add_record_paint (&surface->log, surface->target, op, source, clip, t); + add_record_paint (&device->log, surface->target, op, source, clip, t); + + do_callbacks (surface, &surface->paint_callbacks); + + return CAIRO_STATUS_SUCCESS; +} + +static void +add_record_mask (cairo_observation_t *log, + cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip, + cairo_time_t elapsed) +{ + cairo_observation_record_t record; + cairo_int_status_t status; + + add_record (log, + record_mask (&record, target, op, source, mask, clip, elapsed)); + + if (log->record) { + status = log->record->base.backend->mask (&log->record->base, + op, source, mask, clip); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed)) + log->mask.slowest = record; + log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed); +} + +static cairo_int_status_t +_cairo_surface_observer_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_surface_observer_t *surface = abstract_surface; + cairo_device_observer_t *device = to_device (surface); + cairo_composite_rectangles_t composite; + cairo_int_status_t status; + cairo_time_t t; + int x, y; + + surface->log.mask.count++; + surface->log.mask.operators[op]++; + add_pattern (&surface->log.mask.source, source, surface->target); + add_pattern (&surface->log.mask.mask, mask, surface->target); + add_clip (&surface->log.mask.clip, clip); + + device->log.mask.count++; + device->log.mask.operators[op]++; + add_pattern (&device->log.mask.source, source, surface->target); + add_pattern (&device->log.mask.mask, mask, surface->target); + add_clip (&device->log.mask.clip, clip); + + status = _cairo_composite_rectangles_init_for_mask (&composite, + surface->target, + op, source, mask, + clip); + if (unlikely (status)) { + surface->log.mask.noop++; + device->log.mask.noop++; + return status; + } + + midpt (&composite, &x, &y); + + add_extents (&surface->log.mask.extents, &composite); + add_extents (&device->log.mask.extents, &composite); + _cairo_composite_rectangles_fini (&composite); + + t = _cairo_time_get (); + status = _cairo_surface_mask (surface->target, + op, source, mask, + clip); + if (unlikely (status)) + return status; + + _cairo_surface_sync (surface->target, x, y); + t = _cairo_time_get_delta (t); + + add_record_mask (&surface->log, + surface->target, op, source, mask, clip, + t); + add_record_mask (&device->log, + surface->target, op, source, mask, clip, + t); + + do_callbacks (surface, &surface->mask_callbacks); + + return CAIRO_STATUS_SUCCESS; +} + +static void +add_record_fill (cairo_observation_t *log, + cairo_surface_t *target, + 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_time_t elapsed) +{ + cairo_observation_record_t record; + cairo_int_status_t status; + + add_record (log, + record_fill (&record, + target, op, source, + path, fill_rule, tolerance, antialias, + clip, elapsed)); + + if (log->record) { + status = log->record->base.backend->fill (&log->record->base, + op, source, + path, fill_rule, + tolerance, antialias, + clip); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed)) + log->fill.slowest = record; + log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed); +} + +static cairo_int_status_t +_cairo_surface_observer_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_surface_observer_t *surface = abstract_surface; + cairo_device_observer_t *device = to_device (surface); + cairo_composite_rectangles_t composite; + cairo_int_status_t status; + cairo_time_t t; + int x, y; + + surface->log.fill.count++; + surface->log.fill.operators[op]++; + surface->log.fill.fill_rule[fill_rule]++; + surface->log.fill.antialias[antialias]++; + add_pattern (&surface->log.fill.source, source, surface->target); + add_path (&surface->log.fill.path, path, TRUE); + add_clip (&surface->log.fill.clip, clip); + + device->log.fill.count++; + device->log.fill.operators[op]++; + device->log.fill.fill_rule[fill_rule]++; + device->log.fill.antialias[antialias]++; + add_pattern (&device->log.fill.source, source, surface->target); + add_path (&device->log.fill.path, path, TRUE); + add_clip (&device->log.fill.clip, clip); + + status = _cairo_composite_rectangles_init_for_fill (&composite, + surface->target, + op, source, path, + clip); + if (unlikely (status)) { + surface->log.fill.noop++; + device->log.fill.noop++; + return status; + } + + midpt (&composite, &x, &y); + + add_extents (&surface->log.fill.extents, &composite); + add_extents (&device->log.fill.extents, &composite); + _cairo_composite_rectangles_fini (&composite); + + t = _cairo_time_get (); + status = _cairo_surface_fill (surface->target, + op, source, path, + fill_rule, tolerance, antialias, + clip); + if (unlikely (status)) + return status; + + _cairo_surface_sync (surface->target, x, y); + t = _cairo_time_get_delta (t); + + add_record_fill (&surface->log, + surface->target, op, source, path, + fill_rule, tolerance, antialias, + clip, t); + + add_record_fill (&device->log, + surface->target, op, source, path, + fill_rule, tolerance, antialias, + clip, t); + + do_callbacks (surface, &surface->fill_callbacks); + + return CAIRO_STATUS_SUCCESS; +} + +static void +add_record_stroke (cairo_observation_t *log, + cairo_surface_t *target, + 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_time_t elapsed) +{ + cairo_observation_record_t record; + cairo_int_status_t status; + + add_record (log, + record_stroke (&record, + target, op, source, + path, style, ctm,ctm_inverse, + tolerance, antialias, + clip, elapsed)); + + if (log->record) { + status = log->record->base.backend->stroke (&log->record->base, + op, source, + path, style, ctm,ctm_inverse, + tolerance, antialias, + clip); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed)) + log->stroke.slowest = record; + log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed); +} + +static cairo_int_status_t +_cairo_surface_observer_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_surface_observer_t *surface = abstract_surface; + cairo_device_observer_t *device = to_device (surface); + cairo_composite_rectangles_t composite; + cairo_int_status_t status; + cairo_time_t t; + int x, y; + + surface->log.stroke.count++; + surface->log.stroke.operators[op]++; + surface->log.stroke.antialias[antialias]++; + surface->log.stroke.caps[style->line_cap]++; + surface->log.stroke.joins[style->line_join]++; + add_pattern (&surface->log.stroke.source, source, surface->target); + add_path (&surface->log.stroke.path, path, FALSE); + add_clip (&surface->log.stroke.clip, clip); + + device->log.stroke.count++; + device->log.stroke.operators[op]++; + device->log.stroke.antialias[antialias]++; + device->log.stroke.caps[style->line_cap]++; + device->log.stroke.joins[style->line_join]++; + add_pattern (&device->log.stroke.source, source, surface->target); + add_path (&device->log.stroke.path, path, FALSE); + add_clip (&device->log.stroke.clip, clip); + + status = _cairo_composite_rectangles_init_for_stroke (&composite, + surface->target, + op, source, + path, style, ctm, + clip); + if (unlikely (status)) { + surface->log.stroke.noop++; + device->log.stroke.noop++; + return status; + } + + midpt (&composite, &x, &y); + + add_extents (&surface->log.stroke.extents, &composite); + add_extents (&device->log.stroke.extents, &composite); + _cairo_composite_rectangles_fini (&composite); + + t = _cairo_time_get (); + status = _cairo_surface_stroke (surface->target, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + + _cairo_surface_sync (surface->target, x, y); + t = _cairo_time_get_delta (t); + + add_record_stroke (&surface->log, + surface->target, op, source, path, + style, ctm,ctm_inverse, + tolerance, antialias, + clip, t); + + add_record_stroke (&device->log, + surface->target, op, source, path, + style, ctm,ctm_inverse, + tolerance, antialias, + clip, t); + + do_callbacks (surface, &surface->stroke_callbacks); + + return CAIRO_STATUS_SUCCESS; +} + +static void +add_record_glyphs (cairo_observation_t *log, + cairo_surface_t *target, + 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_time_t elapsed) +{ + cairo_observation_record_t record; + cairo_int_status_t status; + + add_record (log, + record_glyphs (&record, + target, op, source, + glyphs, num_glyphs, scaled_font, + clip, elapsed)); + + if (log->record) { + status = log->record->base.backend->show_text_glyphs (&log->record->base, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed)) + log->glyphs.slowest = record; + log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed); +} + +static cairo_int_status_t +_cairo_surface_observer_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_surface_observer_t *surface = abstract_surface; + cairo_device_observer_t *device = to_device (surface); + cairo_composite_rectangles_t composite; + cairo_int_status_t status; + cairo_glyph_t *dev_glyphs; + cairo_time_t t; + int x, y; + + surface->log.glyphs.count++; + surface->log.glyphs.operators[op]++; + add_pattern (&surface->log.glyphs.source, source, surface->target); + add_clip (&surface->log.glyphs.clip, clip); + + device->log.glyphs.count++; + device->log.glyphs.operators[op]++; + add_pattern (&device->log.glyphs.source, source, surface->target); + add_clip (&device->log.glyphs.clip, clip); + + status = _cairo_composite_rectangles_init_for_glyphs (&composite, + surface->target, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + NULL); + if (unlikely (status)) { + surface->log.glyphs.noop++; + device->log.glyphs.noop++; + return status; + } + + midpt (&composite, &x, &y); + + add_extents (&surface->log.glyphs.extents, &composite); + add_extents (&device->log.glyphs.extents, &composite); + _cairo_composite_rectangles_fini (&composite); + + /* XXX We have to copy the glyphs, because the backend is allowed to + * modify! */ + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (dev_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); + + t = _cairo_time_get (); + status = _cairo_surface_show_text_glyphs (surface->target, op, source, + NULL, 0, + dev_glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); + free (dev_glyphs); + if (unlikely (status)) + return status; + + _cairo_surface_sync (surface->target, x, y); + t = _cairo_time_get_delta (t); + + add_record_glyphs (&surface->log, + surface->target, op, source, + glyphs, num_glyphs, scaled_font, + clip, t); + + add_record_glyphs (&device->log, + surface->target, op, source, + glyphs, num_glyphs, scaled_font, + clip, t); + + do_callbacks (surface, &surface->glyphs_callbacks); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_surface_observer_flush (void *abstract_surface, unsigned flags) +{ + cairo_surface_observer_t *surface = abstract_surface; + + do_callbacks (surface, &surface->flush_callbacks); + return _cairo_surface_flush (surface->target, flags); +} + +static cairo_status_t +_cairo_surface_observer_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_surface_observer_t *surface = abstract_surface; + cairo_status_t status; + + status = CAIRO_STATUS_SUCCESS; + if (surface->target->backend->mark_dirty_rectangle) + status = surface->target->backend->mark_dirty_rectangle (surface->target, + x,y, width,height); + + return status; +} + +static cairo_int_status_t +_cairo_surface_observer_copy_page (void *abstract_surface) +{ + cairo_surface_observer_t *surface = abstract_surface; + cairo_status_t status; + + status = CAIRO_STATUS_SUCCESS; + if (surface->target->backend->copy_page) + status = surface->target->backend->copy_page (surface->target); + + return status; +} + +static cairo_int_status_t +_cairo_surface_observer_show_page (void *abstract_surface) +{ + cairo_surface_observer_t *surface = abstract_surface; + cairo_status_t status; + + status = CAIRO_STATUS_SUCCESS; + if (surface->target->backend->show_page) + status = surface->target->backend->show_page (surface->target); + + return status; +} + +static cairo_bool_t +_cairo_surface_observer_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_observer_t *surface = abstract_surface; + return _cairo_surface_get_extents (surface->target, extents); +} + +static void +_cairo_surface_observer_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_surface_observer_t *surface = abstract_surface; + + if (surface->target->backend->get_font_options != NULL) + surface->target->backend->get_font_options (surface->target, options); +} + +static cairo_surface_t * +_cairo_surface_observer_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_observer_t *surface = abstract_surface; + return _cairo_surface_get_source (surface->target, extents); +} + +static cairo_status_t +_cairo_surface_observer_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_surface_observer_t *surface = abstract_surface; + + surface->log.num_sources_acquired++; + to_device (surface)->log.num_sources_acquired++; + + return _cairo_surface_acquire_source_image (surface->target, + image_out, image_extra); +} + +static void +_cairo_surface_observer_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_observer_t *surface = abstract_surface; + + _cairo_surface_release_source_image (surface->target, image, image_extra); +} + +static cairo_surface_t * +_cairo_surface_observer_snapshot (void *abstract_surface) +{ + cairo_surface_observer_t *surface = abstract_surface; + + /* XXX hook onto the snapshot so that we measure number of reads */ + + if (surface->target->backend->snapshot) + return surface->target->backend->snapshot (surface->target); + + return NULL; +} + +static cairo_t * +_cairo_surface_observer_create_context(void *target) +{ + cairo_surface_observer_t *surface = target; + + if (_cairo_surface_is_subsurface (&surface->base)) + surface = (cairo_surface_observer_t *) + _cairo_surface_subsurface_get_target (&surface->base); + + surface->log.num_contexts++; + to_device (surface)->log.num_contexts++; + + return surface->target->backend->create_context (target); +} + +static const cairo_surface_backend_t _cairo_surface_observer_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER, + _cairo_surface_observer_finish, + + _cairo_surface_observer_create_context, + + _cairo_surface_observer_create_similar, + _cairo_surface_observer_create_similar_image, + _cairo_surface_observer_map_to_image, + _cairo_surface_observer_unmap_image, + + _cairo_surface_observer_source, + _cairo_surface_observer_acquire_source_image, + _cairo_surface_observer_release_source_image, + _cairo_surface_observer_snapshot, + + _cairo_surface_observer_copy_page, + _cairo_surface_observer_show_page, + + _cairo_surface_observer_get_extents, + _cairo_surface_observer_get_font_options, + + _cairo_surface_observer_flush, + _cairo_surface_observer_mark_dirty, + + _cairo_surface_observer_paint, + _cairo_surface_observer_mask, + _cairo_surface_observer_stroke, + _cairo_surface_observer_fill, + NULL, /* fill-stroke */ + _cairo_surface_observer_glyphs, +}; + +/** + * cairo_surface_create_observer: + * @target: an existing surface for which the observer will watch + * @mode: sets the mode of operation (normal vs. record) + * + * Create a new surface that exists solely to watch another is doing. In + * the process it will log operations and times, which are fast, which are + * slow, which are frequent, etc. + * + * The @mode parameter can be set to either CAIRO_SURFACE_OBSERVER_NORMAL + * or CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS, to control whether or not + * the internal observer should record operations. + * + * Return value: a pointer to the newly allocated 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 @other is already in an error state + * or any other error occurs. + * + * Since: 1.12 + **/ +cairo_surface_t * +cairo_surface_create_observer (cairo_surface_t *target, + cairo_surface_observer_mode_t mode) +{ + cairo_device_t *device; + cairo_surface_t *surface; + cairo_bool_t record; + + if (unlikely (target->status)) + return _cairo_surface_create_in_error (target->status); + if (unlikely (target->finished)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS; + device = _cairo_device_create_observer_internal (target->device, record); + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + surface = _cairo_surface_create_observer_internal (device, target); + cairo_device_destroy (device); + + return surface; +} + +static cairo_status_t +_cairo_surface_observer_add_callback (cairo_list_t *head, + cairo_surface_observer_callback_t func, + void *data) +{ + struct callback_list *cb; + + cb = _cairo_malloc (sizeof (*cb)); + if (unlikely (cb == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_add (&cb->link, head); + cb->func = func; + cb->data = data; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->paint_callbacks, + func, data); +} + +cairo_status_t +cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->mask_callbacks, + func, data); +} + +cairo_status_t +cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->fill_callbacks, + func, data); +} + +cairo_status_t +cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->stroke_callbacks, + func, data); +} + +cairo_status_t +cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks, + func, data); +} + +cairo_status_t +cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->flush_callbacks, + func, data); +} + +cairo_status_t +cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return abstract_surface->status; + + if (! _cairo_surface_is_observer (abstract_surface)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *)abstract_surface; + return _cairo_surface_observer_add_callback (&surface->finish_callbacks, + func, data); +} + +static void +print_extents (cairo_output_stream_t *stream, const struct extents *e) +{ + _cairo_output_stream_printf (stream, + " extents: total %g, avg %g [unbounded %d]\n", + e->area.sum, + e->area.sum / e->area.count, + e->unbounded); +} + +static inline int ordercmp (int a, int b, const unsigned int *array) +{ + /* high to low */ + return array[b] - array[a]; +} +CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp) + +static void +print_array (cairo_output_stream_t *stream, + const unsigned int *array, + const char **names, + int count) +{ + int order[64]; + int i, j; + + assert (count < ARRAY_LENGTH (order)); + for (i = j = 0; i < count; i++) { + if (array[i] != 0) + order[j++] = i; + } + + sort_order (order, j, (void *)array); + for (i = 0; i < j; i++) + _cairo_output_stream_printf (stream, " %d %s%s", + array[order[i]], names[order[i]], + i < j -1 ? "," : ""); +} + +static const char *operator_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 */ +}; +static void +print_operators (cairo_output_stream_t *stream, unsigned int *array) +{ + _cairo_output_stream_printf (stream, " op:"); + print_array (stream, array, operator_names, NUM_OPERATORS); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *fill_rule_names[] = { + "non-zero", + "even-odd", +}; +static void +print_fill_rule (cairo_output_stream_t *stream, unsigned int *array) +{ + _cairo_output_stream_printf (stream, " fill rule:"); + print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names)); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *cap_names[] = { + "butt", /* CAIRO_LINE_CAP_BUTT */ + "round", /* CAIRO_LINE_CAP_ROUND */ + "square" /* CAIRO_LINE_CAP_SQUARE */ +}; +static void +print_line_caps (cairo_output_stream_t *stream, unsigned int *array) +{ + _cairo_output_stream_printf (stream, " caps:"); + print_array (stream, array, cap_names, NUM_CAPS); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *join_names[] = { + "miter", /* CAIRO_LINE_JOIN_MITER */ + "round", /* CAIRO_LINE_JOIN_ROUND */ + "bevel", /* CAIRO_LINE_JOIN_BEVEL */ +}; +static void +print_line_joins (cairo_output_stream_t *stream, unsigned int *array) +{ + _cairo_output_stream_printf (stream, " joins:"); + print_array (stream, array, join_names, NUM_JOINS); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *antialias_names[] = { + "default", + "none", + "gray", + "subpixel", + "fast", + "good", + "best" +}; +static void +print_antialias (cairo_output_stream_t *stream, unsigned int *array) +{ + _cairo_output_stream_printf (stream, " antialias:"); + print_array (stream, array, antialias_names, NUM_ANTIALIAS); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *pattern_names[] = { + "native", + "record", + "other surface", + "solid", + "linear", + "radial", + "mesh", + "raster" +}; +static void +print_pattern (cairo_output_stream_t *stream, + const char *name, + const struct pattern *p) +{ + _cairo_output_stream_printf (stream, " %s:", name); + print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names)); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *path_names[] = { + "empty", + "pixel-aligned", + "rectliinear", + "straight", + "curved", +}; +static void +print_path (cairo_output_stream_t *stream, + const struct path *p) +{ + _cairo_output_stream_printf (stream, " path:"); + print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names)); + _cairo_output_stream_printf (stream, "\n"); +} + +static const char *clip_names[] = { + "none", + "region", + "boxes", + "single path", + "polygon", + "general", +}; +static void +print_clip (cairo_output_stream_t *stream, const struct clip *c) +{ + _cairo_output_stream_printf (stream, " clip:"); + print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names)); + _cairo_output_stream_printf (stream, "\n"); +} + +static void +print_record (cairo_output_stream_t *stream, + cairo_observation_record_t *r) +{ + _cairo_output_stream_printf (stream, " op: %s\n", operator_names[r->op]); + _cairo_output_stream_printf (stream, " source: %s\n", + pattern_names[r->source]); + if (r->mask != -1) + _cairo_output_stream_printf (stream, " mask: %s\n", + pattern_names[r->mask]); + if (r->num_glyphs != -1) + _cairo_output_stream_printf (stream, " num_glyphs: %d\n", + r->num_glyphs); + if (r->path != -1) + _cairo_output_stream_printf (stream, " path: %s\n", + path_names[r->path]); + if (r->fill_rule != -1) + _cairo_output_stream_printf (stream, " fill rule: %s\n", + fill_rule_names[r->fill_rule]); + if (r->antialias != -1) + _cairo_output_stream_printf (stream, " antialias: %s\n", + antialias_names[r->antialias]); + _cairo_output_stream_printf (stream, " clip: %s\n", clip_names[r->clip]); + _cairo_output_stream_printf (stream, " elapsed: %f ns\n", + _cairo_time_to_ns (r->elapsed)); +} + +static double percent (cairo_time_t a, cairo_time_t b) +{ + /* Fake %.1f */ + return _cairo_round (_cairo_time_to_s (a) * 1000 / + _cairo_time_to_s (b)) / 10; +} + +static cairo_bool_t +replay_record (cairo_observation_t *log, + cairo_observation_record_t *r, + cairo_device_t *script) +{ +#if CAIRO_HAS_SCRIPT_SURFACE + cairo_surface_t *surface; + cairo_int_status_t status; + + if (log->record == NULL || script == NULL) + return FALSE; + + surface = cairo_script_surface_create (script, + r->target_content, + r->target_width, + r->target_height); + status = + _cairo_recording_surface_replay_one (log->record, r->index, surface); + cairo_surface_destroy (surface); + + assert (status == CAIRO_INT_STATUS_SUCCESS); + + return TRUE; +#else + return FALSE; +#endif +} + +static cairo_time_t +_cairo_observation_total_elapsed (cairo_observation_t *log) +{ + cairo_time_t total; + + total = log->paint.elapsed; + total = _cairo_time_add (total, log->mask.elapsed); + total = _cairo_time_add (total, log->fill.elapsed); + total = _cairo_time_add (total, log->stroke.elapsed); + total = _cairo_time_add (total, log->glyphs.elapsed); + + return total; +} + +static void +_cairo_observation_print (cairo_output_stream_t *stream, + cairo_observation_t *log) +{ + cairo_device_t *script; + cairo_time_t total; + +#if CAIRO_HAS_SCRIPT_SURFACE + script = _cairo_script_context_create_internal (stream); + _cairo_script_context_attach_snapshots (script, FALSE); +#else + script = NULL; +#endif + + total = _cairo_observation_total_elapsed (log); + + _cairo_output_stream_printf (stream, "elapsed: %f\n", + _cairo_time_to_ns (total)); + _cairo_output_stream_printf (stream, "surfaces: %d\n", + log->num_surfaces); + _cairo_output_stream_printf (stream, "contexts: %d\n", + log->num_contexts); + _cairo_output_stream_printf (stream, "sources acquired: %d\n", + log->num_sources_acquired); + + + _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n", + log->paint.count, log->paint.noop, + _cairo_time_to_ns (log->paint.elapsed), + percent (log->paint.elapsed, total)); + if (log->paint.count) { + print_extents (stream, &log->paint.extents); + print_operators (stream, log->paint.operators); + print_pattern (stream, "source", &log->paint.source); + print_clip (stream, &log->paint.clip); + + _cairo_output_stream_printf (stream, "slowest paint: %f%%\n", + percent (log->paint.slowest.elapsed, + log->paint.elapsed)); + print_record (stream, &log->paint.slowest); + + _cairo_output_stream_printf (stream, "\n"); + if (replay_record (log, &log->paint.slowest, script)) + _cairo_output_stream_printf (stream, "\n\n"); + } + + _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n", + log->mask.count, log->mask.noop, + _cairo_time_to_ns (log->mask.elapsed), + percent (log->mask.elapsed, total)); + if (log->mask.count) { + print_extents (stream, &log->mask.extents); + print_operators (stream, log->mask.operators); + print_pattern (stream, "source", &log->mask.source); + print_pattern (stream, "mask", &log->mask.mask); + print_clip (stream, &log->mask.clip); + + _cairo_output_stream_printf (stream, "slowest mask: %f%%\n", + percent (log->mask.slowest.elapsed, + log->mask.elapsed)); + print_record (stream, &log->mask.slowest); + + _cairo_output_stream_printf (stream, "\n"); + if (replay_record (log, &log->mask.slowest, script)) + _cairo_output_stream_printf (stream, "\n\n"); + } + + _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n", + log->fill.count, log->fill.noop, + _cairo_time_to_ns (log->fill.elapsed), + percent (log->fill.elapsed, total)); + if (log->fill.count) { + print_extents (stream, &log->fill.extents); + print_operators (stream, log->fill.operators); + print_pattern (stream, "source", &log->fill.source); + print_path (stream, &log->fill.path); + print_fill_rule (stream, log->fill.fill_rule); + print_antialias (stream, log->fill.antialias); + print_clip (stream, &log->fill.clip); + + _cairo_output_stream_printf (stream, "slowest fill: %f%%\n", + percent (log->fill.slowest.elapsed, + log->fill.elapsed)); + print_record (stream, &log->fill.slowest); + + _cairo_output_stream_printf (stream, "\n"); + if (replay_record (log, &log->fill.slowest, script)) + _cairo_output_stream_printf (stream, "\n\n"); + } + + _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n", + log->stroke.count, log->stroke.noop, + _cairo_time_to_ns (log->stroke.elapsed), + percent (log->stroke.elapsed, total)); + if (log->stroke.count) { + print_extents (stream, &log->stroke.extents); + print_operators (stream, log->stroke.operators); + print_pattern (stream, "source", &log->stroke.source); + print_path (stream, &log->stroke.path); + print_antialias (stream, log->stroke.antialias); + print_line_caps (stream, log->stroke.caps); + print_line_joins (stream, log->stroke.joins); + print_clip (stream, &log->stroke.clip); + + _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n", + percent (log->stroke.slowest.elapsed, + log->stroke.elapsed)); + print_record (stream, &log->stroke.slowest); + + _cairo_output_stream_printf (stream, "\n"); + if (replay_record (log, &log->stroke.slowest, script)) + _cairo_output_stream_printf (stream, "\n\n"); + } + + _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n", + log->glyphs.count, log->glyphs.noop, + _cairo_time_to_ns (log->glyphs.elapsed), + percent (log->glyphs.elapsed, total)); + if (log->glyphs.count) { + print_extents (stream, &log->glyphs.extents); + print_operators (stream, log->glyphs.operators); + print_pattern (stream, "source", &log->glyphs.source); + print_clip (stream, &log->glyphs.clip); + + _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n", + percent (log->glyphs.slowest.elapsed, + log->glyphs.elapsed)); + print_record (stream, &log->glyphs.slowest); + + _cairo_output_stream_printf (stream, "\n"); + if (replay_record (log, &log->glyphs.slowest, script)) + _cairo_output_stream_printf (stream, "\n\n"); + } + + cairo_device_destroy (script); +} + +cairo_status_t +cairo_surface_observer_print (cairo_surface_t *abstract_surface, + cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + cairo_surface_observer_t *surface; + + if (unlikely (abstract_surface->status)) + return abstract_surface->status; + + if (unlikely (! _cairo_surface_is_observer (abstract_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + surface = (cairo_surface_observer_t *) abstract_surface; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + _cairo_observation_print (stream, &surface->log); + return _cairo_output_stream_destroy (stream); +} + +double +cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface) +{ + cairo_surface_observer_t *surface; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count))) + return -1; + + if (! _cairo_surface_is_observer (abstract_surface)) + return -1; + + surface = (cairo_surface_observer_t *) abstract_surface; + return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log)); +} + +cairo_status_t +cairo_device_observer_print (cairo_device_t *abstract_device, + cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + cairo_device_observer_t *device; + + if (unlikely (abstract_device->status)) + return abstract_device->status; + + if (unlikely (! _cairo_device_is_observer (abstract_device))) + return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + device = (cairo_device_observer_t *) abstract_device; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + _cairo_observation_print (stream, &device->log); + return _cairo_output_stream_destroy (stream); +} + +double +cairo_device_observer_elapsed (cairo_device_t *abstract_device) +{ + cairo_device_observer_t *device; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) + return -1; + + if (! _cairo_device_is_observer (abstract_device)) + return -1; + + device = (cairo_device_observer_t *) abstract_device; + return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log)); +} + +double +cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device) +{ + cairo_device_observer_t *device; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) + return -1; + + if (! _cairo_device_is_observer (abstract_device)) + return -1; + + device = (cairo_device_observer_t *) abstract_device; + return _cairo_time_to_ns (device->log.paint.elapsed); +} + +double +cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device) +{ + cairo_device_observer_t *device; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) + return -1; + + if (! _cairo_device_is_observer (abstract_device)) + return -1; + + device = (cairo_device_observer_t *) abstract_device; + return _cairo_time_to_ns (device->log.mask.elapsed); +} + +double +cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device) +{ + cairo_device_observer_t *device; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) + return -1; + + if (! _cairo_device_is_observer (abstract_device)) + return -1; + + device = (cairo_device_observer_t *) abstract_device; + return _cairo_time_to_ns (device->log.fill.elapsed); +} + +double +cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device) +{ + cairo_device_observer_t *device; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) + return -1; + + if (! _cairo_device_is_observer (abstract_device)) + return -1; + + device = (cairo_device_observer_t *) abstract_device; + return _cairo_time_to_ns (device->log.stroke.elapsed); +} + +double +cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device) +{ + cairo_device_observer_t *device; + + if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count))) + return -1; + + if (! _cairo_device_is_observer (abstract_device)) + return -1; + + device = (cairo_device_observer_t *) abstract_device; + return _cairo_time_to_ns (device->log.glyphs.elapsed); +} diff --git a/gfx/cairo/cairo/src/cairo-surface-offset-private.h b/gfx/cairo/cairo/src/cairo-surface-offset-private.h index b7877b3de4fe..310ba5691cdc 100644 --- a/gfx/cairo/cairo/src/cairo-surface-offset-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-offset-private.h @@ -48,7 +48,7 @@ _cairo_surface_offset_paint (cairo_surface_t *target, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_mask (cairo_surface_t *target, @@ -56,31 +56,31 @@ _cairo_surface_offset_mask (cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_stroke (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_fill (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t*source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_offset_glyphs (cairo_surface_t *surface, @@ -90,6 +90,6 @@ _cairo_surface_offset_glyphs (cairo_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, - cairo_clip_t *clip); + const cairo_clip_t *clip); #endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-offset.c b/gfx/cairo/cairo/src/cairo-surface-offset.c index fdbe1a124bed..98f57f298041 100644 --- a/gfx/cairo/cairo/src/cairo-surface-offset.c +++ b/gfx/cairo/cairo/src/cairo-surface-offset.c @@ -37,7 +37,9 @@ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-error-private.h" +#include "cairo-pattern-private.h" #include "cairo-surface-offset-private.h" /* A collection of routines to facilitate drawing to an alternate surface. */ @@ -58,29 +60,22 @@ _cairo_surface_offset_paint (cairo_surface_t *target, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; if (unlikely (target->status)) return target->status; - if (clip && clip->all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } + dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); @@ -89,9 +84,8 @@ _cairo_surface_offset_paint (cairo_surface_t *target, status = _cairo_surface_paint (target, op, source, dev_clip); - FINISH: if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -102,30 +96,23 @@ _cairo_surface_offset_mask (cairo_surface_t *target, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; cairo_pattern_union_t mask_copy; if (unlikely (target->status)) return target->status; - if (clip && clip->all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } + dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); @@ -138,9 +125,8 @@ _cairo_surface_offset_mask (cairo_surface_t *target, source, mask, dev_clip); - FINISH: if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -150,16 +136,16 @@ _cairo_surface_offset_stroke (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_matrix_t dev_ctm = *ctm; cairo_matrix_t dev_ctm_inverse = *ctm_inverse; cairo_pattern_union_t source_copy; @@ -168,12 +154,14 @@ _cairo_surface_offset_stroke (cairo_surface_t *surface, if (unlikely (surface->status)) return surface->status; - if (clip && clip->all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; + dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; @@ -185,13 +173,6 @@ _cairo_surface_offset_stroke (cairo_surface_t *surface, cairo_matrix_init_translate (&m, -x, -y); cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); @@ -205,11 +186,11 @@ _cairo_surface_offset_stroke (cairo_surface_t *surface, tolerance, antialias, dev_clip); - FINISH: +FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -219,26 +200,28 @@ _cairo_surface_offset_fill (cairo_surface_t *surface, int x, int y, cairo_operator_t op, const cairo_pattern_t*source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; if (unlikely (surface->status)) return surface->status; - if (clip && clip->all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (x | y) { cairo_matrix_t m; + dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) goto FINISH; @@ -248,15 +231,6 @@ _cairo_surface_offset_fill (cairo_surface_t *surface, _cairo_fixed_from_int (-y)); dev_path = &path_copy; - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; @@ -267,11 +241,11 @@ _cairo_surface_offset_fill (cairo_surface_t *surface, tolerance, antialias, dev_clip); - FINISH: +FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -284,10 +258,10 @@ _cairo_surface_offset_glyphs (cairo_surface_t *surface, cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; cairo_pattern_union_t source_copy; cairo_glyph_t *dev_glyphs; int i; @@ -295,7 +269,7 @@ _cairo_surface_offset_glyphs (cairo_surface_t *surface, if (unlikely (surface->status)) return surface->status; - if (clip && clip->all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); @@ -307,14 +281,7 @@ _cairo_surface_offset_glyphs (cairo_surface_t *surface, if (x | y) { cairo_matrix_t m; - if (clip != NULL) { - cairo_matrix_init_translate (&m, -x, -y); - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } + dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y); cairo_matrix_init_translate (&m, x, y); _copy_transformed_pattern (&source_copy.base, source, &m); @@ -333,9 +300,8 @@ _cairo_surface_offset_glyphs (cairo_surface_t *surface, scaled_font, dev_clip); - FINISH: if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); free (dev_glyphs); return status; diff --git a/gfx/cairo/cairo/src/cairo-surface-private.h b/gfx/cairo/cairo/src/cairo-surface-private.h index 61acf4b05f3d..e4ad5f3b18d2 100644 --- a/gfx/cairo/cairo/src/cairo-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-private.h @@ -44,6 +44,9 @@ #include "cairo-list-private.h" #include "cairo-reference-count-private.h" #include "cairo-clip-private.h" +#include "cairo-surface-backend-private.h" + +typedef void (*cairo_surface_func_t) (cairo_surface_t *); struct _cairo_surface { const cairo_surface_backend_t *backend; @@ -59,12 +62,15 @@ struct _cairo_surface { cairo_reference_count_t ref_count; cairo_status_t status; unsigned int unique_id; + unsigned int serial; + cairo_damage_t *damage; + unsigned _finishing : 1; unsigned finished : 1; unsigned is_clear : 1; unsigned has_font_options : 1; unsigned owns_device : 1; - unsigned permit_subpixel_antialiasing : 1; + unsigned is_vector : 1; cairo_user_data_array_t user_data; cairo_user_data_array_t mime_data; @@ -100,4 +106,17 @@ struct _cairo_surface { cairo_font_options_t font_options; }; +cairo_private cairo_surface_t * +_cairo_surface_create_in_error (cairo_status_t status); + +cairo_private cairo_surface_t * +_cairo_int_surface_create_in_error (cairo_int_status_t status); + +cairo_private cairo_surface_t * +_cairo_surface_get_source (cairo_surface_t *surface, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_surface_flush (cairo_surface_t *surface, unsigned flags); + #endif /* CAIRO_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-snapshot-inline.h b/gfx/cairo/cairo/src/cairo-surface-snapshot-inline.h new file mode 100644 index 000000000000..bf89c772b429 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-snapshot-inline.h @@ -0,0 +1,67 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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 Intel Corporation. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_SNAPSHOT_INLINE_H +#define CAIRO_SURFACE_SNAPSHOT_INLINE_H + +#include "cairo-surface-snapshot-private.h" +#include "cairo-surface-inline.h" + +static inline cairo_bool_t +_cairo_surface_snapshot_is_reused (cairo_surface_t *surface) +{ + return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2; +} + +static inline cairo_surface_t * +_cairo_surface_snapshot_get_target (cairo_surface_t *surface) +{ + cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; + cairo_surface_t *target; + + CAIRO_MUTEX_LOCK (snapshot->mutex); + target = _cairo_surface_reference (snapshot->target); + CAIRO_MUTEX_UNLOCK (snapshot->mutex); + + return target; +} + +static inline cairo_bool_t +_cairo_surface_is_snapshot (cairo_surface_t *surface) +{ + return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT; +} + +#endif /* CAIRO_SURFACE_SNAPSHOT_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-snapshot-private.h b/gfx/cairo/cairo/src/cairo-surface-snapshot-private.h index bbb2bf2a0a9f..58bee7b49dd1 100644 --- a/gfx/cairo/cairo/src/cairo-surface-snapshot-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-snapshot-private.h @@ -36,11 +36,14 @@ #ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H #define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H +#include "cairo-mutex-private.h" #include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" struct _cairo_surface_snapshot { cairo_surface_t base; + cairo_mutex_t mutex; cairo_surface_t *target; cairo_surface_t *clone; }; diff --git a/gfx/cairo/cairo/src/cairo-surface-snapshot.c b/gfx/cairo/cairo/src/cairo-surface-snapshot.c index 2dbf2507ab36..b2908f6bc523 100644 --- a/gfx/cairo/cairo/src/cairo-surface-snapshot.c +++ b/gfx/cairo/cairo/src/cairo-surface-snapshot.c @@ -40,7 +40,8 @@ #include "cairoint.h" #include "cairo-error-private.h" -#include "cairo-surface-snapshot-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-snapshot-inline.h" static cairo_status_t _cairo_surface_snapshot_finish (void *abstract_surface) @@ -48,6 +49,8 @@ _cairo_surface_snapshot_finish (void *abstract_surface) cairo_surface_snapshot_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (surface->clone != NULL) { cairo_surface_finish (surface->clone); status = surface->clone->status; @@ -55,27 +58,77 @@ _cairo_surface_snapshot_finish (void *abstract_surface) cairo_surface_destroy (surface->clone); } + CAIRO_MUTEX_FINI (surface->mutex); + return status; } +static cairo_status_t +_cairo_surface_snapshot_flush (void *abstract_surface, unsigned flags) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + cairo_surface_t *target; + cairo_status_t status; + + target = _cairo_surface_snapshot_get_target (&surface->base); + status = target->status; + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_surface_flush (target, flags); + cairo_surface_destroy (target); + + return status; +} + +static cairo_surface_t * +_cairo_surface_snapshot_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + return _cairo_surface_get_source (surface->target, extents); /* XXX racy */ +} + +struct snapshot_extra { + cairo_surface_t *target; + void *extra; +}; + static cairo_status_t _cairo_surface_snapshot_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **extra_out) { cairo_surface_snapshot_t *surface = abstract_surface; + struct snapshot_extra *extra; + cairo_status_t status; - return _cairo_surface_acquire_source_image (surface->target, image_out, extra_out); + extra = _cairo_malloc (sizeof (*extra)); + if (unlikely (extra == NULL)) { + *extra_out = NULL; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + extra->target = _cairo_surface_snapshot_get_target (&surface->base); + status = _cairo_surface_acquire_source_image (extra->target, image_out, &extra->extra); + if (unlikely (status)) { + cairo_surface_destroy (extra->target); + free (extra); + extra = NULL; + } + + *extra_out = extra; + return status; } static void _cairo_surface_snapshot_release_source_image (void *abstract_surface, cairo_image_surface_t *image, - void *extra) + void *_extra) { - cairo_surface_snapshot_t *surface = abstract_surface; + struct snapshot_extra *extra = _extra; - _cairo_surface_release_source_image (surface->target, image, extra); + _cairo_surface_release_source_image (extra->target, image, extra->extra); + cairo_surface_destroy (extra->target); + free (extra); } static cairo_bool_t @@ -83,28 +136,38 @@ _cairo_surface_snapshot_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { cairo_surface_snapshot_t *surface = abstract_surface; + cairo_surface_t *target; + cairo_bool_t bounded; - return _cairo_surface_get_extents (surface->target, extents); + target = _cairo_surface_snapshot_get_target (&surface->base); + bounded = _cairo_surface_get_extents (target, extents); + cairo_surface_destroy (target); + + return bounded; } static const cairo_surface_backend_t _cairo_surface_snapshot_backend = { CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT, + _cairo_surface_snapshot_finish, + NULL, NULL, /* create similar */ - _cairo_surface_snapshot_finish, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + _cairo_surface_snapshot_source, _cairo_surface_snapshot_acquire_source_image, _cairo_surface_snapshot_release_source_image, - NULL, NULL, /* acquire, release dest */ - NULL, /* clone similar */ - NULL, /* composite */ - NULL, /* fill rectangles */ - NULL, /* composite trapezoids */ - NULL, /* create span renderer */ - NULL, /* check span renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_surface_snapshot_get_extents, + NULL, /* get-font-options */ + + _cairo_surface_snapshot_flush, }; static void @@ -112,55 +175,52 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) { cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; cairo_image_surface_t *image; - cairo_image_surface_t *clone; + cairo_surface_t *clone; void *extra; cairo_status_t status; + TRACE ((stderr, "%s: target=%d\n", + __FUNCTION__, snapshot->target->unique_id)); + /* We need to make an image copy of the original surface since the * snapshot may exceed the lifetime of the original device, i.e. * when we later need to use the snapshot the data may have already * been lost. */ + CAIRO_MUTEX_LOCK (snapshot->mutex); + + if (snapshot->target->backend->snapshot != NULL) { + clone = snapshot->target->backend->snapshot (snapshot->target); + if (clone != NULL) { + assert (clone->status || ! _cairo_surface_is_snapshot (clone)); + goto done; + } + } + + /* XXX copy to a similar surface, leave acquisition till later? + * We should probably leave such decisions to the backend in case we + * rely upon devices/connections like Xlib. + */ status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra); if (unlikely (status)) { snapshot->target = _cairo_surface_create_in_error (status); status = _cairo_surface_set_error (surface, status); - return; + goto unlock; } - - clone = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - image->pixman_format, - image->width, - image->height, - 0); - if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) { - if (clone->stride == image->stride) { - memcpy (clone->data, image->data, image->stride * image->height); - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - image->pixman_image, NULL, clone->pixman_image, - 0, 0, - 0, 0, - 0, 0, - image->width, image->height); - } - clone->base.is_clear = FALSE; - - snapshot->clone = &clone->base; - } else { - snapshot->clone = &clone->base; - status = _cairo_surface_set_error (surface, clone->base.status); - } - + clone = image->base.backend->snapshot (&image->base); _cairo_surface_release_source_image (snapshot->target, image, extra); - snapshot->target = snapshot->clone; - snapshot->base.type = snapshot->target->type; + +done: + status = _cairo_surface_set_error (surface, clone->status); + snapshot->target = snapshot->clone = clone; + snapshot->base.type = clone->type; +unlock: + CAIRO_MUTEX_UNLOCK (snapshot->mutex); } /** - * _cairo_surface_snapshot + * _cairo_surface_snapshot: * @surface: a #cairo_surface_t * * Make an immutable reference to @surface. It is an error to call a @@ -184,57 +244,37 @@ _cairo_surface_snapshot (cairo_surface_t *surface) cairo_surface_snapshot_t *snapshot; cairo_status_t status; + TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->unique_id)); + if (unlikely (surface->status)) return _cairo_surface_create_in_error (surface->status); - if (surface->finished) + if (unlikely (surface->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (surface->snapshot_of != NULL) return cairo_surface_reference (surface); - if (surface->backend->snapshot != NULL) { - cairo_surface_t *snap; - - snap = _cairo_surface_has_snapshot (surface, surface->backend); - if (snap != NULL) - return cairo_surface_reference (snap); - - snap = surface->backend->snapshot (surface); - if (snap != NULL) { - if (unlikely (snap->status)) - return snap; - - status = _cairo_surface_copy_mime_data (snap, surface); - if (unlikely (status)) { - cairo_surface_destroy (snap); - return _cairo_surface_create_in_error (status); - } - - snap->device_transform = surface->device_transform; - snap->device_transform_inverse = surface->device_transform_inverse; - - cairo_surface_attach_snapshot (surface, snap, NULL); - - return snap; - } - } + if (_cairo_surface_is_snapshot (surface)) + return cairo_surface_reference (surface); snapshot = (cairo_surface_snapshot_t *) _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend); if (snapshot != NULL) return cairo_surface_reference (&snapshot->base); - snapshot = malloc (sizeof (cairo_surface_snapshot_t)); + snapshot = _cairo_malloc (sizeof (cairo_surface_snapshot_t)); if (unlikely (snapshot == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); _cairo_surface_init (&snapshot->base, &_cairo_surface_snapshot_backend, NULL, /* device */ - surface->content); + surface->content, + surface->is_vector); snapshot->base.type = surface->type; + CAIRO_MUTEX_INIT (snapshot->mutex); snapshot->target = surface; snapshot->clone = NULL; @@ -247,7 +287,7 @@ _cairo_surface_snapshot (cairo_surface_t *surface) snapshot->base.device_transform = surface->device_transform; snapshot->base.device_transform_inverse = surface->device_transform_inverse; - cairo_surface_attach_snapshot (surface, + _cairo_surface_attach_snapshot (surface, &snapshot->base, _cairo_surface_snapshot_copy_on_write); diff --git a/gfx/cairo/cairo/src/cairo-surface-subsurface-inline.h b/gfx/cairo/cairo/src/cairo-surface-subsurface-inline.h new file mode 100644 index 000000000000..0cd09e63e703 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-subsurface-inline.h @@ -0,0 +1,72 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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 Intel Corporation. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_SUBSURFACE_INLINE_H +#define CAIRO_SURFACE_SUBSURFACE_INLINE_H + +#include "cairo-surface-subsurface-private.h" + +static inline cairo_surface_t * +_cairo_surface_subsurface_get_target (cairo_surface_t *surface) +{ + return ((cairo_surface_subsurface_t *) surface)->target; +} + +static inline void +_cairo_surface_subsurface_offset (cairo_surface_t *surface, + int *x, int *y) +{ + cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; + *x += ss->extents.x; + *y += ss->extents.y; +} + +static inline cairo_surface_t * +_cairo_surface_subsurface_get_target_with_offset (cairo_surface_t *surface, + int *x, int *y) +{ + cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; + *x += ss->extents.x; + *y += ss->extents.y; + return ss->target; +} + +static inline cairo_bool_t +_cairo_surface_is_subsurface (cairo_surface_t *surface) +{ + return surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE; +} + +#endif /* CAIRO_SURFACE_SUBSURFACE_INLINE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h b/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h index 435e1eb83184..89c5cc01b9d3 100644 --- a/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h @@ -37,6 +37,7 @@ #define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H #include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" struct _cairo_surface_subsurface { cairo_surface_t base; @@ -44,6 +45,11 @@ struct _cairo_surface_subsurface { cairo_rectangle_int_t extents; cairo_surface_t *target; + cairo_surface_t *snapshot; }; +cairo_private void +_cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot); + #endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-subsurface.c b/gfx/cairo/cairo/src/cairo-surface-subsurface.c index 1789662630de..b2a10e9bce3d 100644 --- a/gfx/cairo/cairo/src/cairo-surface-subsurface.c +++ b/gfx/cairo/cairo/src/cairo-surface-subsurface.c @@ -35,9 +35,12 @@ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-offset-private.h" +#include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" static const cairo_surface_backend_t _cairo_surface_subsurface_backend; @@ -48,6 +51,7 @@ _cairo_surface_subsurface_finish (void *abstract_surface) cairo_surface_subsurface_t *surface = abstract_surface; cairo_surface_destroy (surface->target); + cairo_surface_destroy (surface->snapshot); return CAIRO_STATUS_SUCCESS; } @@ -58,30 +62,67 @@ _cairo_surface_subsurface_create_similar (void *other, int width, int height) { cairo_surface_subsurface_t *surface = other; + + if (surface->target->backend->create_similar == NULL) + return NULL; + return surface->target->backend->create_similar (surface->target, content, width, height); } +static cairo_surface_t * +_cairo_surface_subsurface_create_similar_image (void *other, + cairo_format_t format, + int width, int height) +{ + cairo_surface_subsurface_t *surface = other; + + if (surface->target->backend->create_similar_image == NULL) + return NULL; + + return surface->target->backend->create_similar_image (surface->target, + format, + width, height); +} + +static cairo_image_surface_t * +_cairo_surface_subsurface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_rectangle_int_t target_extents; + + target_extents.x = extents->x + surface->extents.x; + target_extents.y = extents->y + surface->extents.y; + target_extents.width = extents->width; + target_extents.height = extents->height; + + return _cairo_surface_map_to_image (surface->target, &target_extents); +} + +static cairo_int_status_t +_cairo_surface_subsurface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + return _cairo_surface_unmap_image (surface->target, image); +} + static cairo_int_status_t _cairo_surface_subsurface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; + cairo_clip_t *target_clip; + target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_paint (surface->target, -surface->extents.x, -surface->extents.y, - op, source, &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); + op, source, target_clip); + _cairo_clip_destroy (target_clip); return status; } @@ -90,23 +131,18 @@ _cairo_surface_subsurface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; + cairo_clip_t *target_clip; + target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_mask (surface->target, -surface->extents.x, -surface->extents.y, - op, source, mask, &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); + op, source, mask, target_clip); + _cairo_clip_destroy (target_clip); return status; } @@ -114,28 +150,23 @@ static cairo_int_status_t _cairo_surface_subsurface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; + cairo_clip_t *target_clip; + target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_fill (surface->target, -surface->extents.x, -surface->extents.y, op, source, path, fill_rule, tolerance, antialias, - &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); + target_clip); + _cairo_clip_destroy (target_clip); return status; } @@ -143,31 +174,26 @@ static cairo_int_status_t _cairo_surface_subsurface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; + cairo_clip_t *target_clip; + target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_stroke (surface->target, -surface->extents.x, -surface->extents.y, op, source, path, stroke_style, ctm, ctm_inverse, tolerance, antialias, - &target_clip); - CLEANUP: - _cairo_clip_fini (&target_clip); + target_clip); + _cairo_clip_destroy (target_clip); return status; } @@ -178,41 +204,28 @@ _cairo_surface_subsurface_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { cairo_surface_subsurface_t *surface = abstract_surface; cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height }; cairo_status_t status; - cairo_clip_t target_clip; - - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &rect); - if (unlikely (status)) - goto CLEANUP; + cairo_clip_t *target_clip; + target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect); status = _cairo_surface_offset_glyphs (surface->target, -surface->extents.x, -surface->extents.y, op, source, scaled_font, glyphs, num_glyphs, - &target_clip); - *remaining_glyphs = 0; - CLEANUP: - _cairo_clip_fini (&target_clip); + target_clip); + _cairo_clip_destroy (target_clip); return status; } static cairo_status_t -_cairo_surface_subsurface_flush (void *abstract_surface) +_cairo_surface_subsurface_flush (void *abstract_surface, unsigned flags) { cairo_surface_subsurface_t *surface = abstract_surface; - cairo_status_t status; - - status = CAIRO_STATUS_SUCCESS; - if (surface->target->backend->flush != NULL) - status = surface->target->backend->flush (surface->target); - - return status; + return _cairo_surface_flush (surface->target, flags); } static cairo_status_t @@ -271,27 +284,18 @@ _cairo_surface_subsurface_get_font_options (void *abstract_surface, surface->target->backend->get_font_options (surface->target, options); } -struct extra { - cairo_image_surface_t *image; - void *image_extra; -}; - -static void -cairo_surface_paint_to_target (cairo_surface_t *target, - cairo_surface_subsurface_t *subsurface) +static cairo_surface_t * +_cairo_surface_subsurface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) { - cairo_t *cr; - - cr = cairo_create (target); + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_surface_t *source; - cairo_set_source_surface (cr, - subsurface->target, - - subsurface->extents.x, - - subsurface->extents.y); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - - cairo_destroy (cr); + source = _cairo_surface_get_source (surface->target, extents); + if (extents) + *extents = surface->extents; + + return source; } static cairo_status_t @@ -299,101 +303,34 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac cairo_image_surface_t **image_out, void **extra_out) { - cairo_rectangle_int_t target_extents; cairo_surface_subsurface_t *surface = abstract_surface; - cairo_image_surface_t *image; + cairo_surface_pattern_t pattern; + cairo_surface_t *image; cairo_status_t status; - struct extra *extra; - uint8_t *data; - cairo_bool_t ret; - if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) { - cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target; - cairo_surface_t *snapshot; + image = _cairo_image_surface_create_with_content (surface->base.content, + surface->extents.width, + surface->extents.height); + if (unlikely (image->status)) + return image->status; - snapshot = _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend); - if (snapshot != NULL) { - *image_out = (cairo_image_surface_t *) cairo_surface_reference (snapshot); - *extra_out = NULL; - return CAIRO_STATUS_SUCCESS; - } - - if (! _cairo_surface_has_snapshot (&meta->base, - &_cairo_image_surface_backend)) - { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_content (meta->content, - surface->extents.width, - surface->extents.height); - if (unlikely (image->base.status)) - return image->base.status; - - cairo_surface_paint_to_target (&image->base, surface); - - cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); - - *image_out = image; - *extra_out = NULL; - return CAIRO_STATUS_SUCCESS; - } + _cairo_pattern_init_for_surface (&pattern, surface->target); + cairo_matrix_init_translate (&pattern.base.matrix, + surface->extents.x, + surface->extents.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (image, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; } - extra = malloc (sizeof (struct extra)); - if (unlikely (extra == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_surface_acquire_source_image (surface->target, &extra->image, &extra->image_extra); - if (unlikely (status)) - goto CLEANUP; - - ret = _cairo_surface_get_extents (&extra->image->base, &target_extents); - assert (ret); - - /* only copy if we need to perform sub-byte manipulation */ - if (PIXMAN_FORMAT_BPP (extra->image->pixman_format) >= 8 && - target_extents.x <= surface->extents.x && - target_extents.y <= surface->extents.y && - surface->extents.x + surface->extents.width <= target_extents.x + target_extents.width && - surface->extents.y + surface->extents.height <= target_extents.y + target_extents.height) { - - assert ((PIXMAN_FORMAT_BPP (extra->image->pixman_format) % 8) == 0); - - data = extra->image->data + surface->extents.y * extra->image->stride; - data += PIXMAN_FORMAT_BPP (extra->image->pixman_format) / 8 * surface->extents.x; - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (data, - extra->image->pixman_format, - surface->extents.width, - surface->extents.height, - extra->image->stride); - if (unlikely ((status = image->base.status))) - goto CLEANUP_IMAGE; - - image->base.is_clear = FALSE; - } else { - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - extra->image->pixman_format, - surface->extents.width, - surface->extents.height, - 0); - if (unlikely ((status = image->base.status))) - goto CLEANUP_IMAGE; - - cairo_surface_paint_to_target (&image->base, surface); - } - - *image_out = image; - *extra_out = extra; + *image_out = (cairo_image_surface_t *)image; + *extra_out = NULL; return CAIRO_STATUS_SUCCESS; - -CLEANUP_IMAGE: - _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra); -CLEANUP: - free (extra); - return status; } static void @@ -401,15 +338,6 @@ _cairo_surface_subsurface_release_source_image (void *abstract cairo_image_surface_t *image, void *abstract_extra) { - cairo_surface_subsurface_t *surface = abstract_surface; - - if (abstract_extra != NULL) { - struct extra *extra = abstract_extra; - - _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra); - free (extra); - } - cairo_surface_destroy (&image->base); } @@ -417,62 +345,75 @@ static cairo_surface_t * _cairo_surface_subsurface_snapshot (void *abstract_surface) { cairo_surface_subsurface_t *surface = abstract_surface; - cairo_surface_subsurface_t *snapshot; + cairo_surface_pattern_t pattern; + cairo_surface_t *clone; + cairo_status_t status; - snapshot = malloc (sizeof (cairo_surface_subsurface_t)); - if (unlikely (snapshot == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->target->unique_id)); - _cairo_surface_init (&snapshot->base, - &_cairo_surface_subsurface_backend, - NULL, /* device */ - surface->target->content); - snapshot->target = _cairo_surface_snapshot (surface->target); - if (unlikely (snapshot->target->status)) { - cairo_status_t status; + clone = _cairo_surface_create_scratch (surface->target, + surface->target->content, + surface->extents.width, + surface->extents.height, + NULL); + if (unlikely (clone->status)) + return clone; - status = snapshot->target->status; - free (snapshot); - return _cairo_surface_create_in_error (status); + _cairo_pattern_init_for_surface (&pattern, surface->target); + cairo_matrix_init_translate (&pattern.base.matrix, + surface->extents.x, surface->extents.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (clone, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (clone); + clone = _cairo_surface_create_in_error (status); } - snapshot->base.type = snapshot->target->type; - snapshot->extents = surface->extents; + return clone; +} - return &snapshot->base; +static cairo_t * +_cairo_surface_subsurface_create_context(void *target) +{ + cairo_surface_subsurface_t *surface = target; + return surface->target->backend->create_context (&surface->base); } static const cairo_surface_backend_t _cairo_surface_subsurface_backend = { CAIRO_SURFACE_TYPE_SUBSURFACE, - _cairo_surface_subsurface_create_similar, _cairo_surface_subsurface_finish, + _cairo_surface_subsurface_create_context, + + _cairo_surface_subsurface_create_similar, + _cairo_surface_subsurface_create_similar_image, + _cairo_surface_subsurface_map_to_image, + _cairo_surface_subsurface_unmap_image, + + _cairo_surface_subsurface_source, _cairo_surface_subsurface_acquire_source_image, _cairo_surface_subsurface_release_source_image, - NULL, NULL, /* acquire, release dest */ - NULL, /* clone similar */ - NULL, /* composite */ - NULL, /* fill rectangles */ - NULL, /* composite trapezoids */ - NULL, /* create span renderer */ - NULL, /* check span renderer */ + _cairo_surface_subsurface_snapshot, + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_surface_subsurface_get_extents, - NULL, /* old_show_glyphs */ _cairo_surface_subsurface_get_font_options, + _cairo_surface_subsurface_flush, _cairo_surface_subsurface_mark_dirty, - NULL, /* font_fini */ - NULL, /* glyph_fini */ _cairo_surface_subsurface_paint, _cairo_surface_subsurface_mask, _cairo_surface_subsurface_stroke, _cairo_surface_subsurface_fill, + NULL, /* fill/stroke */ _cairo_surface_subsurface_glyphs, - - _cairo_surface_subsurface_snapshot, }; /** @@ -513,29 +454,40 @@ cairo_surface_create_for_rectangle (cairo_surface_t *target, { cairo_surface_subsurface_t *surface; + if (unlikely (width < 0 || height < 0)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (unlikely (target->status)) return _cairo_surface_create_in_error (target->status); if (unlikely (target->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - surface = malloc (sizeof (cairo_surface_subsurface_t)); + surface = _cairo_malloc (sizeof (cairo_surface_subsurface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - assert (_cairo_matrix_is_translation (&target->device_transform)); + x *= target->device_transform.xx; + y *= target->device_transform.yy; + + width *= target->device_transform.xx; + height *= target->device_transform.yy; + x += target->device_transform.x0; y += target->device_transform.y0; _cairo_surface_init (&surface->base, &_cairo_surface_subsurface_backend, NULL, /* device */ - target->content); + target->content, + target->is_vector); /* XXX forced integer alignment */ surface->extents.x = ceil (x); surface->extents.y = ceil (y); surface->extents.width = floor (x + width) - surface->extents.x; surface->extents.height = floor (y + height) - surface->extents.y; + if ((surface->extents.width | surface->extents.height) < 0) + surface->extents.width = surface->extents.height = 0; if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { /* Maintain subsurfaces as 1-depth */ @@ -546,7 +498,92 @@ cairo_surface_create_for_rectangle (cairo_surface_t *target, } surface->target = cairo_surface_reference (target); + surface->base.type = surface->target->type; + + surface->snapshot = NULL; + + cairo_surface_set_device_scale (&surface->base, + target->device_transform.xx, + target->device_transform.yy); + + return &surface->base; +} + +cairo_surface_t * +_cairo_surface_create_for_rectangle_int (cairo_surface_t *target, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_subsurface_t *surface; + + if (unlikely (target->status)) + return _cairo_surface_create_in_error (target->status); + if (unlikely (target->finished)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE); + + surface = _cairo_malloc (sizeof (cairo_surface_subsurface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_surface_subsurface_backend, + NULL, /* device */ + target->content, + target->is_vector); + + surface->extents = *extents; + surface->extents.x *= target->device_transform.xx; + surface->extents.y *= target->device_transform.yy; + surface->extents.width *= target->device_transform.xx; + surface->extents.height *= target->device_transform.yy; + surface->extents.x += target->device_transform.x0; + surface->extents.y += target->device_transform.y0; + + surface->target = cairo_surface_reference (target); + surface->base.type = surface->target->type; + + surface->snapshot = NULL; + + cairo_surface_set_device_scale (&surface->base, + target->device_transform.xx, + target->device_transform.yy); return &surface->base; } /* XXX observe mark-dirty */ + +static void +_cairo_surface_subsurface_detach_snapshot (cairo_surface_t *surface) +{ + cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; + + TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, ss->target->unique_id)); + + cairo_surface_destroy (ss->snapshot); + ss->snapshot = NULL; +} + +void +_cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot) +{ + cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface; + + TRACE ((stderr, "%s: target=%d, snapshot=%d\n", __FUNCTION__, + ss->target->unique_id, snapshot->unique_id)); + + /* FIXME: attaching the subsurface as a snapshot to its target creates + * a reference cycle. Let's make this call as a no-op until that bug + * is fixed. + */ + return; + + if (ss->snapshot) + _cairo_surface_detach_snapshot (ss->snapshot); + + ss->snapshot = cairo_surface_reference (snapshot); + + _cairo_surface_attach_snapshot (ss->target, &ss->base, + _cairo_surface_subsurface_detach_snapshot); +} diff --git a/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h b/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h index a1f05d386b23..fd22bd7e6c2c 100644 --- a/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h +++ b/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h @@ -39,15 +39,22 @@ #ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H #define CAIRO_SURFACE_WRAPPER_PRIVATE_H +#include "cairoint.h" #include "cairo-types-private.h" +#include "cairo-surface-backend-private.h" CAIRO_BEGIN_DECLS struct _cairo_surface_wrapper { cairo_surface_t *target; + cairo_matrix_t transform; + cairo_bool_t has_extents; cairo_rectangle_int_t extents; + const cairo_clip_t *clip; + + cairo_bool_t needs_transform; }; cairo_private void @@ -55,12 +62,26 @@ _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, cairo_surface_t *target); cairo_private void -_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper, - const cairo_rectangle_int_t *extents); +_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper, + const cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper, + const cairo_matrix_t *transform); + +cairo_private void +_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper, + const cairo_clip_t *clip); cairo_private void _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper); +static inline cairo_bool_t +_cairo_surface_wrapper_has_fill_stroke (cairo_surface_wrapper_t *wrapper) +{ + return wrapper->target->backend->fill_stroke != NULL; +} + cairo_private cairo_status_t _cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, cairo_image_surface_t **image_out, @@ -76,26 +97,26 @@ cairo_private cairo_status_t _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip); + 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, - cairo_clip_t *clip); + 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, - cairo_path_fixed_t *path, + 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, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, @@ -104,7 +125,7 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, @@ -112,17 +133,17 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_clip_t *clip); + 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, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, @@ -130,13 +151,24 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, const cairo_pattern_t *source, const char *utf8, int utf8_len, - cairo_glyph_t *glyphs, + const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_tag (cairo_surface_wrapper_t *wrapper, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip); cairo_private cairo_surface_t * _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, @@ -157,15 +189,17 @@ _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper); cairo_private cairo_bool_t _cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper); -cairo_private cairo_status_t -_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper); - static inline cairo_bool_t _cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper) { return wrapper->target != (cairo_surface_t *) 0; } +cairo_private cairo_bool_t +_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper, + cairo_bool_t surface_is_unbounded, + cairo_rectangle_int_t *extents); + CAIRO_END_DECLS #endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-wrapper.c b/gfx/cairo/cairo/src/cairo-surface-wrapper.c index d3f126e18cd0..47155c3f7998 100644 --- a/gfx/cairo/cairo/src/cairo-surface-wrapper.c +++ b/gfx/cairo/cairo/src/cairo-surface-wrapper.c @@ -37,7 +37,9 @@ #include "cairoint.h" +#include "cairo-clip-inline.h" #include "cairo-error-private.h" +#include "cairo-pattern-private.h" #include "cairo-surface-wrapper-private.h" /* A collection of routines to facilitate surface wrapping */ @@ -49,26 +51,10 @@ _copy_transformed_pattern (cairo_pattern_t *pattern, { _cairo_pattern_init_static_copy (pattern, original); - /* Device transform should already have been applied before cairo_surface_wrapper_* functions - * are called in _cairo_gstate_copy_transformed_pattern which all the gstate drawing - * functions call through _cairo_gstate_copy_transformed_source and such. */ - if (! _cairo_matrix_is_identity (ctm_inverse)) _cairo_pattern_transform (pattern, ctm_inverse); } -static inline cairo_bool_t -_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) -{ - return ! _cairo_matrix_is_identity (&wrapper->target->device_transform); -} - -static cairo_bool_t -_cairo_surface_wrapper_needs_extents_transform (cairo_surface_wrapper_t *wrapper) -{ - return wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y); -} - cairo_status_t _cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, cairo_image_surface_t **image_out, @@ -89,54 +75,79 @@ _cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, _cairo_surface_release_source_image (wrapper->target, image, image_extra); } +static void +_cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper, + cairo_matrix_t *m) +{ + cairo_matrix_init_identity (m); + + if (! _cairo_matrix_is_identity (&wrapper->transform)) + cairo_matrix_multiply (m, &wrapper->transform, m); + + if (! _cairo_matrix_is_identity (&wrapper->target->device_transform)) + cairo_matrix_multiply (m, &wrapper->target->device_transform, m); +} + +static void +_cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper, + cairo_matrix_t *m) +{ + cairo_matrix_init_identity (m); + + if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse)) + cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m); + + if (! _cairo_matrix_is_identity (&wrapper->transform)) { + cairo_matrix_t inv; + cairo_status_t status; + + inv = wrapper->transform; + status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (m, &inv, m); + } +} + +static cairo_clip_t * +_cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper, + const cairo_clip_t *clip) +{ + cairo_clip_t *copy; + cairo_matrix_t m; + + copy = _cairo_clip_copy (clip); + if (wrapper->has_extents) { + copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents); + } + _cairo_surface_wrapper_get_transform (wrapper, &m); + copy = _cairo_clip_transform (copy, &m); + if (wrapper->clip) + copy = _cairo_clip_intersect_clip (copy, wrapper->clip); + + return copy; +} + cairo_status_t _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_clip_t *dev_clip; cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; if (unlikely (wrapper->target->status)) return wrapper->target->status; - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { + if (wrapper->needs_transform) { cairo_matrix_t m; - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } + _cairo_surface_wrapper_get_transform (wrapper, &m); status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); @@ -147,64 +158,34 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, status = _cairo_surface_paint (wrapper->target, op, source, dev_clip); - FINISH: - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); 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, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_clip_t *dev_clip; cairo_pattern_union_t source_copy; cairo_pattern_union_t mask_copy; - cairo_clip_t target_clip; if (unlikely (wrapper->target->status)) return wrapper->target->status; - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { + if (wrapper->needs_transform) { cairo_matrix_t m; - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } + _cairo_surface_wrapper_get_transform (wrapper, &m); status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); @@ -218,11 +199,7 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip); - FINISH: - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -230,51 +207,32 @@ cairo_status_t _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; + cairo_clip_t *dev_clip; cairo_matrix_t dev_ctm = *ctm; cairo_matrix_t dev_ctm_inverse = *ctm_inverse; cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; if (unlikely (wrapper->target->status)) return wrapper->target->status; - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { + if (wrapper->needs_transform) { cairo_matrix_t m; - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + _cairo_surface_wrapper_get_transform (wrapper, &m); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) @@ -283,14 +241,6 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, _cairo_path_fixed_transform (&path_copy, &m); dev_path = &path_copy; - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); status = cairo_matrix_invert (&m); @@ -301,13 +251,6 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } status = _cairo_surface_stroke (wrapper->target, op, source, dev_path, stroke_style, @@ -318,10 +261,7 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -332,7 +272,7 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, @@ -340,46 +280,27 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path; cairo_matrix_t dev_ctm = *stroke_ctm; cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; + cairo_clip_t *dev_clip; cairo_pattern_union_t stroke_source_copy; cairo_pattern_union_t fill_source_copy; - cairo_clip_t target_clip; if (unlikely (wrapper->target->status)) return wrapper->target->status; - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { + if (wrapper->needs_transform) { cairo_matrix_t m; - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + _cairo_surface_wrapper_get_transform (wrapper, &m); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) @@ -388,14 +309,6 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, _cairo_path_fixed_transform (&path_copy, &m); dev_path = &path_copy; - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); status = cairo_matrix_invert (&m); @@ -409,13 +322,6 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m); fill_source = &fill_source_copy.base; } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } status = _cairo_surface_fill_stroke (wrapper->target, fill_op, fill_source, fill_rule, @@ -430,10 +336,7 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -441,47 +344,28 @@ cairo_status_t _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_path_fixed_t path_copy, *dev_path = path; - cairo_clip_t clip_copy, *dev_clip = clip; + cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; + cairo_clip_t *dev_clip; if (unlikely (wrapper->target->status)) return wrapper->target->status; - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { + if (wrapper->needs_transform) { cairo_matrix_t m; - cairo_matrix_init_identity (&m); - - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); - - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); + _cairo_surface_wrapper_get_transform (wrapper, &m); status = _cairo_path_fixed_init_copy (&path_copy, dev_path); if (unlikely (status)) @@ -490,27 +374,12 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, _cairo_path_fixed_transform (&path_copy, &m); dev_path = &path_copy; - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; - } - status = cairo_matrix_invert (&m); assert (status == CAIRO_STATUS_SUCCESS); _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); - } - } status = _cairo_surface_fill (wrapper->target, op, source, dev_path, fill_rule, @@ -520,10 +389,7 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, FINISH: if (dev_path != path) _cairo_path_fixed_fini (dev_path); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); + _cairo_clip_destroy (dev_clip); return status; } @@ -533,71 +399,62 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, const cairo_pattern_t *source, const char *utf8, int utf8_len, - cairo_glyph_t *glyphs, + const cairo_glyph_t *glyphs, int num_glyphs, const cairo_text_cluster_t *clusters, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; - cairo_clip_t clip_copy, *dev_clip = clip; - cairo_glyph_t *dev_glyphs = glyphs; + cairo_clip_t *dev_clip; + cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)]; + cairo_glyph_t *dev_glyphs = stack_glyphs; + cairo_scaled_font_t *dev_scaled_font = scaled_font; cairo_pattern_union_t source_copy; - cairo_clip_t target_clip; + cairo_font_options_t options; if (unlikely (wrapper->target->status)) return wrapper->target->status; - if (glyphs == NULL || num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (wrapper->has_extents) { - _cairo_clip_init_copy (&target_clip, clip); - status = _cairo_clip_rectangle (&target_clip, &wrapper->extents); - if (unlikely (status)) - goto FINISH; + cairo_surface_get_font_options (wrapper->target, &options); + cairo_font_options_merge (&options, &scaled_font->options); - dev_clip = clip = &target_clip; - } - - if (clip && clip->all_clipped) { - status = CAIRO_STATUS_SUCCESS; - goto FINISH; - } - - if (_cairo_surface_wrapper_needs_device_transform (wrapper) || - _cairo_surface_wrapper_needs_extents_transform (wrapper)) - { + if (wrapper->needs_transform) { cairo_matrix_t m; int i; - cairo_matrix_init_identity (&m); + _cairo_surface_wrapper_get_transform (wrapper, &m); - if (_cairo_surface_wrapper_needs_extents_transform (wrapper)) - cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y); + if (! _cairo_matrix_is_translation (&m)) { + cairo_matrix_t ctm; - if (_cairo_surface_wrapper_needs_device_transform (wrapper)) - cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m); - - if (clip != NULL) { - status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m); - if (unlikely (status)) - goto FINISH; - - dev_clip = &clip_copy; + _cairo_matrix_multiply (&ctm, + &m, + &scaled_font->ctm); + dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &ctm, &options); } - dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (dev_glyphs == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FINISH; + if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (dev_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FINISH; + } } for (i = 0; i < num_glyphs; i++) { dev_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y); + cairo_matrix_transform_point (&m, + &dev_glyphs[i].x, + &dev_glyphs[i].y); } status = cairo_matrix_invert (&m); @@ -605,13 +462,27 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, _copy_transformed_pattern (&source_copy.base, source, &m); source = &source_copy.base; - } - else - { - if (clip != NULL) { - dev_clip = &clip_copy; - _cairo_clip_init_copy (&clip_copy, clip); + } else { + if (! cairo_font_options_equal (&options, &scaled_font->options)) { + dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &scaled_font->ctm, + &options); } + + /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed + * to modify the glyph array that's passed in. We must always + * copy the array before handing it to the backend. + */ + if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) { + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (dev_glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FINISH; + } + } + + memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); } status = _cairo_surface_show_text_glyphs (wrapper->target, op, source, @@ -619,16 +490,61 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, dev_glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - scaled_font, + dev_scaled_font, dev_clip); - FINISH: - if (dev_clip != clip) - _cairo_clip_reset (dev_clip); - if (wrapper->has_extents) - _cairo_clip_reset (&target_clip); - if (dev_glyphs != glyphs) + _cairo_clip_destroy (dev_clip); + if (dev_glyphs != stack_glyphs) free (dev_glyphs); + if (dev_scaled_font != scaled_font) + cairo_scaled_font_destroy (dev_scaled_font); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_tag (cairo_surface_wrapper_t *wrapper, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t *dev_clip; + cairo_matrix_t dev_ctm = *ctm; + cairo_matrix_t dev_ctm_inverse = *ctm_inverse; + cairo_pattern_union_t source_copy; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (wrapper->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_tag (wrapper->target, + begin, tag_name, attributes, + source, stroke_style, + &dev_ctm, &dev_ctm_inverse, + dev_clip); + + _cairo_clip_destroy (dev_clip); return status; } @@ -638,8 +554,8 @@ _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, int width, int height) { - return _cairo_surface_create_similar_scratch (wrapper->target, - content, width, height); + return _cairo_surface_create_scratch (wrapper->target, + content, width, height, NULL); } cairo_bool_t @@ -658,18 +574,57 @@ _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, } } -void -_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper, - const cairo_rectangle_int_t *extents) +static cairo_bool_t +_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) { - if (extents != NULL) { + return + (wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) || + ! _cairo_matrix_is_identity (&wrapper->transform) || + ! _cairo_matrix_is_identity (&wrapper->target->device_transform); +} + +void +_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper, + const cairo_rectangle_int_t *extents) +{ + if (! wrapper->has_extents) { wrapper->extents = *extents; wrapper->has_extents = TRUE; + } else + _cairo_rectangle_intersect (&wrapper->extents, extents); + + wrapper->needs_transform = + _cairo_surface_wrapper_needs_device_transform (wrapper); +} + +void +_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper, + const cairo_matrix_t *transform) +{ + cairo_status_t status; + + if (transform == NULL || _cairo_matrix_is_identity (transform)) { + cairo_matrix_init_identity (&wrapper->transform); + + wrapper->needs_transform = + _cairo_surface_wrapper_needs_device_transform (wrapper); } else { - wrapper->has_extents = FALSE; + wrapper->transform = *transform; + status = cairo_matrix_invert (&wrapper->transform); + /* should always be invertible unless given pathological input */ + assert (status == CAIRO_STATUS_SUCCESS); + + wrapper->needs_transform = TRUE; } } +void +_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper, + const cairo_clip_t *clip) +{ + wrapper->clip = clip; +} + void _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, cairo_font_options_t *options) @@ -680,7 +635,10 @@ _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, cairo_surface_t * _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper) { - return _cairo_surface_snapshot (wrapper->target); + if (wrapper->target->backend->snapshot) + return wrapper->target->backend->snapshot (wrapper->target); + + return NULL; } cairo_bool_t @@ -694,7 +652,16 @@ _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, cairo_surface_t *target) { wrapper->target = cairo_surface_reference (target); + cairo_matrix_init_identity (&wrapper->transform); wrapper->has_extents = FALSE; + wrapper->extents.x = wrapper->extents.y = 0; + wrapper->clip = NULL; + + wrapper->needs_transform = FALSE; + if (target) { + wrapper->needs_transform = + ! _cairo_matrix_is_identity (&target->device_transform); + } } void @@ -703,11 +670,60 @@ _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) cairo_surface_destroy (wrapper->target); } -cairo_status_t -_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper) +cairo_bool_t +_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper, + cairo_bool_t surface_is_unbounded, + cairo_rectangle_int_t *extents) { - if (wrapper->target->backend->flush) { - return wrapper->target->backend->flush(wrapper->target); + cairo_rectangle_int_t clip; + cairo_bool_t has_clip = FALSE; + + if (!surface_is_unbounded) + has_clip = _cairo_surface_get_extents (wrapper->target, &clip); + + if (wrapper->clip) { + if (has_clip) { + if (! _cairo_rectangle_intersect (&clip, + _cairo_clip_get_extents (wrapper->clip))) + return FALSE; + } else { + has_clip = TRUE; + clip = *_cairo_clip_get_extents (wrapper->clip); + } + } + + if (has_clip && wrapper->needs_transform) { + cairo_matrix_t m; + double x1, y1, x2, y2; + + _cairo_surface_wrapper_get_inverse_transform (wrapper, &m); + + x1 = clip.x; + y1 = clip.y; + x2 = clip.x + clip.width; + y2 = clip.y + clip.height; + + _cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL); + + clip.x = floor (x1); + clip.y = floor (y1); + clip.width = ceil (x2) - clip.x; + clip.height = ceil (y2) - clip.y; + } + + if (has_clip) { + if (wrapper->has_extents) { + *extents = wrapper->extents; + return _cairo_rectangle_intersect (extents, &clip); + } else { + *extents = clip; + return TRUE; + } + } else if (wrapper->has_extents) { + *extents = wrapper->extents; + return TRUE; + } else { + _cairo_unbounded_rectangle_init (extents); + return TRUE; } - return CAIRO_STATUS_SUCCESS; } diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c index b57b944b8dcd..0a95047f38f3 100644 --- a/gfx/cairo/cairo/src/cairo-surface.c +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -38,12 +38,17 @@ #include "cairoint.h" -#include "cairo-surface-fallback-private.h" +#include "cairo-array-private.h" +#include "cairo-clip-inline.h" #include "cairo-clip-private.h" +#include "cairo-damage-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" +#include "cairo-list-inline.h" +#include "cairo-image-surface-inline.h" #include "cairo-recording-surface-private.h" #include "cairo-region-private.h" +#include "cairo-surface-inline.h" #include "cairo-tee-surface-private.h" /** @@ -58,7 +63,7 @@ * * A cairo surface is created by using backend-specific * constructors, typically of the form - * cairo_backend_surface_create(). + * cairo_backend_surface_create(). * * Most surface types allow accessing the surface without using Cairo * functions. If you do this, keep in mind that it is mandatory that you call @@ -91,7 +96,7 @@ * Note that for other surface types it might be necessary to acquire the * surface's device first. See cairo_device_acquire() for a discussion of * devices. - */ + **/ #define DEFINE_NIL_SURFACE(status, name) \ const cairo_surface_t name = { \ @@ -102,11 +107,14 @@ const cairo_surface_t name = { \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ 0, /* unique id */ \ + 0, /* serial */ \ + NULL, /* damage */ \ + FALSE, /* _finishing */ \ FALSE, /* finished */ \ TRUE, /* is_clear */ \ FALSE, /* has_font_options */ \ - FALSE, /* owns_device */ \ - FALSE, /* permit_subpixel_antialiasing */ \ + FALSE, /* owns_device */ \ + FALSE, /* is_vector */ \ { 0, 0, 0, NULL, }, /* user_data */ \ { 0, 0, 0, NULL, }, /* mime_data */ \ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ @@ -146,6 +154,12 @@ static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_ static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch); static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error); +static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_UNSUPPORTED, _cairo_surface_nil_unsupported); +static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_NOTHING_TO_DO, _cairo_surface_nil_nothing_to_do); + +static void _cairo_surface_finish_snapshots (cairo_surface_t *surface); +static void _cairo_surface_finish (cairo_surface_t *surface); + /** * _cairo_surface_set_error: * @surface: a surface @@ -166,19 +180,23 @@ static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_e * * Return value: the error status. **/ -cairo_status_t +cairo_int_status_t _cairo_surface_set_error (cairo_surface_t *surface, - cairo_status_t status) + cairo_int_status_t status) { + /* NOTHING_TO_DO is magic. We use it to break out of the inner-most + * surface function, but anything higher just sees "success". + */ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - status = CAIRO_STATUS_SUCCESS; + status = CAIRO_INT_STATUS_SUCCESS; - if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED) - return status; + if (status == CAIRO_INT_STATUS_SUCCESS || + status >= (int)CAIRO_INT_STATUS_LAST_STATUS) + return status; /* Don't overwrite an existing error. This preserves the first * error, which is the most significant. */ - _cairo_status_set_error (&surface->status, status); + _cairo_status_set_error (&surface->status, (cairo_status_t)status); return _cairo_error (status); } @@ -203,7 +221,6 @@ cairo_surface_get_type (cairo_surface_t *surface) * surface. */ return surface->type; } -slim_hidden_def (cairo_surface_get_type); /** * cairo_surface_get_content: @@ -222,7 +239,6 @@ cairo_surface_get_content (cairo_surface_t *surface) { return surface->content; } -slim_hidden_def(cairo_surface_get_content); /** * cairo_surface_status: @@ -235,6 +251,8 @@ slim_hidden_def(cairo_surface_get_content); * %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR, * %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or * %CAIRO_STATUS_INVALID_VISUAL. + * + * Since: 1.0 **/ cairo_status_t cairo_surface_status (cairo_surface_t *surface) @@ -310,17 +328,17 @@ _cairo_surface_detach_mime_data (cairo_surface_t *surface) } static void -cairo_surface_detach_snapshots (cairo_surface_t *surface) +_cairo_surface_detach_snapshots (cairo_surface_t *surface) { while (_cairo_surface_has_snapshots (surface)) { - cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots, + _cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots, cairo_surface_t, snapshot)); } } void -cairo_surface_detach_snapshot (cairo_surface_t *snapshot) +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot) { assert (snapshot->snapshot_of != NULL); @@ -334,7 +352,7 @@ cairo_surface_detach_snapshot (cairo_surface_t *snapshot) } void -cairo_surface_attach_snapshot (cairo_surface_t *surface, +_cairo_surface_attach_snapshot (cairo_surface_t *surface, cairo_surface_t *snapshot, cairo_surface_func_t detach_func) { @@ -344,7 +362,7 @@ cairo_surface_attach_snapshot (cairo_surface_t *surface, cairo_surface_reference (snapshot); if (snapshot->snapshot_of != NULL) - cairo_surface_detach_snapshot (snapshot); + _cairo_surface_detach_snapshot (snapshot); snapshot->snapshot_of = surface; snapshot->snapshot_detach = detach_func; @@ -363,7 +381,6 @@ _cairo_surface_has_snapshot (cairo_surface_t *surface, cairo_list_foreach_entry (snapshot, cairo_surface_t, &surface->snapshots, snapshot) { - /* XXX is_similar? */ if (snapshot->backend == backend) return snapshot; } @@ -371,31 +388,21 @@ _cairo_surface_has_snapshot (cairo_surface_t *surface, return NULL; } -static cairo_bool_t -_cairo_surface_is_writable (cairo_surface_t *surface) -{ - return ! surface->finished && - surface->snapshot_of == NULL && - ! _cairo_surface_has_snapshots (surface) && - ! _cairo_surface_has_mime_data (surface); -} - -static void +cairo_status_t _cairo_surface_begin_modification (cairo_surface_t *surface) { assert (surface->status == CAIRO_STATUS_SUCCESS); assert (! surface->finished); - assert (surface->snapshot_of == NULL); - cairo_surface_detach_snapshots (surface); - _cairo_surface_detach_mime_data (surface); + return _cairo_surface_flush (surface, 1); } void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend, cairo_device_t *device, - cairo_content_t content) + cairo_content_t content, + cairo_bool_t is_vector) { CAIRO_MUTEX_INITIALIZE (); @@ -403,15 +410,17 @@ _cairo_surface_init (cairo_surface_t *surface, surface->device = cairo_device_reference (device); surface->content = content; surface->type = backend->type; + surface->is_vector = is_vector; CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1); surface->status = CAIRO_STATUS_SUCCESS; surface->unique_id = _cairo_surface_allocate_unique_id (); surface->finished = FALSE; + surface->_finishing = FALSE; surface->is_clear = FALSE; + surface->serial = 0; + surface->damage = NULL; surface->owns_device = (device != NULL); - surface->has_font_options = FALSE; - surface->permit_subpixel_antialiasing = TRUE; _cairo_user_data_array_init (&surface->user_data); _cairo_user_data_array_init (&surface->mime_data); @@ -428,6 +437,8 @@ _cairo_surface_init (cairo_surface_t *surface, cairo_list_init (&surface->snapshots); surface->snapshot_of = NULL; + + surface->has_font_options = FALSE; } static void @@ -441,37 +452,11 @@ _cairo_surface_copy_similar_properties (cairo_surface_t *surface, _cairo_surface_set_font_options (surface, &options); } - surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing; - cairo_surface_set_fallback_resolution (surface, other->x_fallback_resolution, other->y_fallback_resolution); } -cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_content_t content, - int width, - int height) -{ - cairo_surface_t *surface; - - if (unlikely (other->status)) - return _cairo_surface_create_in_error (other->status); - - if (other->backend->create_similar == NULL) - return NULL; - - surface = other->backend->create_similar (other, - content, width, height); - if (surface == NULL || surface->status) - return surface; - - _cairo_surface_copy_similar_properties (surface, other); - - return surface; -} - /** * cairo_surface_create_similar: * @other: an existing surface used to select the backend of the new surface @@ -481,14 +466,18 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, * * Create a new surface that is as compatible as possible with an * existing surface. For example the new surface will have the same - * fallback resolution and font options as @other. Generally, the new - * surface will also use the same backend as @other, unless that is - * not possible for some reason. The type of the returned surface may - * be examined with cairo_surface_get_type(). + * device scale, fallback resolution and font options as + * @other. Generally, the new surface will also use the same backend + * as @other, unless that is not possible for some reason. The type of + * the returned surface may be examined with + * cairo_surface_get_type(). * * Initially the surface contents are all 0 (transparent if contents * have transparency, black otherwise.) * + * Use cairo_surface_create_similar_image() if you need an image surface + * which can be painted quickly to the target surface. + * * Return value: a pointer to the newly allocated surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. @@ -496,6 +485,8 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, * This function always returns a valid pointer, but it will return a * pointer to a "nil" surface if @other is already in an error state * or any other error occurs. + * + * Since: 1.0 **/ cairo_surface_t * cairo_surface_create_similar (cairo_surface_t *other, @@ -503,96 +494,415 @@ cairo_surface_create_similar (cairo_surface_t *other, int width, int height) { + cairo_surface_t *surface; + cairo_status_t status; + cairo_solid_pattern_t pattern; + if (unlikely (other->status)) return _cairo_surface_create_in_error (other->status); if (unlikely (other->finished)) return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); - + if (unlikely (width < 0 || height < 0)) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); if (unlikely (! CAIRO_CONTENT_VALID (content))) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT); - return _cairo_surface_create_similar_solid (other, - content, width, height, - CAIRO_COLOR_TRANSPARENT, - TRUE); -} + /* We inherit the device scale, so create a larger surface */ + width = width * other->device_transform.xx; + height = height * other->device_transform.yy; -cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_content_t content, - int width, - int height, - const cairo_color_t *color, - cairo_bool_t allow_fallback) -{ - cairo_status_t status; - cairo_surface_t *surface; - cairo_solid_pattern_t pattern; + surface = NULL; + if (other->backend->create_similar) + surface = other->backend->create_similar (other, content, width, height); + if (surface == NULL) + surface = cairo_surface_create_similar_image (other, + _cairo_format_from_content (content), + width, height); - surface = _cairo_surface_create_similar_scratch (other, content, - width, height); - if (surface == NULL && allow_fallback) - surface = _cairo_image_surface_create_with_content (content, - width, height); - if (surface == NULL || surface->status) + if (unlikely (surface->status)) return surface; - _cairo_pattern_init_solid (&pattern, color); + _cairo_surface_copy_similar_properties (surface, other); + cairo_surface_set_device_scale (surface, + other->device_transform.xx, + other->device_transform.yy); + + if (unlikely (surface->status)) + return surface; + + _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_TRANSPARENT); status = _cairo_surface_paint (surface, - color == CAIRO_COLOR_TRANSPARENT ? - CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, + CAIRO_OPERATOR_CLEAR, &pattern.base, NULL); if (unlikely (status)) { cairo_surface_destroy (surface); surface = _cairo_surface_create_in_error (status); } + assert (surface->is_clear); + return surface; } +/** + * cairo_surface_create_similar_image: + * @other: an existing surface used to select the preference of the new surface + * @format: the format for the new surface + * @width: width of the new surface, (in pixels) + * @height: height of the new surface (in pixels) + * + * Create a new image surface that is as compatible as possible for uploading + * to and the use in conjunction with an existing surface. However, this surface + * can still be used like any normal image surface. Unlike + * cairo_surface_create_similar() the new image surface won't inherit + * the device scale from @other. + * + * Initially the surface contents are all 0 (transparent if contents + * have transparency, black otherwise.) + * + * Use cairo_surface_create_similar() if you don't need an image surface. + * + * Return value: a pointer to the newly allocated image 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 @other is already in an error state + * or any other error occurs. + * + * Since: 1.12 + **/ cairo_surface_t * -_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, - const cairo_solid_pattern_t *solid_pattern) +cairo_surface_create_similar_image (cairo_surface_t *other, + cairo_format_t format, + int width, + int height) { - if (other->backend->create_solid_pattern_surface != NULL) { - cairo_surface_t *surface; + cairo_surface_t *image; - surface = other->backend->create_solid_pattern_surface (other, - solid_pattern); - if (surface) - return surface; - } + if (unlikely (other->status)) + return _cairo_surface_create_in_error (other->status); + if (unlikely (other->finished)) + return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); - return _cairo_surface_create_similar_solid (other, - _cairo_color_get_content (&solid_pattern->color), - 1, 1, - &solid_pattern->color, - FALSE); + if (unlikely (width < 0 || height < 0)) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + if (unlikely (! CAIRO_FORMAT_VALID (format))) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT); + + image = NULL; + if (other->backend->create_similar_image) + image = other->backend->create_similar_image (other, + format, width, height); + if (image == NULL) + image = cairo_image_surface_create (format, width, height); + + assert (image->is_clear); + + return image; +} +slim_hidden_def (cairo_surface_create_similar_image); + +/** + * _cairo_surface_map_to_image: + * @surface: an existing surface used to extract the image from + * @extents: limit the extraction to an rectangular region + * + * Returns an image surface that is the most efficient mechanism for + * modifying the backing store of the target surface. The region + * retrieved is limited to @extents. + * + * Note, the use of the original surface as a target or source whilst + * it is mapped is undefined. The result of mapping the surface + * multiple times is undefined. Calling cairo_surface_destroy() or + * cairo_surface_finish() on the resulting image surface results in + * undefined behavior. Changing the device transform of the image + * surface or of @surface before the image surface is unmapped results + * in undefined behavior. + * + * Assumes that @surface is valid (CAIRO_STATUS_SUCCESS, + * non-finished). + * + * Return value: a pointer to the newly allocated image surface. The + * caller must use _cairo_surface_unmap_image() to destroy this image + * surface. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if @other is already in an error state + * or any other error occurs. + * + * The returned image might have a %CAIRO_FORMAT_INVALID format. + **/ +cairo_image_surface_t * +_cairo_surface_map_to_image (cairo_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *image = NULL; + + assert (extents != NULL); + + /* TODO: require map_to_image != NULL */ + if (surface->backend->map_to_image) + image = surface->backend->map_to_image (surface, extents); + + if (image == NULL) + image = _cairo_image_surface_clone_subimage (surface, extents); + + return image; } +/** + * _cairo_surface_unmap_image: + * @surface: the surface passed to _cairo_surface_map_to_image(). + * @image: the currently mapped image + * + * Unmaps the image surface as returned from + * _cairo_surface_map_to_image(). + * + * The content of the image will be uploaded to the target surface. + * Afterwards, the image is destroyed. + * + * Using an image surface which wasn't returned by + * _cairo_surface_map_to_image() results in undefined behavior. + * + * An image surface in error status can be passed to + * _cairo_surface_unmap_image(). + * + * Return value: the unmap status. + * + * Even if the unmap status is not successful, @image is destroyed. + **/ cairo_int_status_t -_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, - cairo_surface_t *solid_surface, - const cairo_solid_pattern_t *solid_pattern) +_cairo_surface_unmap_image (cairo_surface_t *surface, + cairo_image_surface_t *image) { - /* Solid pattern surface for these backends are special and not trivial - * to repaint. Skip repainting. - * - * This does not work optimally with things like analysis surface that - * are proxies. But returning UNSUPPORTED is *safe* as it only - * disables some caching. - */ - if (other->backend->create_solid_pattern_surface != NULL && - ! other->backend->can_repaint_solid_pattern_surface (solid_surface, - solid_pattern)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; + cairo_surface_pattern_t pattern; + cairo_rectangle_int_t extents; + cairo_clip_t *clip; + cairo_int_status_t status; + + /* map_to_image can return error surfaces */ + if (unlikely (image->base.status)) { + status = image->base.status; + goto destroy; } - return _cairo_surface_paint (solid_surface, - CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, - NULL); + /* If the image is untouched just skip the update */ + if (image->base.serial == 0) { + status = CAIRO_STATUS_SUCCESS; + goto destroy; + } + + /* TODO: require unmap_image != NULL */ + if (surface->backend->unmap_image && + ! _cairo_image_surface_is_clone (image)) + { + status = surface->backend->unmap_image (surface, image); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + _cairo_pattern_init_for_surface (&pattern, &image->base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + + /* We have to apply the translate from map_to_image's extents.x and .y */ + cairo_matrix_init_translate (&pattern.base.matrix, + image->base.device_transform.x0, + image->base.device_transform.y0); + + /* And we also have to clip the operation to the image's extents */ + extents.x = image->base.device_transform_inverse.x0; + extents.y = image->base.device_transform_inverse.y0; + extents.width = image->width; + extents.height = image->height; + clip = _cairo_clip_intersect_rectangle (NULL, &extents); + + status = _cairo_surface_paint (surface, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + clip); + + _cairo_pattern_fini (&pattern.base); + _cairo_clip_destroy (clip); + +destroy: + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return status; +} + +/** + * cairo_surface_map_to_image: + * @surface: an existing surface used to extract the image from + * @extents: limit the extraction to an rectangular region + * + * Returns an image surface that is the most efficient mechanism for + * modifying the backing store of the target surface. The region retrieved + * may be limited to the @extents or %NULL for the whole surface + * + * Note, the use of the original surface as a target or source whilst + * it is mapped is undefined. The result of mapping the surface + * multiple times is undefined. Calling cairo_surface_destroy() or + * cairo_surface_finish() on the resulting image surface results in + * undefined behavior. Changing the device transform of the image + * surface or of @surface before the image surface is unmapped results + * in undefined behavior. + * + * Return value: a pointer to the newly allocated image surface. The caller + * must use cairo_surface_unmap_image() to destroy this image surface. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if @other is already in an error state + * or any other error occurs. If the returned pointer does not have an + * error status, it is guaranteed to be an image surface whose format + * is not %CAIRO_FORMAT_INVALID. + * + * Since: 1.12 + **/ +cairo_surface_t * +cairo_surface_map_to_image (cairo_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_rectangle_int_t rect; + cairo_image_surface_t *image; + cairo_status_t status; + + if (unlikely (surface->status)) + return _cairo_surface_create_in_error (surface->status); + if (unlikely (surface->finished)) + return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + + if (extents == NULL) { + if (unlikely (! surface->backend->get_extents (surface, &rect))) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + + extents = ▭ + } else { + cairo_rectangle_int_t surface_extents; + + /* If this surface is bounded, we can't map parts + * that are outside of it. */ + if (likely (surface->backend->get_extents (surface, &surface_extents))) { + if (unlikely (! _cairo_rectangle_contains_rectangle (&surface_extents, extents))) + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + } + } + + image = _cairo_surface_map_to_image (surface, extents); + + status = image->base.status; + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return _cairo_surface_create_in_error (status); + } + + if (image->format == CAIRO_FORMAT_INVALID) { + cairo_surface_destroy (&image->base); + image = _cairo_image_surface_clone_subimage (surface, extents); + } + + return &image->base; +} + +/** + * cairo_surface_unmap_image: + * @surface: the surface passed to cairo_surface_map_to_image(). + * @image: the currently mapped image + * + * Unmaps the image surface as returned from #cairo_surface_map_to_image(). + * + * The content of the image will be uploaded to the target surface. + * Afterwards, the image is destroyed. + * + * Using an image surface which wasn't returned by cairo_surface_map_to_image() + * results in undefined behavior. + * + * Since: 1.12 + **/ +void +cairo_surface_unmap_image (cairo_surface_t *surface, + cairo_surface_t *image) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (unlikely (surface->status)) { + status = surface->status; + goto error; + } + if (unlikely (surface->finished)) { + status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + goto error; + } + if (unlikely (image->status)) { + status = image->status; + goto error; + } + if (unlikely (image->finished)) { + status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + goto error; + } + if (unlikely (! _cairo_surface_is_image (image))) { + status = _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + goto error; + } + + status = _cairo_surface_unmap_image (surface, + (cairo_image_surface_t *) image); + if (unlikely (status)) + _cairo_surface_set_error (surface, status); + + return; + +error: + _cairo_surface_set_error (surface, status); + cairo_surface_finish (image); + cairo_surface_destroy (image); +} + +cairo_surface_t * +_cairo_surface_create_scratch (cairo_surface_t *other, + cairo_content_t content, + int width, + int height, + const cairo_color_t *color) +{ + cairo_surface_t *surface; + cairo_status_t status; + cairo_solid_pattern_t pattern; + + if (unlikely (other->status)) + return _cairo_surface_create_in_error (other->status); + + surface = NULL; + if (other->backend->create_similar) + surface = other->backend->create_similar (other, content, width, height); + if (surface == NULL) + surface = cairo_surface_create_similar_image (other, + _cairo_format_from_content (content), + width, height); + + if (unlikely (surface->status)) + return surface; + + _cairo_surface_copy_similar_properties (surface, other); + + if (unlikely (surface->status)) + return surface; + + if (color) { + _cairo_pattern_init_solid (&pattern, color); + status = _cairo_surface_paint (surface, + color == CAIRO_COLOR_TRANSPARENT ? + CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + } + + return surface; } /** @@ -603,10 +913,12 @@ _cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, * @surface from being destroyed until a matching call to * cairo_surface_destroy() is made. * - * The number of references to a #cairo_surface_t can be get using - * cairo_surface_get_reference_count(). + * Use cairo_surface_get_reference_count() to get the number of + * references to a #cairo_surface_t. * * Return value: the referenced #cairo_surface_t. + * + * Since: 1.0 **/ cairo_surface_t * cairo_surface_reference (cairo_surface_t *surface) @@ -630,6 +942,8 @@ slim_hidden_def (cairo_surface_reference); * Decreases the reference count on @surface by one. If the result is * zero, then @surface and all associated resources are freed. See * cairo_surface_reference(). + * + * Since: 1.0 **/ void cairo_surface_destroy (cairo_surface_t *surface) @@ -645,11 +959,19 @@ cairo_surface_destroy (cairo_surface_t *surface) assert (surface->snapshot_of == NULL); - if (! surface->finished) - cairo_surface_finish (surface); + if (! surface->finished) { + _cairo_surface_finish_snapshots (surface); + /* We may have been referenced by a snapshot prior to have + * detaching it with the copy-on-write. + */ + if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count)) + return; - /* paranoid check that nobody took a reference whilst finishing */ - assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + _cairo_surface_finish (surface); + } + + if (surface->damage) + _cairo_damage_destroy (surface->damage); _cairo_user_data_array_fini (&surface->user_data); _cairo_user_data_array_fini (&surface->mime_data); @@ -657,6 +979,11 @@ cairo_surface_destroy (cairo_surface_t *surface) if (surface->owns_device) cairo_device_destroy (surface->device); + assert (surface->snapshot_of == NULL); + assert (! _cairo_surface_has_snapshots (surface)); + /* paranoid check that nobody took a reference whilst finishing */ + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + free (surface); } slim_hidden_def(cairo_surface_destroy); @@ -682,6 +1009,35 @@ cairo_surface_get_reference_count (cairo_surface_t *surface) return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count); } +static void +_cairo_surface_finish_snapshots (cairo_surface_t *surface) +{ + cairo_status_t status; + + /* update the snapshots *before* we declare the surface as finished */ + surface->_finishing = TRUE; + status = _cairo_surface_flush (surface, 0); + (void) status; +} + +static void +_cairo_surface_finish (cairo_surface_t *surface) +{ + cairo_status_t status; + + /* call finish even if in error mode */ + if (surface->backend->finish) { + status = surface->backend->finish (surface); + if (unlikely (status)) + _cairo_surface_set_error (surface, status); + } + + surface->finished = TRUE; + + assert (surface->snapshot_of == NULL); + assert (!_cairo_surface_has_snapshots (surface)); +} + /** * cairo_surface_finish: * @surface: the #cairo_surface_t to finish @@ -690,8 +1046,8 @@ cairo_surface_get_reference_count (cairo_surface_t *surface) * external resources. For example, for the Xlib backend it means * that cairo will no longer access the drawable, which can be freed. * After calling cairo_surface_finish() the only valid operations on a - * surface are getting and setting user, referencing and - * destroying, and flushing and finishing it. + * surface are checking status, getting and setting user, referencing + * and destroying, and flushing and finishing it. * Further drawing to the surface will not affect the * surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED * error. @@ -700,12 +1056,12 @@ cairo_surface_get_reference_count (cairo_surface_t *surface) * reference count to zero, cairo will call cairo_surface_finish() if * it hasn't been called already, before freeing the resources * associated with the surface. + * + * Since: 1.0 **/ void cairo_surface_finish (cairo_surface_t *surface) { - cairo_status_t status; - if (surface == NULL) return; @@ -715,20 +1071,14 @@ cairo_surface_finish (cairo_surface_t *surface) if (surface->finished) return; - /* update the snapshots *before* we declare the surface as finished */ - cairo_surface_detach_snapshots (surface); - if (surface->snapshot_of != NULL) - cairo_surface_detach_snapshot (surface); + /* We have to be careful when decoupling potential reference cycles */ + cairo_surface_reference (surface); - cairo_surface_flush (surface); - surface->finished = TRUE; + _cairo_surface_finish_snapshots (surface); + /* XXX need to block and wait for snapshot references */ + _cairo_surface_finish (surface); - /* call finish even if in error mode */ - if (surface->backend->finish) { - status = surface->backend->finish (surface); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); - } + cairo_surface_destroy (surface); } slim_hidden_def (cairo_surface_finish); @@ -764,13 +1114,18 @@ _cairo_surface_release_device_reference (cairo_surface_t *surface) * function returns %NULL. * * Return value: the user data previously attached or %NULL. + * + * Since: 1.0 **/ void * cairo_surface_get_user_data (cairo_surface_t *surface, const cairo_user_data_key_t *key) { - return _cairo_user_data_array_get_data (&surface->user_data, - key); + /* Prevent reads of the array during teardown */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) + return NULL; + + return _cairo_user_data_array_get_data (&surface->user_data, key); } /** @@ -788,6 +1143,8 @@ cairo_surface_get_user_data (cairo_surface_t *surface, * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. + * + * Since: 1.0 **/ cairo_status_t cairo_surface_set_user_data (cairo_surface_t *surface, @@ -798,6 +1155,9 @@ cairo_surface_set_user_data (cairo_surface_t *surface, if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) return surface->status; + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + return _cairo_user_data_array_set_data (&surface->user_data, key, user_data, destroy); } @@ -826,7 +1186,9 @@ cairo_surface_get_mime_data (cairo_surface_t *surface, *data = NULL; *length = 0; - if (unlikely (surface->status)) + + /* Prevent reads of the array during teardown */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) return; /* The number of mime-types attached to a surface is usually small, @@ -837,7 +1199,7 @@ cairo_surface_get_mime_data (cairo_surface_t *surface, num_slots = surface->mime_data.num_elements; slots = _cairo_array_index (&surface->mime_data, 0); for (i = 0; i < num_slots; i++) { - if (strcmp ((char *) slots[i].key, mime_type) == 0) { + if (slots[i].key != NULL && strcmp ((char *) slots[i].key, mime_type) == 0) { cairo_mime_data_t *mime_data = slots[i].user_data; *data = mime_data->data; @@ -862,37 +1224,145 @@ _cairo_mime_data_destroy (void *ptr) free (mime_data); } + +static const char *_cairo_surface_image_mime_types[] = { + CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_PNG, + CAIRO_MIME_TYPE_JP2, + CAIRO_MIME_TYPE_JBIG2, + CAIRO_MIME_TYPE_CCITT_FAX, +}; + +cairo_bool_t +_cairo_surface_has_mime_image (cairo_surface_t *surface) +{ + cairo_user_data_slot_t *slots; + int i, j, num_slots; + + /* Prevent reads of the array during teardown */ + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) + return FALSE; + + /* The number of mime-types attached to a surface is usually small, + * typically zero. Therefore it is quicker to do a strcmp() against + * each key than it is to intern the string (i.e. compute a hash, + * search the hash table, and do a final strcmp). + */ + num_slots = surface->mime_data.num_elements; + slots = _cairo_array_index (&surface->mime_data, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].key != NULL) { + for (j = 0; j < ARRAY_LENGTH (_cairo_surface_image_mime_types); j++) { + if (strcmp ((char *) slots[i].key, _cairo_surface_image_mime_types[j]) == 0) + return TRUE; + } + } + } + + return FALSE; +} + +/** + * CAIRO_MIME_TYPE_CCITT_FAX: + * + * Group 3 or Group 4 CCITT facsimile encoding (International + * Telecommunication Union, Recommendations T.4 and T.6.) + * + * Since: 1.16 + **/ + +/** + * CAIRO_MIME_TYPE_CCITT_FAX_PARAMS: + * + * Decode parameters for Group 3 or Group 4 CCITT facsimile encoding. + * See [CCITT Fax Images][ccitt]. + * + * Since: 1.16 + **/ + +/** + * CAIRO_MIME_TYPE_EPS: + * + * Encapsulated PostScript file. + * [Encapsulated PostScript File Format Specification](http://wwwimages.adobe.com/content/dam/Adobe/endevnet/postscript/pdfs/5002.EPSF_Spec.pdf) + * + * Since: 1.16 + **/ + +/** + * CAIRO_MIME_TYPE_EPS_PARAMS: + * + * Embedding parameters Encapsulated PostScript data. + * See [Embedding EPS files][eps]. + * + * Since: 1.16 + **/ + +/** + * CAIRO_MIME_TYPE_JBIG2: + * + * Joint Bi-level Image Experts Group image coding standard (ISO/IEC 11544). + * + * Since: 1.14 + **/ + +/** + * CAIRO_MIME_TYPE_JBIG2_GLOBAL: + * + * Joint Bi-level Image Experts Group image coding standard (ISO/IEC 11544) global segment. + * + * Since: 1.14 + **/ + +/** + * CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID: + * + * An unique identifier shared by a JBIG2 global segment and all JBIG2 images + * that depend on the global segment. + * + * Since: 1.14 + **/ + /** * CAIRO_MIME_TYPE_JP2: * * The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1). * - * @Since: 1.10 - */ + * Since: 1.10 + **/ /** * CAIRO_MIME_TYPE_JPEG: * * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1). * - * @Since: 1.10 - */ + * Since: 1.10 + **/ /** * CAIRO_MIME_TYPE_PNG: * * The Portable Network Graphics image file format (ISO/IEC 15948). * - * @Since: 1.10 - */ + * Since: 1.10 + **/ /** * CAIRO_MIME_TYPE_URI: * * URI for an image file (unofficial MIME type). * - * @Since: 1.10 - */ + * Since: 1.10 + **/ + +/** + * CAIRO_MIME_TYPE_UNIQUE_ID: + * + * Unique identifier for a surface (cairo specific MIME type). All surfaces with + * the same unique identifier will only be embedded once. + * + * Since: 1.12 + **/ /** * cairo_surface_set_mime_data: @@ -916,17 +1386,30 @@ _cairo_mime_data_destroy (void *ptr) * memory and disk space. * * The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG, - * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI. + * %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI, + * %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. * * See corresponding backend surface docs for details about which MIME * types it can handle. Caution: the associated MIME data will be * discarded if you draw on the surface afterwards. Use this function * with care. * - * Since: 1.10 + * Even if a backend supports a MIME type, that does not mean cairo + * will always be able to use the attached MIME data. For example, if + * the backend does not natively support the compositing operation used + * to apply the MIME data to the backend. In that case, the MIME data + * will be ignored. Therefore, to apply an image in all cases, it is best + * to create an image surface which contains the decoded image data and + * then attach the MIME data to that. This ensures the image will always + * be used while still allowing the MIME data to be used whenever + * possible. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. + * + * Since: 1.10 **/ cairo_status_t cairo_surface_set_mime_data (cairo_surface_t *surface, @@ -939,9 +1422,15 @@ cairo_surface_set_mime_data (cairo_surface_t *surface, cairo_status_t status; cairo_mime_data_t *mime_data; + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return surface->status; + + if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + if (unlikely (surface->status)) return surface->status; - if (surface->finished) + if (unlikely (surface->finished)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); status = _cairo_intern_string (&mime_type, -1); @@ -949,7 +1438,7 @@ cairo_surface_set_mime_data (cairo_surface_t *surface, return _cairo_surface_set_error (surface, status); if (data != NULL) { - mime_data = malloc (sizeof (cairo_mime_data_t)); + mime_data = _cairo_malloc (sizeof (cairo_mime_data_t)); if (unlikely (mime_data == NULL)) return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -967,16 +1456,57 @@ cairo_surface_set_mime_data (cairo_surface_t *surface, mime_data, _cairo_mime_data_destroy); if (unlikely (status)) { - if (mime_data != NULL) - free (mime_data); + free (mime_data); return _cairo_surface_set_error (surface, status); } + surface->is_clear = FALSE; + return CAIRO_STATUS_SUCCESS; } slim_hidden_def (cairo_surface_set_mime_data); +/** + * cairo_surface_supports_mime_type: + * @surface: a #cairo_surface_t + * @mime_type: the mime type + * + * Return whether @surface supports @mime_type. + * + * Return value: %TRUE if @surface supports + * @mime_type, %FALSE otherwise + * + * Since: 1.12 + **/ +cairo_bool_t +cairo_surface_supports_mime_type (cairo_surface_t *surface, + const char *mime_type) +{ + const char **types; + + if (unlikely (surface->status)) + return FALSE; + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (surface->backend->get_supported_mime_types) { + types = surface->backend->get_supported_mime_types (surface); + if (types) { + while (*types) { + if (strcmp (*types, mime_type) == 0) + return TRUE; + types++; + } + } + } + + return FALSE; +} +slim_hidden_def (cairo_surface_supports_mime_type); + static void _cairo_mime_data_reference (const void *key, void *elt, void *closure) { @@ -1007,6 +1537,8 @@ _cairo_surface_copy_mime_data (cairo_surface_t *dst, _cairo_mime_data_reference, NULL); + dst->is_clear = FALSE; + return CAIRO_STATUS_SUCCESS; } @@ -1029,16 +1561,13 @@ void _cairo_surface_set_font_options (cairo_surface_t *surface, cairo_font_options_t *options) { - cairo_status_t status; - if (surface->status) return; assert (surface->snapshot_of == NULL); if (surface->finished) { - status = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } @@ -1061,6 +1590,8 @@ _cairo_surface_set_font_options (cairo_surface_t *surface, * for rendering on them, print surfaces to disable hinting of * metrics and so forth. The result can then be used with * cairo_scaled_font_create(). + * + * Since: 1.0 **/ void cairo_surface_get_font_options (cairo_surface_t *surface, @@ -1088,16 +1619,30 @@ cairo_surface_get_font_options (cairo_surface_t *surface, } slim_hidden_def (cairo_surface_get_font_options); +cairo_status_t +_cairo_surface_flush (cairo_surface_t *surface, unsigned flags) +{ + /* update the current snapshots *before* the user updates the surface */ + _cairo_surface_detach_snapshots (surface); + if (surface->snapshot_of != NULL) + _cairo_surface_detach_snapshot (surface); + _cairo_surface_detach_mime_data (surface); + + return __cairo_surface_flush (surface, flags); +} + /** * cairo_surface_flush: * @surface: a #cairo_surface_t * - * Do any pending drawing for the surface and also restore any - * temporary modifications cairo has made to the surface's - * state. This function must be called before switching from - * drawing on the surface with cairo to drawing on it directly - * with native APIs. If the surface doesn't support direct access, - * then this function does nothing. + * Do any pending drawing for the surface and also restore any temporary + * modifications cairo has made to the surface's state. This function + * must be called before switching from drawing on the surface with + * cairo to drawing on it directly with native APIs, or accessing its + * memory outside of Cairo. If the surface doesn't support direct + * access, then this function does nothing. + * + * Since: 1.0 **/ void cairo_surface_flush (cairo_surface_t *surface) @@ -1110,14 +1655,9 @@ cairo_surface_flush (cairo_surface_t *surface) if (surface->finished) return; - /* update the current snapshots *before* the user updates the surface */ - cairo_surface_detach_snapshots (surface); - - if (surface->backend->flush) { - status = surface->backend->flush (surface); - if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); - } + status = _cairo_surface_flush (surface, 0); + if (unlikely (status)) + _cairo_surface_set_error (surface, status); } slim_hidden_def (cairo_surface_flush); @@ -1128,11 +1668,25 @@ slim_hidden_def (cairo_surface_flush); * Tells cairo that drawing has been done to surface using means other * than cairo, and that cairo should reread any cached areas. Note * that you must call cairo_surface_flush() before doing such drawing. - */ + * + * Since: 1.0 + **/ void cairo_surface_mark_dirty (cairo_surface_t *surface) { - cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1); + cairo_rectangle_int_t extents; + + if (unlikely (surface->status)) + return; + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + _cairo_surface_get_extents (surface, &extents); + cairo_surface_mark_dirty_rectangle (surface, + extents.x, extents.y, + extents.width, extents.height); } slim_hidden_def (cairo_surface_mark_dirty); @@ -1151,7 +1705,9 @@ slim_hidden_def (cairo_surface_mark_dirty); * Any cached clip set on the surface will be reset by this function, * to make sure that future cairo calls have the clip set that they * expect. - */ + * + * Since: 1.0 + **/ void cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, int x, @@ -1161,13 +1717,13 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } @@ -1178,6 +1734,18 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, assert (! _cairo_surface_has_mime_data (surface)); surface->is_clear = FALSE; + surface->serial++; + + if (surface->damage) { + cairo_box_t box; + + box.p1.x = x; + box.p1.y = y; + box.p2.x = x + width; + box.p2.y = y + height; + + surface->damage = _cairo_damage_add_box (surface->damage, &box); + } if (surface->backend->mark_dirty_rectangle != NULL) { /* XXX: FRAGILE: We're ignoring the scaling component of @@ -1191,49 +1759,55 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, width, height); if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); + _cairo_surface_set_error (surface, status); } } slim_hidden_def (cairo_surface_mark_dirty_rectangle); /** - * _cairo_surface_set_device_scale: + * cairo_surface_set_device_scale: * @surface: a #cairo_surface_t - * @sx: a scale factor in the X direction - * @sy: a scale factor in the Y direction + * @x_scale: a scale factor in the X direction + * @y_scale: a scale factor in the Y direction * - * Private function for setting an extra scale factor to affect all - * drawing to a surface. This is used, for example, when replaying a - * recording surface to an image fallback intended for an eventual - * vector-oriented backend. Since the recording surface will record - * coordinates in one backend space, but the image fallback uses a - * different backend space, (differing by the fallback resolution - * scale factors), we need a scale factor correction. + * Sets a scale that is multiplied to the device coordinates determined + * 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 + * sufficient to do this, since functions like + * cairo_device_to_user() will expose the hidden scale. * - * Caution: Not all places we use device transform correctly handle - * both a translate and a scale. An audit would be nice. + * Note that the scale affects drawing to the surface as well as + * using the surface in a source pattern. + * + * Since: 1.14 **/ void -_cairo_surface_set_device_scale (cairo_surface_t *surface, - double sx, - double sy) +cairo_surface_set_device_scale (cairo_surface_t *surface, + double x_scale, + double y_scale) { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } - _cairo_surface_begin_modification (surface); + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) { + _cairo_surface_set_error (surface, status); + return; + } - surface->device_transform.xx = sx; - surface->device_transform.yy = sy; + surface->device_transform.xx = x_scale; + surface->device_transform.yy = y_scale; surface->device_transform.xy = 0.0; surface->device_transform.yx = 0.0; @@ -1244,6 +1818,30 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface, _cairo_observers_notify (&surface->device_transform_observers, surface); } +slim_hidden_def (cairo_surface_set_device_scale); + +/** + * cairo_surface_get_device_scale: + * @surface: a #cairo_surface_t + * @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 + * cairo_surface_set_device_scale(). + * + * Since: 1.14 + **/ +void +cairo_surface_get_device_scale (cairo_surface_t *surface, + double *x_scale, + double *y_scale) +{ + if (x_scale) + *x_scale = surface->device_transform.xx; + if (y_scale) + *y_scale = surface->device_transform.yy; +} +slim_hidden_def (cairo_surface_get_device_scale); /** * cairo_surface_set_device_offset: @@ -1262,6 +1860,8 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface, * * Note that the offset affects drawing to the surface as well as * using the surface in a source pattern. + * + * Since: 1.0 **/ void cairo_surface_set_device_offset (cairo_surface_t *surface, @@ -1270,17 +1870,21 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } - _cairo_surface_begin_modification (surface); + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) { + _cairo_surface_set_error (surface, status); + return; + } surface->device_transform.x0 = x_offset; surface->device_transform.y0 = y_offset; @@ -1345,7 +1949,7 @@ slim_hidden_def (cairo_surface_get_device_offset); * there is currently no way to have more than one fallback resolution * in effect on a single page. * - * The default fallback resoultion is 300 pixels per inch in both + * The default fallback resolution is 300 pixels per inch in both * dimensions. * * Since: 1.2 @@ -1357,13 +1961,13 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); - if (surface->finished) { - status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } @@ -1371,11 +1975,15 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, /* XXX Could delay raising the error until we fallback, but throwing * the error here means that we can catch the real culprit. */ - status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); + _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); return; } - _cairo_surface_begin_modification (surface); + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) { + _cairo_surface_set_error (surface, status); + return; + } surface->x_fallback_resolution = x_pixels_per_inch; surface->y_fallback_resolution = y_pixels_per_inch; @@ -1405,12 +2013,6 @@ cairo_surface_get_fallback_resolution (cairo_surface_t *surface, *y_pixels_per_inch = surface->y_fallback_resolution; } -int -_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface) -{ - return surface->backend->fill == NULL ? 10240 : 256; -} - cairo_bool_t _cairo_surface_has_device_transform (cairo_surface_t *surface) { @@ -1441,7 +2043,7 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return surface->status; assert (!surface->finished); @@ -1454,21 +2056,27 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, if (unlikely (status)) return _cairo_surface_set_error (surface, status); - if (PIXMAN_FORMAT_BPP((*image_out)->pixman_format) == 0) { - volatile char* acquire_source_image_ptr[10]; - volatile char* crasher; - int i; - for (i = 0; i < 10; i++) { - acquire_source_image_ptr[i] = (char*)surface->backend->acquire_source_image; - } - crasher = NULL; - *crasher = acquire_source_image_ptr[5]; - } _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); return CAIRO_STATUS_SUCCESS; } +cairo_status_t +_cairo_surface_default_acquire_source_image (void *_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_surface_t *surface = _surface; + cairo_rectangle_int_t extents; + + if (unlikely (! surface->backend->get_extents (surface, &extents))) + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + + *image_out = _cairo_surface_map_to_image (surface, &extents); + *image_extra = NULL; + return (*image_out)->base.status; +} + /** * _cairo_surface_release_source_image: * @surface: a #cairo_surface_t @@ -1487,572 +2095,33 @@ _cairo_surface_release_source_image (cairo_surface_t *surface, surface->backend->release_source_image (surface, image, image_extra); } -/** - * _cairo_surface_acquire_dest_image: - * @surface: a #cairo_surface_t - * @interest_rect: area of @surface for which fallback drawing is being done. - * A value of %NULL indicates that the entire surface is desired. - * XXXX I'd like to get rid of being able to pass %NULL here (nothing seems to) - * @image_out: location to store a pointer to an image surface that includes at least - * the intersection of @interest_rect with the visible area of @surface. - * This surface could be @surface itself, a surface held internal to @surface, - * or it could be a new surface with a copy of the relevant portion of @surface. - * If a new surface is created, it should have the same channels and depth - * as @surface so that copying to and from it is exact. - * @image_rect: location to store area of the original surface occupied - * by the surface stored in @image. - * @image_extra: location to store image specific backend data - * - * Retrieves a local image for a surface for implementing a fallback drawing - * operation. After calling this function, the implementation of the fallback - * drawing operation draws the primitive to the surface stored in @image_out - * then calls _cairo_surface_release_dest_image(), - * which, if a temporary surface was created, copies the bits back to the - * main surface and frees the temporary surface. - * - * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. - * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that - * the backend can't draw with fallbacks. It's possible for the routine - * to store %NULL in @local_out and return %CAIRO_STATUS_SUCCESS; - * that indicates that no part of @interest_rect is visible, so no drawing - * is necessary. _cairo_surface_release_dest_image() should not be called in that - * case. - **/ -cairo_status_t -_cairo_surface_acquire_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - if (surface->backend->acquire_dest_image == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = surface->backend->acquire_dest_image (surface, - interest_rect, - image_out, - image_rect, - image_extra); - if (unlikely (status)) - return _cairo_surface_set_error (surface, status); - - _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); - - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_release_dest_image: - * @surface: a #cairo_surface_t - * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() - * @image: same as returned from the matching _cairo_surface_acquire_dest_image() - * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() - * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() - * - * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if - * necessary, copying the image from @image back to @surface and freeing any - * resources that were allocated. - **/ void -_cairo_surface_release_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) +_cairo_surface_default_release_source_image (void *surface, + cairo_image_surface_t *image, + void *image_extra) { - assert (_cairo_surface_is_writable (surface)); + cairo_status_t ignored; - if (surface->backend->release_dest_image) - surface->backend->release_dest_image (surface, interest_rect, - image, image_rect, image_extra); + ignored = _cairo_surface_unmap_image (surface, image); + (void)ignored; } -static cairo_status_t -_cairo_recording_surface_clone_similar (cairo_surface_t *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_surface_t * +_cairo_surface_get_source (cairo_surface_t *surface, + cairo_rectangle_int_t *extents) { - cairo_recording_surface_t *recorder = (cairo_recording_surface_t *) src; - cairo_surface_t *similar; - cairo_status_t status; - - similar = _cairo_surface_has_snapshot (src, surface->backend); - if (similar != NULL) { - *clone_out = cairo_surface_reference (similar); - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - - if (recorder->unbounded || - width*height*8 < recorder->extents.width*recorder->extents.height) - { - similar = _cairo_surface_create_similar_solid (surface, - src->content, - width, height, - CAIRO_COLOR_TRANSPARENT, - FALSE); - if (similar == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (similar->status)) - return similar->status; - - cairo_surface_set_device_offset (similar, -src_x, -src_y); - - status = _cairo_recording_surface_replay (src, similar); - if (unlikely (status)) { - cairo_surface_destroy (similar); - return status; - } - } else { - similar = _cairo_surface_create_similar_scratch (surface, - src->content, - recorder->extents.width, - recorder->extents.height); - if (similar == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (unlikely (similar->status)) - return similar->status; - - status = _cairo_recording_surface_replay (src, similar); - if (unlikely (status)) { - cairo_surface_destroy (similar); - return status; - } - - cairo_surface_attach_snapshot (src, similar, NULL); - - src_x = src_y = 0; - } - - *clone_out = similar; - *clone_offset_x = src_x; - *clone_offset_y = src_y; - return CAIRO_STATUS_SUCCESS; + assert (surface->backend->source); + return surface->backend->source (surface, extents); } -struct acquire_source_image_data +cairo_surface_t * +_cairo_surface_default_source (void *surface, + cairo_rectangle_int_t *extents) { - cairo_surface_t *src; - cairo_image_surface_t *image; - void *image_extra; -}; - -static void -_wrap_release_source_image (void *data) -{ - struct acquire_source_image_data *acquire_data = data; - _cairo_surface_release_source_image (acquire_data->src, - acquire_data->image, - acquire_data->image_extra); - free(data); -} - -static cairo_status_t -_wrap_image (cairo_surface_t *src, - cairo_image_surface_t *image, - void *image_extra, - cairo_image_surface_t **out) -{ - static cairo_user_data_key_t wrap_image_key; - cairo_image_surface_t *surface; - cairo_status_t status; - - struct acquire_source_image_data *data = malloc (sizeof (*data)); - if (unlikely (data == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - data->src = src; - data->image = image; - data->image_extra = image_extra; - - surface = (cairo_image_surface_t*) - _cairo_image_surface_create_with_pixman_format (image->data, - image->pixman_format, - image->width, - image->height, - image->stride); - status = surface->base.status; - if (status) { - free (data); - return status; - } - - status = _cairo_user_data_array_set_data (&surface->base.user_data, - &wrap_image_key, - data, - _wrap_release_source_image); - if (status) { - cairo_surface_destroy (&surface->base); - free (data); - return status; - } - - pixman_image_set_component_alpha ( - surface->pixman_image, - pixman_image_get_component_alpha (image->pixman_image)); - - *out = surface; - return CAIRO_STATUS_SUCCESS; -} - -/** - * _cairo_surface_clone_similar: - * @surface: a #cairo_surface_t - * @src: the source image - * @src_x: extent for the rectangle in src we actually care about - * @src_y: extent for the rectangle in src we actually care about - * @width: extent for the rectangle in src we actually care about - * @height: extent for the rectangle in src we actually care about - * @clone_out: location to store a surface compatible with @surface - * and with contents identical to @src. The caller must call - * cairo_surface_destroy() on the result. - * - * Creates a surface with contents identical to @src but that - * can be used efficiently with @surface. If @surface and @src are - * already compatible then it may return a new reference to @src. - * - * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored - * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another - * error like %CAIRO_STATUS_NO_MEMORY. - **/ -cairo_status_t -_cairo_surface_clone_similar (cairo_surface_t *surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) -{ - cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_image_surface_t *image; - void *image_extra; - - if (unlikely (surface->status)) - return surface->status; - - if (unlikely (surface->finished)) - return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - -#if CAIRO_HAS_TEE_SURFACE - - if (src->type == CAIRO_SURFACE_TYPE_TEE) { - cairo_surface_t *match; - - match = _cairo_tee_surface_find_match (src, - surface->backend, - src->content); - if (match != NULL) - src = match; - } - -#endif - - if (surface->backend->clone_similar != NULL) { - status = surface->backend->clone_similar (surface, src, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - if (_cairo_surface_is_image (src)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* First check to see if we can replay to a similar surface */ - if (_cairo_surface_is_recording (src)) { - return _cairo_recording_surface_clone_similar (surface, src, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - } - - /* If we failed, try again with an image surface */ - status = _cairo_surface_acquire_source_image (src, &image, &image_extra); - if (status == CAIRO_STATUS_SUCCESS) { - status = _wrap_image(src, image, image_extra, &image); - if (status != CAIRO_STATUS_SUCCESS) { - _cairo_surface_release_source_image (src, image, image_extra); - } else { - status = - surface->backend->clone_similar (surface, &image->base, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - cairo_surface_destroy(&image->base); - } - } - } - } - - /* If we're still unsupported, hit our fallback path to get a clone */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = - _cairo_surface_fallback_clone_similar (surface, src, - src_x, src_y, - width, height, - clone_offset_x, - clone_offset_y, - clone_out); - } - - if (unlikely (status)) - return status; - - /* Update the clone's device_transform (which the underlying surface - * backend knows nothing about) */ - if (*clone_out != src) { - (*clone_out)->device_transform = src->device_transform; - (*clone_out)->device_transform_inverse = src->device_transform_inverse; - } - - return status; -} - -/** - * _cairo_surface_is_similar - * @surface_a: a #cairo_surface_t - * @surface_b: a #cairo_surface_t - * @content: a #cairo_content_t - * - * Find out whether the given surfaces share the same backend, - * and if so, whether they can be considered similar. - * - * The definition of "similar" depends on the backend. In - * general, it means that the surface is equivalent to one - * that would have been generated by a call to cairo_surface_create_similar(). - * - * Return value: %TRUE if the surfaces are similar. - **/ -cairo_bool_t -_cairo_surface_is_similar (cairo_surface_t *surface_a, - cairo_surface_t *surface_b) -{ - if (surface_a->backend != surface_b->backend) - return FALSE; - - if (surface_a->backend->is_similar != NULL) - return surface_a->backend->is_similar (surface_a, surface_b); - - return TRUE; -} - -cairo_status_t -_cairo_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_int_status_t status; - - if (unlikely (dst->status)) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - if (mask) { - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); - } - - if (dst->backend->composite) { - status = dst->backend->composite (op, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (dst, status); - } - - return _cairo_surface_set_error (dst, - _cairo_surface_fallback_composite (op, - src, mask, dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - clip_region)); -} - -/** - * _cairo_surface_fill_rectangle: - * @surface: a #cairo_surface_t - * @op: the operator to apply to the rectangle - * @color: the source color - * @x: X coordinate of rectangle, in backend coordinates - * @y: Y coordinate of rectangle, in backend coordinates - * @width: width of rectangle, in backend coordinates - * @height: height of rectangle, in backend coordinates - * - * Applies an operator to a rectangle using a solid color as the source. - * See _cairo_surface_fill_rectangles() for full details. - * - * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred - **/ -cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - int x, - int y, - int width, - int height) -{ - cairo_rectangle_int_t rect; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - rect.x = x; - rect.y = y; - rect.width = width; - rect.height = height; - - return _cairo_surface_fill_rectangles (surface, op, color, &rect, 1); -} - -/** - * _cairo_surface_fill_region: - * @surface: a #cairo_surface_t - * @op: the operator to apply to the region - * @color: the source color - * @region: the region to modify, in backend coordinates - * - * Applies an operator to a set of rectangles specified as a - * #cairo_region_t using a solid color as the source. - * See _cairo_surface_fill_rectangles() for full details. - * - * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred - **/ -cairo_status_t -_cairo_surface_fill_region (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_region_t *region) -{ - int num_rects; - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_status_t status; - int i; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - num_rects = cairo_region_num_rectangles (region); - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; - - /* catch a common reduction of _cairo_clip_combine_with_surface() */ - if (op == CAIRO_OPERATOR_IN && - _cairo_color_equal (color, CAIRO_COLOR_WHITE)) - { - return CAIRO_STATUS_SUCCESS; - } - - if (num_rects > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (num_rects, - sizeof (cairo_rectangle_int_t)); - if (rects == NULL) { - return _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - } - - for (i = 0; i < num_rects; i++) - cairo_region_get_rectangle (region, i, &rects[i]); - - status = _cairo_surface_fill_rectangles (surface, - op, color, rects, num_rects); - - if (rects != stack_rects) - free (rects); - - return _cairo_surface_set_error (surface, status); -} - -/** - * _cairo_surface_fill_rectangles: - * @surface: a #cairo_surface_t - * @op: the operator to apply to the region - * @color: the source color - * @rects: the rectangles to modify, in backend coordinates - * @num_rects: the number of rectangles in @rects - * - * Applies an operator to a set of rectangles using a solid color - * as the source. Note that even if the operator is an unbounded operator - * such as %CAIRO_OPERATOR_IN, only the given set of rectangles - * is affected. This differs from _cairo_surface_composite_trapezoids() - * where the entire destination rectangle is cleared. - * - * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred - **/ -cairo_status_t -_cairo_surface_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_int_status_t status; - - if (surface->status) - return surface->status; - - assert (_cairo_surface_is_writable (surface)); - - if (num_rects == 0) - return CAIRO_STATUS_SUCCESS; - - if (surface->backend->fill_rectangles) { - status = surface->backend->fill_rectangles (surface, - op, color, - rects, num_rects); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (surface, status); - } - - return _cairo_surface_set_error (surface, - _cairo_surface_fallback_fill_rectangles (surface, - op, color, - rects, num_rects)); + if (extents) + _cairo_surface_get_extents(surface, extents); + return surface; } static cairo_status_t @@ -2076,50 +2145,64 @@ _pattern_has_error (const cairo_pattern_t *pattern) return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_surface_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) +static cairo_bool_t +nothing_to_do (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) { - cairo_status_t status; - cairo_rectangle_int_t extents; + if (_cairo_pattern_is_clear (source)) { + if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) + return TRUE; - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; + if (op == CAIRO_OPERATOR_SOURCE) + op = CAIRO_OPERATOR_CLEAR; + } if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; + return TRUE; - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { + if (op == CAIRO_OPERATOR_ATOP && (surface->content & CAIRO_CONTENT_COLOR) ==0) + return TRUE; + + return FALSE; +} + +cairo_status_t +_cairo_surface_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_int_status_t status; + cairo_bool_t is_clear; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (unlikely (surface->status)) + return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; - } status = _pattern_has_error (source); if (unlikely (status)) return status; - _cairo_surface_begin_modification (surface); + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - if (surface->backend->paint != NULL) { - status = surface->backend->paint (surface, op, source, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) + return status; + + 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) { + surface->is_clear = is_clear; + surface->serial++; } - status = _cairo_surface_fallback_paint (surface, op, source, clip); - - FINISH: - surface->is_clear = op == CAIRO_OPERATOR_CLEAR && - (clip == NULL || - (_cairo_surface_get_extents (surface, &extents) && - _cairo_clip_contains_rectangle (clip, &extents))); - return _cairo_surface_set_error (surface, status); } @@ -2128,17 +2211,17 @@ _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_status_t status; + cairo_int_status_t status; + TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; /* If the mask is blank, this is just an expensive no-op */ @@ -2148,12 +2231,6 @@ _cairo_surface_mask (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - status = _pattern_has_error (source); if (unlikely (status)) return status; @@ -2162,19 +2239,19 @@ _cairo_surface_mask (cairo_surface_t *surface, if (unlikely (status)) return status; - _cairo_surface_begin_modification (surface); + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - if (surface->backend->mask != NULL) { - status = surface->backend->mask (surface, op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) + return status; + + status = surface->backend->mask (surface, op, source, mask, clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + surface->is_clear = FALSE; + surface->serial++; } - status = _cairo_surface_fallback_mask (surface, op, source, mask, clip); - - FINISH: - surface->is_clear = FALSE; - return _cairo_surface_set_error (surface, status); } @@ -2193,14 +2270,17 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_status_t status; + cairo_int_status_t status; + TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - if (clip && clip->all_clipped) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; if (surface->is_clear && @@ -2218,7 +2298,9 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, if (unlikely (status)) return status; - _cairo_surface_begin_modification (surface); + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) + return status; if (surface->backend->fill_stroke) { cairo_matrix_t dev_ctm = *stroke_ctm; @@ -2252,218 +2334,103 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, goto FINISH; FINISH: - surface->is_clear = FALSE; + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + surface->is_clear = FALSE; + surface->serial++; + } return _cairo_surface_set_error (surface, status); } cairo_status_t -_cairo_surface_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, +_cairo_surface_stroke (cairo_surface_t *surface, + 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, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->stroke != NULL) { - status = surface->backend->stroke (surface, op, source, - path, stroke_style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fallback_stroke (surface, op, source, - path, stroke_style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - FINISH: - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_status_t status; - - if (unlikely (surface->status)) - return surface->status; - - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_clear (source)) - { - return CAIRO_STATUS_SUCCESS; - } - - status = _pattern_has_error (source); - if (unlikely (status)) - return status; - - _cairo_surface_begin_modification (surface); - - if (surface->backend->fill != NULL) { - status = surface->backend->fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } - - status = _cairo_surface_fallback_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - - FINISH: - surface->is_clear = FALSE; - - return _cairo_surface_set_error (surface, status); -} - -cairo_status_t -_cairo_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *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) + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_int_status_t status; - if (dst->status) - return dst->status; + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (unlikely (surface->status)) + return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - assert (_cairo_surface_is_writable (dst)); + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; - /* These operators aren't interpreted the same way by the backends; - * they are implemented in terms of other operators in cairo-gstate.c - */ - assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR); + status = _pattern_has_error (source); + if (unlikely (status)) + return status; - if (dst->backend->composite_trapezoids) { - status = dst->backend->composite_trapezoids (op, - pattern, dst, - antialias, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return _cairo_surface_set_error (dst, status); + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) + return status; + + status = surface->backend->stroke (surface, op, source, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + surface->is_clear = FALSE; + surface->serial++; } - return _cairo_surface_set_error (dst, - _cairo_surface_fallback_composite_trapezoids (op, pattern, dst, - antialias, - src_x, src_y, - dst_x, dst_y, - width, height, - traps, num_traps, - clip_region)); + return _cairo_surface_set_error (surface, status); } -cairo_span_renderer_t * -_cairo_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region) +cairo_status_t +_cairo_surface_fill (cairo_surface_t *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) { - assert (dst->snapshot_of == NULL); + cairo_int_status_t status; - if (unlikely (dst->status)) - return _cairo_span_renderer_create_in_error (dst->status); + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (unlikely (surface->status)) + return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - if (unlikely (dst->finished)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; - if (dst->backend->create_span_renderer) { - return dst->backend->create_span_renderer (op, - pattern, dst, - antialias, - rects, - clip_region); + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) + return status; + + status = surface->backend->fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + surface->is_clear = FALSE; + surface->serial++; } - ASSERT_NOT_REACHED; - return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED); -} -cairo_bool_t -_cairo_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias) -{ - assert (dst->snapshot_of == NULL); - assert (dst->status == CAIRO_STATUS_SUCCESS); - assert (! dst->finished); - - /* XXX: Currently we have no mono span renderer */ - if (antialias == CAIRO_ANTIALIAS_NONE) - return FALSE; - - if (dst->backend->check_span_renderer != NULL) - return dst->backend->check_span_renderer (op, pattern, dst, antialias); - - return FALSE; + return _cairo_surface_set_error (surface, status); } /** @@ -2479,20 +2446,17 @@ _cairo_surface_check_span_renderer (cairo_operator_t op, * namely cairo_copy_page(). * * Since: 1.6 - */ + **/ void cairo_surface_copy_page (cairo_surface_t *surface) { - cairo_status_t status_ignored; - - if (surface->status) + if (unlikely (surface->status)) return; assert (surface->snapshot_of == NULL); - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - CAIRO_STATUS_SURFACE_FINISHED); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } @@ -2500,8 +2464,7 @@ cairo_surface_copy_page (cairo_surface_t *surface) if (surface->backend->copy_page == NULL) return; - status_ignored = _cairo_surface_set_error (surface, - surface->backend->copy_page (surface)); + _cairo_surface_set_error (surface, surface->backend->copy_page (surface)); } slim_hidden_def (cairo_surface_copy_page); @@ -2520,25 +2483,27 @@ slim_hidden_def (cairo_surface_copy_page); void cairo_surface_show_page (cairo_surface_t *surface) { - cairo_status_t status_ignored; + cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return; - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - CAIRO_STATUS_SURFACE_FINISHED); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return; } - _cairo_surface_begin_modification (surface); + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) { + _cairo_surface_set_error (surface, status); + return; + } /* It's fine if some backends don't implement show_page */ if (surface->backend->show_page == NULL) return; - status_ignored = _cairo_surface_set_error (surface, - surface->backend->show_page (surface)); + _cairo_surface_set_error (surface, surface->backend->show_page (surface)); } slim_hidden_def (cairo_surface_show_page); @@ -2565,21 +2530,33 @@ slim_hidden_def (cairo_surface_show_page); * * This behavior would have to be changed is we ever exported a public * variant of this function. - */ + **/ cairo_bool_t _cairo_surface_get_extents (cairo_surface_t *surface, cairo_rectangle_int_t *extents) { cairo_bool_t bounded; + if (unlikely (surface->status)) + goto zero_extents; + if (unlikely (surface->finished)) { + _cairo_surface_set_error(surface, CAIRO_STATUS_SURFACE_FINISHED); + goto zero_extents; + } + bounded = FALSE; - if (! surface->status && surface->backend->get_extents != NULL) + if (surface->backend->get_extents != NULL) bounded = surface->backend->get_extents (surface, extents); if (! bounded) _cairo_unbounded_rectangle_init (extents); return bounded; + +zero_extents: + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return TRUE; } /** @@ -2606,14 +2583,11 @@ _cairo_surface_get_extents (cairo_surface_t *surface, cairo_bool_t cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) { - cairo_status_t status_ignored; - - if (surface->status) + if (unlikely (surface->status)) return FALSE; - if (surface->finished) { - status_ignored = _cairo_surface_set_error (surface, - CAIRO_STATUS_SURFACE_FINISHED); + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); return FALSE; } @@ -2624,56 +2598,199 @@ cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) } slim_hidden_def (cairo_surface_has_show_text_glyphs); -/** - * cairo_surface_set_subpixel_antialiasing: - * @surface: a #cairo_surface_t - * - * Sets whether the surface permits subpixel antialiasing. By default, - * surfaces permit subpixel antialiasing. - * - * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally - * requires that the pixels in the areas under a subpixel antialiasing - * operation already be opaque. - * - * Since: 1.12 - **/ -void -cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, - cairo_subpixel_antialiasing_t enabled) -{ - if (surface->status) - return; +#define GLYPH_CACHE_SIZE 64 - if (surface->finished) { - _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); - return; +static inline cairo_int_status_t +ensure_scaled_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t **glyph_cache, + cairo_glyph_t *glyph, + cairo_scaled_glyph_t **scaled_glyph) +{ + int cache_index; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + cache_index = glyph->index % GLYPH_CACHE_SIZE; + *scaled_glyph = glyph_cache[cache_index]; + if (*scaled_glyph == NULL || _cairo_scaled_glyph_index (*scaled_glyph) != glyph->index) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph->index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + scaled_glyph); + if (unlikely (status)) + status = _cairo_scaled_font_set_error (scaled_font, status); + + glyph_cache[cache_index] = *scaled_glyph; } - surface->permit_subpixel_antialiasing = - enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; + return status; } -slim_hidden_def (cairo_surface_set_subpixel_antialiasing); -/** - * cairo_surface_get_subpixel_antialiasing: - * @surface: a #cairo_surface_t - * - * Gets whether the surface supports subpixel antialiasing. By default, - * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other - * surfaces do not. - * - * Since: 1.12 - **/ -cairo_subpixel_antialiasing_t -cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) +static inline cairo_int_status_t +composite_one_color_glyph (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip, + cairo_glyph_t *glyph, + cairo_scaled_glyph_t *scaled_glyph) { - if (surface->status) - return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; + cairo_int_status_t status; + cairo_image_surface_t *glyph_surface; + cairo_pattern_t *pattern; + cairo_matrix_t matrix; - return surface->permit_subpixel_antialiasing ? - CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; + status = CAIRO_INT_STATUS_SUCCESS; + + glyph_surface = scaled_glyph->color_surface; + + if (glyph_surface->width && glyph_surface->height) { + int x, y; + /* round glyph locations to the nearest pixels */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (glyph->x - glyph_surface->base.device_transform.x0); + y = _cairo_lround (glyph->y - glyph_surface->base.device_transform.y0); + + pattern = cairo_pattern_create_for_surface ((cairo_surface_t *)glyph_surface); + cairo_matrix_init_translate (&matrix, - x, - y); + cairo_pattern_set_matrix (pattern, &matrix); + if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) + status = surface->backend->mask (surface, op, pattern, pattern, clip); + else + status = surface->backend->paint (surface, op, pattern, clip); + cairo_pattern_destroy (pattern); + } + + return status; +} + +static cairo_int_status_t +composite_color_glyphs (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + char *utf8, + int *utf8_len, + cairo_glyph_t *glyphs, + int *num_glyphs, + cairo_text_cluster_t *clusters, + int *num_clusters, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_int_status_t status; + int i, j; + cairo_scaled_glyph_t *scaled_glyph; + int remaining_clusters = 0; + int remaining_glyphs = 0; + int remaining_bytes = 0; + int glyph_pos = 0; + int byte_pos = 0; + int gp; + cairo_scaled_glyph_t *glyph_cache[GLYPH_CACHE_SIZE]; + + memset (glyph_cache, 0, sizeof (glyph_cache)); + + status = CAIRO_INT_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (scaled_font); + + if (clusters) { + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + glyph_pos = *num_glyphs - 1; + + for (i = 0; i < *num_clusters; i++) { + cairo_bool_t skip_cluster = FALSE; + + for (j = 0; j < clusters[i].num_glyphs; j++) { + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + gp = glyph_pos - j; + else + gp = glyph_pos + j; + + status = ensure_scaled_glyph (scaled_font, glyph_cache, + &glyphs[gp], &scaled_glyph); + if (unlikely (status)) + goto UNLOCK; + + if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0) { + skip_cluster = TRUE; + break; + } + } + + if (skip_cluster) { + memmove (utf8 + remaining_bytes, utf8 + byte_pos, clusters[i].num_bytes); + remaining_bytes += clusters[i].num_bytes; + byte_pos += clusters[i].num_bytes; + for (j = 0; j < clusters[i].num_glyphs; j++, remaining_glyphs++) { + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + glyphs[*num_glyphs - 1 - remaining_glyphs] = glyphs[glyph_pos--]; + else + glyphs[remaining_glyphs] = glyphs[glyph_pos++]; + } + clusters[remaining_clusters++] = clusters[i]; + continue; + } + + for (j = 0; j < clusters[i].num_glyphs; j++) { + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + gp = glyph_pos - j; + else + gp = glyph_pos + j; + + status = ensure_scaled_glyph (scaled_font, glyph_cache, + &glyphs[gp], &scaled_glyph); + if (unlikely (status)) + goto UNLOCK; + + status = composite_one_color_glyph (surface, op, source, clip, + &glyphs[gp], scaled_glyph); + if (unlikely (status && status != CAIRO_INT_STATUS_NOTHING_TO_DO)) + goto UNLOCK; + } + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + glyph_pos -= clusters[i].num_glyphs; + else + glyph_pos += clusters[i].num_glyphs; + + byte_pos += clusters[i].num_bytes; + } + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + memmove (utf8, utf8 + *utf8_len - remaining_bytes, remaining_bytes); + + *utf8_len = remaining_bytes; + *num_glyphs = remaining_glyphs; + *num_clusters = remaining_clusters; + + } else { + + for (glyph_pos = 0; glyph_pos < *num_glyphs; glyph_pos++) { + status = ensure_scaled_glyph (scaled_font, glyph_cache, + &glyphs[glyph_pos], &scaled_glyph); + if (unlikely (status)) + goto UNLOCK; + + if ((scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) == 0) { + glyphs[remaining_glyphs++] = glyphs[glyph_pos]; + continue; + } + + status = composite_one_color_glyph (surface, op, source, clip, + &glyphs[glyph_pos], scaled_glyph); + if (unlikely (status && status != CAIRO_INT_STATUS_NOTHING_TO_DO)) + goto UNLOCK; + } + + *num_glyphs = remaining_glyphs; + } + +UNLOCK: + _cairo_scaled_font_thaw_cache (scaled_font); + + return status; } -slim_hidden_def (cairo_surface_get_subpixel_antialiasing); /* Note: the backends may modify the contents of the glyph array as long as * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to @@ -2702,50 +2819,58 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_status_t status; - cairo_scaled_font_t *dev_scaled_font = scaled_font; + cairo_int_status_t status; + char *utf8_copy = NULL; + TRACE ((stderr, "%s\n", __FUNCTION__)); if (unlikely (surface->status)) return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (num_glyphs == 0 && utf8_len == 0) return CAIRO_STATUS_SUCCESS; - if (clip && clip->all_clipped) - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; status = _pattern_has_error (source); if (unlikely (status)) return status; - _cairo_surface_begin_modification (surface); + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - if (_cairo_surface_has_device_transform (surface) && - ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL)) - { - cairo_font_options_t font_options; - cairo_matrix_t dev_ctm, font_matrix; - - cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); - cairo_scaled_font_get_ctm (scaled_font, &dev_ctm); - cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform); - cairo_scaled_font_get_font_options (scaled_font, &font_options); - dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font), - &font_matrix, - &dev_ctm, - &font_options); - } - status = cairo_scaled_font_status (dev_scaled_font); + status = _cairo_surface_begin_modification (surface); if (unlikely (status)) - return _cairo_surface_set_error (surface, status); + return status; status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_scaled_font_has_color_glyphs (scaled_font)) { + utf8_copy = malloc (sizeof (char) * utf8_len); + memcpy (utf8_copy, utf8, sizeof (char) * utf8_len); + utf8 = utf8_copy; + + status = composite_color_glyphs (surface, op, + source, + (char *)utf8, &utf8_len, + glyphs, &num_glyphs, + (cairo_text_cluster_t *)clusters, &num_clusters, cluster_flags, + scaled_font, + clip); + + if (unlikely (status && status != CAIRO_INT_STATUS_NOTHING_TO_DO)) + goto DONE; + + if (num_glyphs == 0) + goto DONE; + } + else + utf8_copy = NULL; + /* The logic here is duplicated in _cairo_analysis_surface show_glyphs and * show_text_glyphs. Keep in synch. */ if (clusters) { @@ -2757,38 +2882,26 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - dev_scaled_font, + scaled_font, clip); } if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->backend->show_glyphs) { - int remaining_glyphs = num_glyphs; status = surface->backend->show_glyphs (surface, op, source, glyphs, num_glyphs, - dev_scaled_font, - clip, - &remaining_glyphs); - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; + scaled_font, + clip); } } else { /* A mere show_glyphs call. Try show_glyphs backend method first */ if (surface->backend->show_glyphs != NULL) { - int remaining_glyphs = num_glyphs; status = surface->backend->show_glyphs (surface, op, source, glyphs, num_glyphs, - dev_scaled_font, - clip, - &remaining_glyphs); - glyphs += num_glyphs - remaining_glyphs; - num_glyphs = remaining_glyphs; - if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0) - status = CAIRO_STATUS_SUCCESS; + scaled_font, + clip); } else if (surface->backend->show_text_glyphs != NULL) { /* Intentionally only try show_text_glyphs method for show_glyphs * calls if backend does not have show_glyphs. If backend has @@ -2803,280 +2916,62 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, cluster_flags, - dev_scaled_font, + scaled_font, clip); } } - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_surface_fallback_show_glyphs (surface, op, - source, - glyphs, num_glyphs, - dev_scaled_font, - clip); +DONE: + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + surface->is_clear = FALSE; + surface->serial++; } - if (dev_scaled_font != scaled_font) - cairo_scaled_font_destroy (dev_scaled_font); + if (utf8_copy) + free (utf8_copy); + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_tag (cairo_surface_t *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip) +{ + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (unlikely (surface->status)) + return surface->status; + if (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + if (surface->backend->tag == NULL) + return CAIRO_STATUS_SUCCESS; + + if (begin) { + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + } + + status = surface->backend->tag (surface, begin, tag_name, attributes, + source, stroke_style, + ctm, ctm_inverse, clip); surface->is_clear = FALSE; return _cairo_surface_set_error (surface, status); } -/* XXX: Previously, we had a function named _cairo_surface_show_glyphs - * with not-so-useful semantics. We've now got a - * _cairo_surface_show_text_glyphs with the proper semantics, and its - * fallback still uses this old function (which still needs to be - * cleaned up in terms of both semantics and naming). */ -cairo_status_t -_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region) -{ - cairo_status_t status; - - if (dst->status) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - if (dst->backend->old_show_glyphs) { - status = dst->backend->old_show_glyphs (scaled_font, - op, pattern, dst, - source_x, source_y, - dest_x, dest_y, - width, height, - glyphs, num_glyphs, - clip_region); - } else - status = CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_surface_set_error (dst, status); -} - -static cairo_status_t -_cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, - cairo_rectangle_int_t *src_rectangle, - cairo_rectangle_int_t *mask_rectangle, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_rectangle_int_t dst_rectangle; - cairo_region_t clear_region; - cairo_status_t status; - - /* The area that was drawn is the area in the destination rectangle but - * not within the source or the mask. - */ - dst_rectangle.x = dst_x; - dst_rectangle.y = dst_y; - dst_rectangle.width = width; - dst_rectangle.height = height; - - _cairo_region_init_rectangle (&clear_region, &dst_rectangle); - - if (clip_region != NULL) { - status = cairo_region_intersect (&clear_region, clip_region); - if (unlikely (status)) - goto CLEANUP_REGIONS; - } - - if (src_rectangle != NULL) { - if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle)) - goto EMPTY; - } - - if (mask_rectangle != NULL) { - if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle)) - goto EMPTY; - } - - /* Now compute the area that is in dst but not drawn */ - status = cairo_region_subtract_rectangle (&clear_region, &dst_rectangle); - if (unlikely (status) || cairo_region_is_empty (&clear_region)) - goto CLEANUP_REGIONS; - - EMPTY: - status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - &clear_region); - - CLEANUP_REGIONS: - _cairo_region_fini (&clear_region); - - return _cairo_surface_set_error (dst, status); -} /** - * _cairo_surface_composite_fixup_unbounded: - * @dst: the destination surface - * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface()) - * @src_width: width of source surface - * @src_height: height of source surface - * @mask_attr: mask surface attributes or %NULL if no mask - * @mask_width: width of mask surface - * @mask_height: height of mask surface - * @src_x: @src_x from _cairo_surface_composite() - * @src_y: @src_y from _cairo_surface_composite() - * @mask_x: @mask_x from _cairo_surface_composite() - * @mask_y: @mask_y from _cairo_surface_composite() - * @dst_x: @dst_x from _cairo_surface_composite() - * @dst_y: @dst_y from _cairo_surface_composite() - * @width: @width from _cairo_surface_composite() - * @height: @height_x from _cairo_surface_composite() - * - * Eeek! Too many parameters! This is a helper function to take care of fixing - * up for bugs in libpixman and RENDER where, when asked to composite an - * untransformed surface with an unbounded operator (like CLEAR or SOURCE) - * only the region inside both the source and the mask is affected. - * This function clears the region that should have been drawn but was wasn't. - **/ -cairo_status_t -_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - cairo_surface_attributes_t *mask_attr, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_rectangle_int_t src_tmp, mask_tmp; - cairo_rectangle_int_t *src_rectangle = NULL; - cairo_rectangle_int_t *mask_rectangle = NULL; - - if (unlikely (dst->status)) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, - * non-repeating sources and masks. Other sources and masks can be ignored. - */ - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - src_attr->extend == CAIRO_EXTEND_NONE) - { - src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); - src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); - src_tmp.width = src_width; - src_tmp.height = src_height; - - src_rectangle = &src_tmp; - } - - if (mask_attr && - _cairo_matrix_is_integer_translation (&mask_attr->matrix, NULL, NULL) && - mask_attr->extend == CAIRO_EXTEND_NONE) - { - mask_tmp.x = (dst_x - (mask_x + mask_attr->x_offset)); - mask_tmp.y = (dst_y - (mask_y + mask_attr->y_offset)); - mask_tmp.width = mask_width; - mask_tmp.height = mask_height; - - mask_rectangle = &mask_tmp; - } - - return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle, - dst_x, dst_y, width, height, - clip_region); -} - -/** - * _cairo_surface_composite_shape_fixup_unbounded: - * @dst: the destination surface - * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface()) - * @src_width: width of source surface - * @src_height: height of source surface - * @mask_width: width of mask surface - * @mask_height: height of mask surface - * @src_x: @src_x from _cairo_surface_composite() - * @src_y: @src_y from _cairo_surface_composite() - * @mask_x: @mask_x from _cairo_surface_composite() - * @mask_y: @mask_y from _cairo_surface_composite() - * @dst_x: @dst_x from _cairo_surface_composite() - * @dst_y: @dst_y from _cairo_surface_composite() - * @width: @width from _cairo_surface_composite() - * @height: @height_x from _cairo_surface_composite() - * - * Like _cairo_surface_composite_fixup_unbounded(), but instead of - * handling the case where we have a source pattern and a mask - * pattern, handle the case where we are compositing a source pattern - * using a mask we create ourselves, as in - * _cairo_surface_composite_glyphs() or _cairo_surface_composite_trapezoids() - **/ -cairo_status_t -_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_rectangle_int_t src_tmp, *src= NULL; - cairo_rectangle_int_t mask; - - if (dst->status) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - /* The RENDER/libpixman operators are clipped to the bounds of the untransformed, - * non-repeating sources and masks. Other sources and masks can be ignored. - */ - if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - src_attr->extend == CAIRO_EXTEND_NONE) - { - src_tmp.x = (dst_x - (src_x + src_attr->x_offset)); - src_tmp.y = (dst_y - (src_y + src_attr->y_offset)); - src_tmp.width = src_width; - src_tmp.height = src_height; - - src = &src_tmp; - } - - mask.x = dst_x - mask_x; - mask.y = dst_y - mask_y; - mask.width = mask_width; - mask.height = mask_height; - - return _cairo_surface_composite_fixup_unbounded_internal (dst, src, &mask, - dst_x, dst_y, width, height, - clip_region); -} - -/** - * _cairo_surface_set_resolution + * _cairo_surface_set_resolution: * @surface: the surface * @x_res: x resolution, in dpi * @y_res: y resolution, in dpi @@ -3084,7 +2979,7 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, * Set the actual surface resolution of @surface to the given x and y DPI. * Mainly used for correctly computing the scale factor when fallback * rendering needs to take place in the paginated surface. - */ + **/ void _cairo_surface_set_resolution (cairo_surface_t *surface, double x_res, @@ -3097,171 +2992,20 @@ _cairo_surface_set_resolution (cairo_surface_t *surface, surface->y_resolution = y_res; } -/* Generic methods for determining operation extents. */ - -static void -_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip) -{ - const cairo_rectangle_int_t *clip_extents; - cairo_bool_t is_empty; - - clip_extents = NULL; - if (clip != NULL) - clip_extents = _cairo_clip_get_extents (clip); - - if (clip_extents != NULL) - is_empty = _cairo_rectangle_intersect (extents, clip_extents); -} - -static void -_cairo_surface_operation_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - is_empty = _cairo_surface_get_extents (surface, extents); - - if (_cairo_operator_bounded_by_source (op)) { - cairo_rectangle_int_t source_extents; - - _cairo_pattern_get_extents (source, &source_extents); - is_empty = _cairo_rectangle_intersect (extents, &source_extents); - } - - _rectangle_intersect_clip (extents, clip); -} - -cairo_status_t -_cairo_surface_paint_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - _cairo_surface_operation_extents (surface, op, source, clip, extents); - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_mask_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - _cairo_pattern_get_extents (mask, &mask_extents); - is_empty = _cairo_rectangle_intersect (extents, &mask_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_stroke_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &mask_extents); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (extents, &mask_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_fill_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t mask_extents; - - _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &mask_extents); - is_empty = _cairo_rectangle_intersect (extents, &mask_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - -cairo_status_t -_cairo_surface_glyphs_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_bool_t is_empty; - - _cairo_surface_operation_extents (surface, op, source, clip, extents); - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t glyph_extents; - - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - return status; - - is_empty = _cairo_rectangle_intersect (extents, &glyph_extents); - } - - return CAIRO_STATUS_SUCCESS; -} - +/** + * _cairo_surface_create_in_error: + * @status: the error status + * + * Return an appropriate static error surface for the error status. + * On error, surface creation functions should always return a surface + * created with _cairo_surface_create_in_error() instead of a new surface + * in an error state. This simplifies internal code as no refcounting has + * to be done. + **/ cairo_surface_t * _cairo_surface_create_in_error (cairo_status_t status) { + assert (status < CAIRO_STATUS_LAST_STATUS); switch (status) { case CAIRO_STATUS_NO_MEMORY: return (cairo_surface_t *) &_cairo_surface_nil; @@ -3316,6 +3060,30 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_SLANT: case CAIRO_STATUS_INVALID_WEIGHT: case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: + case CAIRO_STATUS_DEVICE_FINISHED: + case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: + case CAIRO_STATUS_PNG_ERROR: + case CAIRO_STATUS_FREETYPE_ERROR: + case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_STATUS_TAG_ERROR: + default: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } +} + +cairo_surface_t * +_cairo_int_surface_create_in_error (cairo_int_status_t status) +{ + if (status < CAIRO_INT_STATUS_LAST_STATUS) + return _cairo_surface_create_in_error (status); + + switch ((int)status) { + case CAIRO_INT_STATUS_UNSUPPORTED: + return (cairo_surface_t *) &_cairo_surface_nil_unsupported; + case CAIRO_INT_STATUS_NOTHING_TO_DO: + return (cairo_surface_t *) &_cairo_surface_nil_nothing_to_do; default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_surface_t *) &_cairo_surface_nil; diff --git a/gfx/cairo/cairo/src/cairo-svg-surface-private.h b/gfx/cairo/cairo/src/cairo-svg-surface-private.h index ddbf464b1d29..6f693252a89c 100644 --- a/gfx/cairo/cairo/src/cairo-svg-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-svg-surface-private.h @@ -48,6 +48,13 @@ typedef struct cairo_svg_document cairo_svg_document_t; +typedef struct _cairo_svg_source_surface { + cairo_hash_entry_t base; + unsigned int id; + unsigned char *unique_id; + unsigned long unique_id_length; +} cairo_svg_source_surface_t; + typedef struct cairo_svg_surface { cairo_surface_t base; @@ -55,11 +62,13 @@ typedef struct cairo_svg_surface { double width; double height; + cairo_bool_t surface_bounded; cairo_svg_document_t *document; cairo_output_stream_t *xml_node; cairo_array_t page_set; + cairo_hash_table_t *source_surfaces; cairo_surface_clipper_t clipper; unsigned int clip_level; diff --git a/gfx/cairo/cairo/src/cairo-svg-surface.c b/gfx/cairo/cairo/src/cairo-svg-surface.c index 71a6564ae663..7e7051eb68df 100644 --- a/gfx/cairo/cairo/src/cairo-svg-surface.c +++ b/gfx/cairo/cairo/src/cairo-svg-surface.c @@ -1,4 +1,4 @@ -/* vim: set sw=2 sts=2: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* vim: set sw=4 sts=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 @@ -39,18 +39,24 @@ * Carl Worth */ -#define _BSD_SOURCE /* for snprintf() */ +#define _DEFAULT_SOURCE /* for snprintf() */ #include "cairoint.h" + #include "cairo-svg.h" + +#include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-info-private.h" -#include "cairo-recording-surface-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-recording-surface-inline.h" #include "cairo-output-stream-private.h" #include "cairo-path-fixed-private.h" #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-clipper-private.h" +#include "cairo-surface-snapshot-inline.h" #include "cairo-svg-surface-private.h" /** @@ -61,14 +67,16 @@ * * The SVG surface is used to render cairo graphics to * SVG files and is a multi-page vector surface backend. - */ + **/ /** * CAIRO_HAS_SVG_SURFACE: * * Defined if the SVG surface backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.2 + **/ typedef struct cairo_svg_page cairo_svg_page_t; @@ -82,9 +90,18 @@ static const cairo_svg_version_t _cairo_svg_versions[] = #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions) +static const char *_cairo_svg_supported_mime_types[] = +{ + CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_PNG, + CAIRO_MIME_TYPE_UNIQUE_ID, + CAIRO_MIME_TYPE_URI, + NULL +}; + static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse); static cairo_bool_t @@ -105,6 +122,20 @@ static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] "1.2" }; +static const char * _cairo_svg_unit_strings[] = +{ + "", + "em", + "ex", + "px", + "in", + "cm", + "mm", + "pt", + "pc", + "%" +}; + struct cairo_svg_page { unsigned int surface_id; unsigned int clip_level; @@ -119,6 +150,7 @@ struct cairo_svg_document { double width; double height; + cairo_svg_unit_t unit; cairo_output_stream_t *xml_node_defs; cairo_output_stream_t *xml_node_glyphs; @@ -160,7 +192,9 @@ static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, cairo_content_t content, double width, - double height); + double height, + cairo_bool_t bounded); + static cairo_surface_t * _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, double width, @@ -192,7 +226,7 @@ static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backe * occurs. You can use cairo_surface_status() to check for this. * * Since: 1.2 - */ + **/ cairo_surface_t * cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, void *closure, @@ -236,6 +270,9 @@ cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is * Base64-encoded and emitted. * + * If %CAIRO_MIME_TYPE_UNIQUE_ID is present, all surfaces with the same + * unique identifier will only be embedded once. + * * Return value: a pointer to the newly created surface. The caller * owns the surface and should call cairo_surface_destroy() when done * with it. @@ -383,6 +420,164 @@ cairo_svg_version_to_string (cairo_svg_version_t version) return _cairo_svg_version_strings[version]; } +/** + * cairo_svg_surface_set_document_unit: + * @surface: a SVG #cairo_surface_t + * @unit: SVG unit + * + * Use the specified unit for the width and height of the generated SVG file. + * See #cairo_svg_unit_t for a list of available unit values that can be used + * here. + * + * This function can be called at any time before generating the SVG file. + * + * However to minimize the risk of ambiguities it's recommended to call it + * before any drawing operations have been performed on the given surface, to + * make it clearer what the unit used in the drawing operations is. + * + * The simplest way to do this is to call this function immediately after + * creating the SVG surface. + * + * Note if this function is never called, the default unit for SVG documents + * generated by cairo will be "pt". This is for historical reasons. + * + * Since: 1.16 + **/ +void +cairo_svg_surface_set_document_unit (cairo_surface_t *abstract_surface, + cairo_svg_unit_t unit) +{ + cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ + + if (! _extract_svg_surface (abstract_surface, &surface)) + return; + + if (unit <= CAIRO_SVG_UNIT_PERCENT) + surface->document->unit = unit; +} + +/** + * cairo_svg_surface_get_document_unit: + * @surface: a SVG #cairo_surface_t + * + * Get the unit of the SVG surface. + * + * If the surface passed as an argument is not a SVG surface, the function + * sets the error status to CAIRO_STATUS_SURFACE_TYPE_MISMATCH and returns + * CAIRO_SVG_UNIT_USER. + * + * Return value: the SVG unit of the SVG surface. + * + * Since: 1.16 + **/ +cairo_svg_unit_t +cairo_svg_surface_get_document_unit (cairo_surface_t *abstract_surface) +{ + cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ + + if (! _extract_svg_surface (abstract_surface, &surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return CAIRO_SVG_UNIT_USER; + } + + return surface->document->unit; +} + +static void +_cairo_svg_source_surface_init_key (cairo_svg_source_surface_t *key) +{ + if (key->unique_id && key->unique_id_length > 0) { + key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE, + key->unique_id, key->unique_id_length); + } else { + key->base.hash = key->id; + } +} + +static cairo_bool_t +_cairo_svg_source_surface_equal (const void *key_a, const void *key_b) +{ + const cairo_svg_source_surface_t *a = key_a; + const cairo_svg_source_surface_t *b = key_b; + + if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length) + return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0); + + return (a->id == b->id); +} + +static void +_cairo_svg_source_surface_pluck (void *entry, void *closure) +{ + cairo_svg_source_surface_t *surface_entry = entry; + cairo_hash_table_t *patterns = closure; + + _cairo_hash_table_remove (patterns, &surface_entry->base); + free (surface_entry->unique_id); + free (surface_entry); +} + +static cairo_status_t +_cairo_svg_surface_add_source_surface (cairo_svg_surface_t *surface, + cairo_surface_t *source_surface, + int *source_id, + cairo_bool_t *is_new) +{ + cairo_svg_source_surface_t source_key; + cairo_svg_source_surface_t *source_entry; + unsigned char *unique_id = NULL; + unsigned long unique_id_length = 0; + cairo_status_t status; + + source_key.id = source_surface->unique_id; + cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID, + (const unsigned char **) &source_key.unique_id, + &source_key.unique_id_length); + _cairo_svg_source_surface_init_key (&source_key); + source_entry = _cairo_hash_table_lookup (surface->source_surfaces, &source_key.base); + if (source_entry) { + *source_id = source_entry->id; + *is_new = FALSE; + return CAIRO_STATUS_SUCCESS; + } + + if (source_key.unique_id && source_key.unique_id_length > 0) { + unique_id = _cairo_malloc (source_key.unique_id_length); + if (unique_id == NULL) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + unique_id_length = source_key.unique_id_length; + memcpy (unique_id, source_key.unique_id, unique_id_length); + } else { + unique_id = NULL; + unique_id_length = 0; + } + + source_entry = malloc (sizeof (cairo_svg_source_surface_t)); + if (source_entry == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + source_entry->id = source_key.id; + source_entry->unique_id_length = unique_id_length; + source_entry->unique_id = unique_id; + _cairo_svg_source_surface_init_key (source_entry); + status = _cairo_hash_table_insert (surface->source_surfaces, &source_entry->base); + if (unlikely(status)) + goto fail; + + *is_new = TRUE; + *source_id = source_entry->id; + return CAIRO_STATUS_SUCCESS; + + fail: + free (unique_id); + free (source_entry); + return status; +} + static cairo_bool_t _cliprect_covers_surface (cairo_svg_surface_t *surface, cairo_path_fixed_t *path) @@ -454,23 +649,26 @@ static cairo_surface_t * _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, cairo_content_t content, double width, - double height) + double height, + cairo_bool_t bounded) { cairo_svg_surface_t *surface; cairo_surface_t *paginated; cairo_status_t status, status_ignored; - surface = malloc (sizeof (cairo_svg_surface_t)); + surface = _cairo_malloc (sizeof (cairo_svg_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_svg_surface_backend, NULL, /* device */ - content); + content, + TRUE); /* is_vector */ surface->width = width; surface->height = height; + surface->surface_bounded = bounded; surface->document = _cairo_svg_document_reference (document); @@ -503,6 +701,12 @@ _cairo_svg_surface_create_for_document (cairo_svg_document_t *document, surface->force_fallbacks = FALSE; surface->content = content; + surface->source_surfaces = _cairo_hash_table_create (_cairo_svg_source_surface_equal); + if (unlikely (surface->source_surfaces == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + paginated = _cairo_paginated_surface_create (&surface->base, surface->content, &cairo_svg_surface_paginated_backend); @@ -544,7 +748,7 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, } surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, - width, height); + width, height, TRUE); if (surface->status) { status = _cairo_svg_document_destroy (document); return surface; @@ -561,10 +765,10 @@ _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, static cairo_svg_page_t * _cairo_svg_surface_store_page (cairo_svg_surface_t *surface) { - unsigned int i; cairo_svg_page_t page; cairo_output_stream_t *stream; - cairo_status_t status; + cairo_int_status_t status; + unsigned int i; stream = _cairo_memory_stream_create (); if (_cairo_output_stream_get_status (stream)) { @@ -714,7 +918,7 @@ _cairo_svg_path_close_path (void *closure) static void _cairo_svg_surface_emit_path (cairo_output_stream_t *output, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_matrix_t *ctm_inverse) { cairo_status_t status; @@ -725,7 +929,6 @@ _cairo_svg_surface_emit_path (cairo_output_stream_t *output, info.output = output; info.ctm_inverse = ctm_inverse; status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_svg_path_move_to, _cairo_svg_path_line_to, _cairo_svg_path_curve_to, @@ -793,7 +996,7 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, _cairo_output_stream_printf (document->xml_node_glyphs, "xml_node_glyphs, " transform", &image->base.device_transform_inverse, NULL); - _cairo_output_stream_printf (document->xml_node_glyphs, ">/n"); + _cairo_output_stream_printf (document->xml_node_glyphs, ">\n"); for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) { for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { @@ -814,14 +1017,14 @@ _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, cairo_scaled_font_t *scaled_font, unsigned long scaled_font_glyph_index, unsigned int font_id, unsigned int subset_glyph_index) { - cairo_status_t status; + cairo_int_status_t status; _cairo_output_stream_printf (document->xml_node_glyphs, "\n", @@ -840,16 +1043,16 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_SUCCESS; } -static cairo_status_t +static cairo_int_status_t _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset, void *closure) { cairo_svg_document_t *document = closure; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; unsigned int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; _cairo_scaled_font_freeze_cache (font_subset->scaled_font); for (i = 0; i < font_subset->num_glyphs; i++) { @@ -935,6 +1138,9 @@ _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } + if (pattern->type == CAIRO_PATTERN_TYPE_MESH) + return CAIRO_INT_STATUS_UNSUPPORTED; + /* SVG doesn't support extend reflect for image pattern */ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE && pattern->extend == CAIRO_EXTEND_REFLECT) @@ -992,6 +1198,11 @@ _cairo_svg_surface_finish (void *abstract_surface) _cairo_surface_clipper_reset (&surface->clipper); + _cairo_hash_table_foreach (surface->source_surfaces, + _cairo_svg_source_surface_pluck, + surface->source_surfaces); + _cairo_hash_table_destroy (surface->source_surfaces); + status2 = _cairo_svg_document_destroy (document); if (status == CAIRO_STATUS_SUCCESS) status = status2; @@ -1065,6 +1276,7 @@ base64_write_func (void *closure, switch (info->trailing) { case 2: dst[2] = '='; + /* fall through */ case 1: dst[3] = '='; default: @@ -1100,6 +1312,9 @@ _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, if (unlikely (status)) return status; + if (image_info.num_components == 4) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); info.output = output; @@ -1160,7 +1375,7 @@ static cairo_int_status_t _cairo_surface_base64_encode (cairo_surface_t *surface, cairo_output_stream_t *output) { - cairo_status_t status; + cairo_int_status_t status; base64_write_closure_t info; status = _cairo_surface_base64_encode_jpeg (surface, output); @@ -1260,7 +1475,8 @@ _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream, static cairo_status_t _cairo_svg_surface_emit_surface (cairo_svg_document_t *document, - cairo_surface_t *surface) + cairo_surface_t *surface, + int source_id) { cairo_rectangle_int_t extents; cairo_bool_t is_bounded; @@ -1268,18 +1484,12 @@ _cairo_svg_surface_emit_surface (cairo_svg_document_t *document, const unsigned char *uri; unsigned long uri_len; - if (_cairo_user_data_array_get_data (&surface->user_data, - (cairo_user_data_key_t *) document)) - { - return CAIRO_STATUS_SUCCESS; - } - is_bounded = _cairo_surface_get_extents (surface, &extents); assert (is_bounded); _cairo_output_stream_printf (document->xml_node_defs, "unique_id, + source_id, extents.width, extents.height); _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\""); @@ -1298,10 +1508,7 @@ _cairo_svg_surface_emit_surface (cairo_svg_document_t *document, _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n"); - /* and tag it */ - return _cairo_user_data_array_set_data (&surface->user_data, - (cairo_user_data_key_t *) document, - document, NULL); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -1315,17 +1522,29 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *outp { cairo_status_t status; cairo_matrix_t p2u; + int source_id; + cairo_bool_t is_new; p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - status = _cairo_svg_surface_emit_surface (svg_surface->document, - pattern->surface); + status = _cairo_svg_surface_add_source_surface (svg_surface, + pattern->surface, + &source_id, + &is_new); if (unlikely (status)) return status; + if (is_new) { + status = _cairo_svg_surface_emit_surface (svg_surface->document, + pattern->surface, + source_id); + if (unlikely (status)) + return status; + } + if (pattern_id != invalid_pattern_id) { cairo_rectangle_int_t extents; cairo_bool_t is_bounded; @@ -1347,7 +1566,7 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *outp _cairo_output_stream_printf (output, "surface->unique_id); + source_id); if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); @@ -1368,25 +1587,23 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *outp static cairo_status_t _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, - cairo_recording_surface_t *source) + cairo_recording_surface_t *source, + int source_id) { cairo_status_t status; cairo_surface_t *paginated_surface; cairo_svg_surface_t *svg_surface; cairo_array_t *page_set; - + cairo_rectangle_int_t extents; + cairo_bool_t bounded; cairo_output_stream_t *contents; - if (_cairo_user_data_array_get_data (&source->base.user_data, - (cairo_user_data_key_t *) document)) - { - return CAIRO_STATUS_SUCCESS; - } - + bounded = _cairo_surface_get_extents (&source->base, &extents); paginated_surface = _cairo_svg_surface_create_for_document (document, - source->content, - source->extents_pixels.width, - source->extents_pixels.height); + source->base.content, + extents.width, + extents.height, + bounded); if (unlikely (paginated_surface->status)) return paginated_surface->status; @@ -1414,28 +1631,32 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, if (! svg_surface->is_base_clip_emitted) { svg_surface->is_base_clip_emitted = TRUE; - _cairo_output_stream_printf (document->xml_node_defs, - "\n" - " \n" - "\n", - svg_surface->base_clip, - svg_surface->width, - svg_surface->height); + if (_cairo_surface_get_extents (&svg_surface->base, &extents)) { + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " \n" + "\n", + svg_surface->base_clip, + extents.x, + extents.y, + extents.width, + extents.height); + } } - if (source->content == CAIRO_CONTENT_ALPHA) { + if (source->base.content == CAIRO_CONTENT_ALPHA) { _cairo_svg_surface_emit_alpha_filter (document); _cairo_output_stream_printf (document->xml_node_defs, "\n", - source->base.unique_id, + source_id, svg_surface->base_clip); } else { _cairo_output_stream_printf (document->xml_node_defs, "\n", - source->base.unique_id, + source_id, svg_surface->base_clip); } @@ -1461,13 +1682,18 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, status = cairo_surface_status (paginated_surface); cairo_surface_destroy (paginated_surface); - if (unlikely (status)) - return status; + return status; +} - /* and tag it */ - return _cairo_user_data_array_set_data (&source->base.user_data, - (cairo_user_data_key_t *) document, - document, NULL); +static cairo_recording_surface_t * +to_recording_surface (const cairo_surface_pattern_t *pattern) +{ + cairo_surface_t *surface = pattern->surface; + if (_cairo_surface_is_paginated (surface)) + surface = _cairo_paginated_surface_get_recording (surface); + if (_cairo_surface_is_snapshot (surface)) + surface = _cairo_surface_snapshot_get_target (surface); + return (cairo_recording_surface_t *) surface; } static cairo_status_t @@ -1483,17 +1709,28 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *outp cairo_recording_surface_t *recording_surface; cairo_matrix_t p2u; cairo_status_t status; + int source_id; + cairo_bool_t is_new; p2u = pattern->base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - recording_surface = (cairo_recording_surface_t *) pattern->surface; - status = _cairo_svg_surface_emit_recording_surface (document, recording_surface); + status = _cairo_svg_surface_add_source_surface (surface, + pattern->surface, + &source_id, + &is_new); if (unlikely (status)) return status; + recording_surface = to_recording_surface (pattern); + if (is_new) { + status = _cairo_svg_surface_emit_recording_surface (document, recording_surface, source_id); + if (unlikely (status)) + return status; + } + if (pattern_id != invalid_pattern_id) { _cairo_output_stream_printf (output, "base.unique_id); + source_id); if (pattern_id == invalid_pattern_id) { _cairo_svg_surface_emit_operator (output, surface, op); @@ -1536,7 +1773,7 @@ _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output, const char *extra_attributes) { - if (_cairo_surface_is_recording (pattern->surface)) { + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { return _cairo_svg_surface_emit_composite_recording_pattern (output, surface, op, pattern, pattern_id, @@ -1782,7 +2019,6 @@ _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, const cairo_matrix_t *parent_matrix) { cairo_svg_document_t *document = surface->document; - double x0, y0, x1, y1; cairo_matrix_t p2u; cairo_status_t status; @@ -1791,17 +2027,13 @@ _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - x0 = _cairo_fixed_to_double (pattern->p1.x); - y0 = _cairo_fixed_to_double (pattern->p1.y); - x1 = _cairo_fixed_to_double (pattern->p2.x); - y1 = _cairo_fixed_to_double (pattern->p2.y); - _cairo_output_stream_printf (document->xml_node_defs, "linear_pattern_id, - x0, y0, x1, y1); + pattern->pd1.x, pattern->pd1.y, + pattern->pd2.x, pattern->pd2.y); _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base), _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix); @@ -1840,38 +2072,33 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, double fx, fy; cairo_bool_t reverse_stops; cairo_status_t status; - cairo_point_t *c0, *c1; - cairo_fixed_t radius0, radius1; + cairo_circle_double_t *c0, *c1; extend = pattern->base.base.extend; - if (pattern->r1 < pattern->r2) { - c0 = &pattern->c1; - c1 = &pattern->c2; - radius0 = pattern->r1; - radius1 = pattern->r2; + if (pattern->cd1.radius < pattern->cd2.radius) { + c0 = &pattern->cd1; + c1 = &pattern->cd2; reverse_stops = FALSE; } else { - c0 = &pattern->c2; - c1 = &pattern->c1; - radius0 = pattern->r2; - radius1 = pattern->r1; + c0 = &pattern->cd2; + c1 = &pattern->cd1; reverse_stops = TRUE; } - x0 = _cairo_fixed_to_double (c0->x); - y0 = _cairo_fixed_to_double (c0->y); - r0 = _cairo_fixed_to_double (radius0); - x1 = _cairo_fixed_to_double (c1->x); - y1 = _cairo_fixed_to_double (c1->y); - r1 = _cairo_fixed_to_double (radius1); + x0 = c0->center.x; + y0 = c0->center.y; + r0 = c0->radius; + x1 = c1->center.x; + y1 = c1->center.y; + r1 = c1->radius; p2u = pattern->base.base.matrix; status = cairo_matrix_invert (&p2u); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - if (pattern->r1 == pattern->r2) { + if (r0 == r1) { unsigned int n_stops = pattern->base.n_stops; _cairo_output_stream_printf (document->xml_node_defs, @@ -2036,6 +2263,10 @@ _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern, output, is_stroke, parent_matrix); + + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + ASSERT_NOT_REACHED; } return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); } @@ -2141,7 +2372,7 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, cairo_fill_rule_t fill_rule, double fill_tolerance, cairo_antialias_t fill_antialias, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_operator_t stroke_op, const cairo_pattern_t *stroke_source, const cairo_stroke_style_t *stroke_style, @@ -2149,7 +2380,7 @@ _cairo_svg_surface_fill_stroke (void *abstract_surface, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; @@ -2183,11 +2414,11 @@ static cairo_int_status_t _cairo_svg_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_status_t status; @@ -2231,7 +2462,7 @@ _cairo_svg_surface_get_extents (void *abstract_surface, rectangle->width = ceil (surface->width); rectangle->height = ceil (surface->height); - return TRUE; + return surface->surface_bounded; } static cairo_status_t @@ -2278,7 +2509,7 @@ static cairo_int_status_t _cairo_svg_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; @@ -2344,7 +2575,7 @@ _cairo_svg_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_status_t status; cairo_svg_surface_t *surface = abstract_surface; @@ -2382,7 +2613,7 @@ _cairo_svg_surface_mask (void *abstract_surface, if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; - cairo_content_t content = cairo_surface_get_content (surface_pattern->surface); + cairo_content_t content = surface_pattern->surface->content; if (content == CAIRO_CONTENT_ALPHA) discard_filter = TRUE; } @@ -2434,13 +2665,13 @@ static cairo_int_status_t _cairo_svg_surface_stroke (void *abstract_dst, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_dst; cairo_status_t status; @@ -2477,13 +2708,12 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { cairo_svg_surface_t *surface = abstract_surface; cairo_svg_document_t *document = surface->document; cairo_path_fixed_t path; - cairo_status_t status; + cairo_int_status_t status; cairo_scaled_font_subsets_glyph_t subset_glyph; int i; @@ -2574,39 +2804,50 @@ _cairo_svg_surface_get_font_options (void *abstract_surface, cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); +} + + +static const char ** +_cairo_svg_surface_get_supported_mime_types (void *abstract_surface) +{ + return _cairo_svg_supported_mime_types; } static const cairo_surface_backend_t cairo_svg_surface_backend = { CAIRO_SURFACE_TYPE_SVG, - NULL, /* create_similar: handled by wrapper */ _cairo_svg_surface_finish, + + _cairo_default_context_create, + + NULL, /* create_similar: handled by wrapper */ + NULL, /* create_similar_image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* _cairo_svg_surface_composite, */ - NULL, /* _cairo_svg_surface_fill_rectangles, */ - NULL, /* _cairo_svg_surface_composite_trapezoids,*/ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + _cairo_svg_surface_copy_page, _cairo_svg_surface_show_page, + _cairo_svg_surface_get_extents, - NULL, /* _cairo_svg_surface_old_show_glyphs, */ _cairo_svg_surface_get_font_options, + NULL, /* flush */ NULL, /* mark dirty rectangle */ - NULL, /* scaled font fini */ - NULL, /* scaled glyph fini */ + _cairo_svg_surface_paint, _cairo_svg_surface_mask, _cairo_svg_surface_stroke, _cairo_svg_surface_fill, + _cairo_svg_surface_fill_stroke, _cairo_svg_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ - _cairo_svg_surface_fill_stroke + NULL, /* has_show_text_glyphs */ + NULL, /* show_text_glyphs */ + _cairo_svg_surface_get_supported_mime_types, }; static cairo_status_t @@ -2622,7 +2863,7 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, if (output_stream->status) return output_stream->status; - document = malloc (sizeof (cairo_svg_document_t)); + document = _cairo_malloc (sizeof (cairo_svg_document_t)); if (unlikely (document == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2639,6 +2880,7 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, document->finished = FALSE; document->width = width; document->height = height; + document->unit = CAIRO_SVG_UNIT_PT; document->linear_pattern_id = 0; document->radial_pattern_id = 0; @@ -2741,9 +2983,10 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) "\n" "\n", - document->width, document->height, + document->width, _cairo_svg_unit_strings [document->unit], + document->height, _cairo_svg_unit_strings [document->unit], document->width, document->height, _cairo_svg_internal_version_strings [document->svg_version]); @@ -2815,13 +3058,15 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) return status; } -static void +static cairo_int_status_t _cairo_svg_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { cairo_svg_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; + + return CAIRO_STATUS_SUCCESS; } static cairo_bool_t @@ -2835,7 +3080,7 @@ _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface) CAIRO_OPERATOR_SOURCE); } - return status == CAIRO_STATUS_SUCCESS; + return status == CAIRO_INT_STATUS_SUCCESS; } static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = { diff --git a/gfx/cairo/cairo/src/cairo-svg.h b/gfx/cairo/cairo/src/cairo-svg.h index 0f739fc04cae..4d24857bcaaa 100644 --- a/gfx/cairo/cairo/src/cairo-svg.h +++ b/gfx/cairo/cairo/src/cairo-svg.h @@ -40,17 +40,59 @@ CAIRO_BEGIN_DECLS /** * cairo_svg_version_t: - * @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification. - * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. + * @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification. (Since 1.2) + * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. (Since 1.2) * * #cairo_svg_version_t is used to describe the version number of the SVG * specification that a generated SVG file will conform to. - */ + * + * Since: 1.2 + **/ typedef enum _cairo_svg_version { CAIRO_SVG_VERSION_1_1, CAIRO_SVG_VERSION_1_2 } cairo_svg_version_t; +/** + * cairo_svg_unit_t: + * + * @CAIRO_SVG_UNIT_USER: User unit, a value in the current coordinate system. + * If used in the root element for the initial coordinate systems it + * corresponds to pixels. (Since 1.16) + * @CAIRO_SVG_UNIT_EM: The size of the element's font. (Since 1.16) + * @CAIRO_SVG_UNIT_EX: The x-height of the element’s font. (Since 1.16) + * @CAIRO_SVG_UNIT_PX: Pixels (1px = 1/96th of 1in). (Since 1.16) + * @CAIRO_SVG_UNIT_IN: Inches (1in = 2.54cm = 96px). (Since 1.16) + * @CAIRO_SVG_UNIT_CM: Centimeters (1cm = 96px/2.54). (Since 1.16) + * @CAIRO_SVG_UNIT_MM: Millimeters (1mm = 1/10th of 1cm). (Since 1.16) + * @CAIRO_SVG_UNIT_PT: Points (1pt = 1/72th of 1in). (Since 1.16) + * @CAIRO_SVG_UNIT_PC: Picas (1pc = 1/6th of 1in). (Since 1.16) + * @CAIRO_SVG_UNIT_PERCENT: Percent, a value that is some fraction of another + * reference value. (Since 1.16) + * + * #cairo_svg_unit_t is used to describe the units valid for coordinates and + * 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 + * + * Since: 1.16 + **/ +typedef enum _cairo_svg_unit { + CAIRO_SVG_UNIT_USER = 0, + CAIRO_SVG_UNIT_EM, + CAIRO_SVG_UNIT_EX, + CAIRO_SVG_UNIT_PX, + CAIRO_SVG_UNIT_IN, + CAIRO_SVG_UNIT_CM, + CAIRO_SVG_UNIT_MM, + CAIRO_SVG_UNIT_PT, + CAIRO_SVG_UNIT_PC, + CAIRO_SVG_UNIT_PERCENT +} cairo_svg_unit_t; + cairo_public cairo_surface_t * cairo_svg_surface_create (const char *filename, double width_in_points, @@ -73,6 +115,13 @@ cairo_svg_get_versions (cairo_svg_version_t const **versions, cairo_public const char * cairo_svg_version_to_string (cairo_svg_version_t version); +cairo_public void +cairo_svg_surface_set_document_unit (cairo_surface_t *surface, + cairo_svg_unit_t unit); + +cairo_public cairo_svg_unit_t +cairo_svg_surface_get_document_unit (cairo_surface_t *surface); + CAIRO_END_DECLS #else /* CAIRO_HAS_SVG_SURFACE */ diff --git a/gfx/cairo/cairo/src/cairo-tag-attributes-private.h b/gfx/cairo/cairo/src/cairo-tag-attributes-private.h new file mode 100644 index 000000000000..3f5fa5b64a25 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-attributes-private.h @@ -0,0 +1,99 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + */ + +#ifndef CAIRO_TAG_ATTRIBUTES_PRIVATE_H +#define CAIRO_TAG_ATTRIBUTES_PRIVATE_H + +#include "cairo-array-private.h" +#include "cairo-error-private.h" +#include "cairo-types-private.h" + +typedef enum { + TAG_LINK_INVALID = 0, + TAG_LINK_EMPTY, + TAG_LINK_DEST, + TAG_LINK_URI, + TAG_LINK_FILE, +} cairo_tag_link_type_t; + +typedef struct _cairo_link_attrs { + cairo_tag_link_type_t link_type; + cairo_array_t rects; + char *dest; + char *uri; + char *file; + int page; + cairo_bool_t has_pos; + cairo_point_double_t pos; +} cairo_link_attrs_t; + +typedef struct _cairo_dest_attrs { + char *name; + double x; + double y; + cairo_bool_t x_valid; + cairo_bool_t y_valid; + cairo_bool_t internal; +} cairo_dest_attrs_t; + +typedef struct _cairo_ccitt_params { + int columns; + int rows; + int k; + cairo_bool_t end_of_line; + cairo_bool_t encoded_byte_align; + cairo_bool_t end_of_block; + cairo_bool_t black_is_1; + int damaged_rows_before_error; +} cairo_ccitt_params_t; + +typedef struct _cairo_eps_params { + cairo_box_double_t bbox; +} cairo_eps_params_t; + +cairo_private cairo_int_status_t +_cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs); + +cairo_private cairo_int_status_t +_cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs); + +cairo_private cairo_int_status_t +_cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *dest_attrs); + +cairo_private cairo_int_status_t +_cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *dest_attrs); + +#endif /* CAIRO_TAG_ATTRIBUTES_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-tag-attributes.c b/gfx/cairo/cairo/src/cairo-tag-attributes.c new file mode 100644 index 000000000000..7977d87f4c47 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-attributes.c @@ -0,0 +1,721 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + */ + +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-list-inline.h" +#include "cairo-tag-attributes-private.h" + +#include + +typedef enum { + ATTRIBUTE_BOOL, /* Either true/false or 1/0 may be used. */ + ATTRIBUTE_INT, + ATTRIBUTE_FLOAT, /* Decimal separator is in current locale. */ + ATTRIBUTE_STRING, /* Enclose in single quotes. String escapes: + * \' - single quote + * \\ - backslash + */ +} attribute_type_t; + +typedef struct _attribute_spec { + const char *name; + attribute_type_t type; + int array_size; /* 0 = scalar, -1 = variable size array */ +} attribute_spec_t; + +/* + * name [required] Unique name of this destination (UTF-8) + * x [optional] x coordinate of destination on page. Default is x coord of + * extents of operations enclosed by the dest begin/end tags. + * y [optional] y coordinate of destination on page. Default is y coord of + * extents of operations enclosed by the dest begin/end tags. + * internal [optional] If true, the name may be optimized out of the PDF where + * possible. Default false. + */ +static attribute_spec_t _dest_attrib_spec[] = { + { "name", ATTRIBUTE_STRING }, + { "x", ATTRIBUTE_FLOAT }, + { "y", ATTRIBUTE_FLOAT }, + { "internal", ATTRIBUTE_BOOL }, + { NULL } +}; + +/* + * rect [optional] One or more rectangles to define link region. Default + * is the extents of the operations enclosed by the link begin/end tags. + * Each rectangle is specified by four array elements: x, y, width, height. + * ie the array size must be a multiple of four. + * + * Internal Links + * -------------- + * either: + * dest - name of dest tag in the PDF file to link to (UTF8) + * or + * page - Page number in the PDF file to link to + * pos - [optional] Position of destination on page. Default is 0,0. + * + * URI Links + * --------- + * uri [required] Uniform resource identifier (ASCII). + + * File Links + * ---------- + * file - [required] File name of PDF file to link to. + * either: + * dest - name of dest tag in the PDF file to link to (UTF8) + * or + * page - Page number in the PDF file to link to + * pos - [optional] Position of destination on page. Default is 0,0. + */ +static attribute_spec_t _link_attrib_spec[] = +{ + { "rect", ATTRIBUTE_FLOAT, -1 }, + { "dest", ATTRIBUTE_STRING }, + { "uri", ATTRIBUTE_STRING }, + { "file", ATTRIBUTE_STRING }, + { "page", ATTRIBUTE_INT }, + { "pos", ATTRIBUTE_FLOAT, 2 }, + { NULL } +}; + +/* + * Required: + * Columns - width of the image in pixels. + * Rows - height of the image in scan lines. + * + * Optional: + * K - An integer identifying the encoding scheme used. < 0 is 2 dimensional + * Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 and 2 dimensional + * encoding. Default: 0. + * EndOfLine - If true end-of-line bit patterns are present. Default: false. + * EncodedByteAlign - If true the end of line is padded with 0 bits so the next + * line begins on a byte boundary. Default: false. + * EndOfBlock - If true the data contains an end-of-block pattern. Default: true. + * BlackIs1 - If true 1 bits are black pixels. Default: false. + * DamagedRowsBeforeError - Number of damages rows tolerated before an error + * occurs. Default: 0. + */ +static attribute_spec_t _ccitt_params_spec[] = +{ + { "Columns", ATTRIBUTE_INT }, + { "Rows", ATTRIBUTE_INT }, + { "K", ATTRIBUTE_INT }, + { "EndOfLine", ATTRIBUTE_BOOL }, + { "EncodedByteAlign", ATTRIBUTE_BOOL }, + { "EndOfBlock", ATTRIBUTE_BOOL }, + { "BlackIs1", ATTRIBUTE_BOOL }, + { "DamagedRowsBeforeError", ATTRIBUTE_INT }, + { NULL } +}; + +/* + * bbox - Bounding box of EPS file. The format is [ llx lly urx ury ] + * llx - lower left x xoordinate + * lly - lower left y xoordinate + * urx - upper right x xoordinate + * ury - upper right y xoordinate + * all coordinates are in PostScript coordinates. + */ +static attribute_spec_t _eps_params_spec[] = +{ + { "bbox", ATTRIBUTE_FLOAT, 4 }, + { NULL } +}; + +typedef union { + cairo_bool_t b; + int i; + double f; + char *s; +} attrib_val_t; + +typedef struct _attribute { + char *name; + attribute_type_t type; + int array_len; /* 0 = scalar */ + attrib_val_t scalar; + cairo_array_t array; /* array of attrib_val_t */ + cairo_list_t link; +} attribute_t; + +static const char * +skip_space (const char *p) +{ + while (_cairo_isspace (*p)) + p++; + + return p; +} + +static const char * +parse_bool (const char *p, cairo_bool_t *b) +{ + if (*p == '1') { + *b = TRUE; + return p + 1; + } else if (*p == '0') { + *b = FALSE; + return p + 1; + } else if (strcmp (p, "true") == 0) { + *b = TRUE; + return p + 4; + } else if (strcmp (p, "false") == 0) { + *b = FALSE; + return p + 5; + } + + return NULL; +} + +static const char * +parse_int (const char *p, int *i) +{ + int n; + + if (sscanf(p, "%d%n", i, &n) > 0) + return p + n; + + return NULL; +} + +static const char * +parse_float (const char *p, double *d) +{ + int n; + const char *start = p; + cairo_bool_t has_decimal_point = FALSE; + + while (*p) { + if (*p == '.' || *p == ']' || _cairo_isspace (*p)) + break; + p++; + } + + if (*p == '.') + has_decimal_point = TRUE; + + if (has_decimal_point) { + char *end; + *d = _cairo_strtod (start, &end); + if (end) + return end; + + } else { + if (sscanf(start, "%lf%n", d, &n) > 0) + return start + n; + } + + return NULL; +} + +static const char * +decode_string (const char *p, int *len, char *s) +{ + if (*p != '\'') + return NULL; + + p++; + if (! *p) + return NULL; + + *len = 0; + while (*p) { + if (*p == '\\') { + p++; + if (*p) { + if (s) + *s++ = *p; + p++; + (*len)++; + } + } else if (*p == '\'') { + return p + 1; + } else { + if (s) + *s++ = *p; + p++; + (*len)++; + } + } + + return NULL; +} + +static const char * +parse_string (const char *p, char **s) +{ + const char *end; + int len; + + end = decode_string (p, &len, NULL); + if (!end) + return NULL; + + *s = _cairo_malloc (len + 1); + decode_string (p, &len, *s); + (*s)[len] = 0; + + return end; +} + +static const char * +parse_scalar (const char *p, attribute_type_t type, attrib_val_t *scalar) +{ + switch (type) { + case ATTRIBUTE_BOOL: + return parse_bool (p, &scalar->b); + case ATTRIBUTE_INT: + return parse_int (p, &scalar->i); + case ATTRIBUTE_FLOAT: + return parse_float (p, &scalar->f); + case ATTRIBUTE_STRING: + return parse_string (p, &scalar->s); + } + + return NULL; +} + +static cairo_int_status_t +parse_array (const char *p, attribute_type_t type, cairo_array_t *array, const char **end) +{ + attrib_val_t val; + cairo_int_status_t status; + + p = skip_space (p); + if (! *p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + if (*p++ != '[') + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + while (TRUE) { + p = skip_space (p); + if (! *p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + if (*p == ']') { + *end = p + 1; + return CAIRO_INT_STATUS_SUCCESS; + } + + p = parse_scalar (p, type, &val); + if (!p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + status = _cairo_array_append (array, &val); + if (unlikely (status)) + return status; + } + + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); +} + +static cairo_int_status_t +parse_name (const char *p, const char **end, char **s) +{ + const char *p2; + char *name; + int len; + + if (! _cairo_isalpha (*p)) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + p2 = p; + while (_cairo_isalpha (*p2) || _cairo_isdigit (*p2)) + p2++; + + len = p2 - p; + name = _cairo_malloc (len + 1); + if (unlikely (name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (name, p, len); + name[len] = 0; + *s = name; + *end = p2; + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +parse_attributes (const char *attributes, attribute_spec_t *attrib_def, cairo_list_t *list) +{ + attribute_spec_t *def; + attribute_t *attrib; + char *name = NULL; + cairo_int_status_t status; + const char *p = attributes; + + if (! p) + return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + while (*p) { + p = skip_space (p); + if (! *p) + break; + + status = parse_name (p, &p, &name); + if (status) + return status; + + def = attrib_def; + while (def->name) { + if (strcmp (name, def->name) == 0) + break; + def++; + } + if (! def->name) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail1; + } + + attrib = calloc (1, sizeof (attribute_t)); + if (unlikely (attrib == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + attrib->name = name; + attrib->type = def->type; + _cairo_array_init (&attrib->array, sizeof(attrib_val_t)); + + p = skip_space (p); + if (def->type == ATTRIBUTE_BOOL && *p != '=') { + attrib->scalar.b = TRUE; + } else { + if (*p++ != '=') { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail2; + } + + if (def->array_size == 0) { + p = parse_scalar (p, def->type, &attrib->scalar); + if (!p) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail2; + } + + attrib->array_len = 0; + } else { + status = parse_array (p, def->type, &attrib->array, &p); + if (unlikely (status)) + goto fail2; + + attrib->array_len = _cairo_array_num_elements (&attrib->array); + if (def->array_size > 0 && attrib->array_len != def->array_size) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto fail2; + } + } + } + + cairo_list_add_tail (&attrib->link, list); + } + + return CAIRO_INT_STATUS_SUCCESS; + + fail2: + _cairo_array_fini (&attrib->array); + if (attrib->type == ATTRIBUTE_STRING) + free (attrib->scalar.s); + free (attrib); + fail1: + free (name); + + return status; +} + +static void +free_attributes_list (cairo_list_t *list) +{ + attribute_t *attr, *next; + + cairo_list_foreach_entry_safe (attr, next, attribute_t, list, link) + { + cairo_list_del (&attr->link); + free (attr->name); + _cairo_array_fini (&attr->array); + if (attr->type == ATTRIBUTE_STRING) + free (attr->scalar.s); + free (attr); + } +} + +static attribute_t * +find_attribute (cairo_list_t *list, const char *name) +{ + attribute_t *attr; + + cairo_list_foreach_entry (attr, attribute_t, list, link) + { + if (strcmp (attr->name, name) == 0) + return attr; + } + + return NULL; +} + +cairo_int_status_t +_cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + attrib_val_t val; + + cairo_list_init (&list); + status = parse_attributes (attributes, _link_attrib_spec, &list); + if (unlikely (status)) + return status; + + memset (link_attrs, 0, sizeof (cairo_link_attrs_t)); + _cairo_array_init (&link_attrs->rects, sizeof (cairo_rectangle_t)); + if (find_attribute (&list, "uri")) { + link_attrs->link_type = TAG_LINK_URI; + } else if (find_attribute (&list, "file")) { + link_attrs->link_type = TAG_LINK_FILE; + } else if (find_attribute (&list, "dest")) { + link_attrs->link_type = TAG_LINK_DEST; + } else if (find_attribute (&list, "page")) { + link_attrs->link_type = TAG_LINK_DEST; + } else { + link_attrs->link_type = TAG_LINK_EMPTY; + goto cleanup; + } + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "uri") == 0) { + if (link_attrs->link_type != TAG_LINK_URI) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->uri = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "file") == 0) { + if (link_attrs->link_type != TAG_LINK_FILE) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->file = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "dest") == 0) { + if (! (link_attrs->link_type == TAG_LINK_DEST || + link_attrs->link_type != TAG_LINK_FILE)) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->dest = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "page") == 0) { + if (! (link_attrs->link_type == TAG_LINK_DEST || + link_attrs->link_type != TAG_LINK_FILE)) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + link_attrs->page = attr->scalar.i; + + } else if (strcmp (attr->name, "pos") == 0) { + if (! (link_attrs->link_type == TAG_LINK_DEST || + link_attrs->link_type != TAG_LINK_FILE)) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + _cairo_array_copy_element (&attr->array, 0, &val); + link_attrs->pos.x = val.f; + _cairo_array_copy_element (&attr->array, 1, &val); + link_attrs->pos.y = val.f; + link_attrs->has_pos = TRUE; + } else if (strcmp (attr->name, "rect") == 0) { + cairo_rectangle_t rect; + int i; + int num_elem = _cairo_array_num_elements (&attr->array); + if (num_elem == 0 || num_elem % 4 != 0) { + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + goto cleanup; + } + + for (i = 0; i < num_elem; i += 4) { + _cairo_array_copy_element (&attr->array, i, &val); + rect.x = val.f; + _cairo_array_copy_element (&attr->array, i+1, &val); + rect.y = val.f; + _cairo_array_copy_element (&attr->array, i+2, &val); + rect.width = val.f; + _cairo_array_copy_element (&attr->array, i+3, &val); + rect.height = val.f; + status = _cairo_array_append (&link_attrs->rects, &rect); + if (unlikely (status)) + goto cleanup; + } + } + } + + cleanup: + free_attributes_list (&list); + if (unlikely (status)) { + free (link_attrs->dest); + free (link_attrs->uri); + free (link_attrs->file); + _cairo_array_fini (&link_attrs->rects); + } + + return status; +} + +cairo_int_status_t +_cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + + memset (dest_attrs, 0, sizeof (cairo_dest_attrs_t)); + cairo_list_init (&list); + status = parse_attributes (attributes, _dest_attrib_spec, &list); + if (unlikely (status)) + goto cleanup; + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "name") == 0) { + dest_attrs->name = strdup (attr->scalar.s); + } else if (strcmp (attr->name, "x") == 0) { + dest_attrs->x = attr->scalar.f; + dest_attrs->x_valid = TRUE; + } else if (strcmp (attr->name, "y") == 0) { + dest_attrs->y = attr->scalar.f; + dest_attrs->y_valid = TRUE; + } else if (strcmp (attr->name, "internal") == 0) { + dest_attrs->internal = attr->scalar.b; + } + } + + if (! dest_attrs->name) + status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR); + + cleanup: + free_attributes_list (&list); + + return status; +} + +cairo_int_status_t +_cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *ccitt_params) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + + ccitt_params->columns = -1; + ccitt_params->rows = -1; + + /* set defaults */ + ccitt_params->k = 0; + ccitt_params->end_of_line = FALSE; + ccitt_params->encoded_byte_align = FALSE; + ccitt_params->end_of_block = TRUE; + ccitt_params->black_is_1 = FALSE; + ccitt_params->damaged_rows_before_error = 0; + + cairo_list_init (&list); + status = parse_attributes (attributes, _ccitt_params_spec, &list); + if (unlikely (status)) + goto cleanup; + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "Columns") == 0) { + ccitt_params->columns = attr->scalar.i; + } else if (strcmp (attr->name, "Rows") == 0) { + ccitt_params->rows = attr->scalar.i; + } else if (strcmp (attr->name, "K") == 0) { + ccitt_params->k = attr->scalar.i; + } else if (strcmp (attr->name, "EndOfLine") == 0) { + ccitt_params->end_of_line = attr->scalar.b; + } else if (strcmp (attr->name, "EncodedByteAlign") == 0) { + ccitt_params->encoded_byte_align = attr->scalar.b; + } else if (strcmp (attr->name, "EndOfBlock") == 0) { + ccitt_params->end_of_block = attr->scalar.b; + } else if (strcmp (attr->name, "BlackIs1") == 0) { + ccitt_params->black_is_1 = attr->scalar.b; + } else if (strcmp (attr->name, "DamagedRowsBeforeError") == 0) { + ccitt_params->damaged_rows_before_error = attr->scalar.b; + } + } + + cleanup: + free_attributes_list (&list); + + return status; +} + +cairo_int_status_t +_cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *eps_params) +{ + cairo_list_t list; + cairo_int_status_t status; + attribute_t *attr; + attrib_val_t val; + + cairo_list_init (&list); + status = parse_attributes (attributes, _eps_params_spec, &list); + if (unlikely (status)) + goto cleanup; + + cairo_list_foreach_entry (attr, attribute_t, &list, link) + { + if (strcmp (attr->name, "bbox") == 0) { + _cairo_array_copy_element (&attr->array, 0, &val); + eps_params->bbox.p1.x = val.f; + _cairo_array_copy_element (&attr->array, 1, &val); + eps_params->bbox.p1.y = val.f; + _cairo_array_copy_element (&attr->array, 2, &val); + eps_params->bbox.p2.x = val.f; + _cairo_array_copy_element (&attr->array, 3, &val); + eps_params->bbox.p2.y = val.f; + } + } + + cleanup: + free_attributes_list (&list); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-tag-stack-private.h b/gfx/cairo/cairo/src/cairo-tag-stack-private.h new file mode 100644 index 000000000000..4af2a85dec55 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-stack-private.h @@ -0,0 +1,107 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + */ + +#ifndef CAIRO_TAG_STACK_PRIVATE_H +#define CAIRO_TAG_STACK_PRIVATE_H + +#include "cairo-error-private.h" +#include "cairo-list-inline.h" + +/* The type of a single tag */ +typedef enum { + TAG_TYPE_INVALID = 0, + TAG_TYPE_STRUCTURE = 1, + TAG_TYPE_LINK = 2, + TAG_TYPE_DEST = 4, +} cairo_tag_type_t; + +/* The type of the structure tree. */ +typedef enum _cairo_tag_stack_structure_type { + TAG_TREE_TYPE_TAGGED, /* compliant with Tagged PDF */ + TAG_TREE_TYPE_STRUCTURE, /* valid structure but not 'Tagged PDF' compliant */ + TAG_TREE_TYPE_LINK_ONLY, /* contains Link tags only */ + TAG_TREE_TYPE_NO_TAGS, /* no tags used */ + TAG_TREE_TYPE_INVALID, /* invalid tag structure */ +} cairo_tag_stack_structure_type_t; + +typedef struct _cairo_tag_stack_elem { + char *name; + char *attributes; + void *data; + cairo_list_t link; + +} cairo_tag_stack_elem_t; + +typedef struct _cairo_tag_stack { + cairo_list_t list; + cairo_tag_stack_structure_type_t type; + int size; + +} cairo_tag_stack_t; + +cairo_private void +_cairo_tag_stack_init (cairo_tag_stack_t *stack); + +cairo_private void +_cairo_tag_stack_fini (cairo_tag_stack_t *stack); + +cairo_private cairo_tag_stack_structure_type_t +_cairo_tag_stack_get_structure_type (cairo_tag_stack_t *stack); + +cairo_private cairo_int_status_t +_cairo_tag_stack_push (cairo_tag_stack_t *stack, + const char *name, + const char *attributes); + +cairo_private void +_cairo_tag_stack_set_top_data (cairo_tag_stack_t *stack, + void *data); + +cairo_private cairo_int_status_t +_cairo_tag_stack_pop (cairo_tag_stack_t *stack, + const char *name, + cairo_tag_stack_elem_t **elem); + +cairo_private cairo_tag_stack_elem_t * +_cairo_tag_stack_top_elem (cairo_tag_stack_t *stack); + +cairo_private void +_cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem); + +cairo_private cairo_tag_type_t +_cairo_tag_get_type (const char *name); + +#endif /* CAIRO_TAG_STACK_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-tag-stack.c b/gfx/cairo/cairo/src/cairo-tag-stack.c new file mode 100644 index 000000000000..7c2d5245e0a8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-stack.c @@ -0,0 +1,280 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2016 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 + */ + +#include "cairoint.h" + +#include "cairo-tag-stack-private.h" + +/* Tagged PDF must have one of these tags at the top level */ +static const char * _cairo_tag_stack_tagged_pdf_top_level_element_list[] = +{ + "Document", + "Part", + "Art", + "Sect", + "Div", + NULL +}; + +/* List of valid tag names. Table numbers reference PDF 32000 */ +static const char * _cairo_tag_stack_struct_pdf_list[] = +{ + /* Table 333 - Grouping Elements */ + "Document", + "Part", + "Art", + "Sect", + "Div", + "BlockQuote", + "Caption", + "TOC", + "TOCI", + "Index", + "NonStruct", + "Private", + + /* Table 335 - Standard structure types for paragraphlike elements */ + "P", "H", + "H1", "H2", "H3", "H4", "H5", "H6", + + /* Table 336 - Standard structure types for list elements */ + "L", "LI", "Lbl", "LBody", + + /* Table 337 - Standard structure types for table elements */ + "Table", + "TR", "TH", "TD", + "THead", "TBody", "TFoot", + + /* Table 338 - Standard structure types for inline-level structure elements */ + "Span", + "Quote", + "Note", + "Reference", + "BibEntry", + "Code", + "Link", /* CAIRO_TAG_LINK */ + "Annot", + "Ruby", + "Warichu", + + /* Table 339 - Standard structure types for Ruby and Warichu elements */ + "RB", "RT", "RP", + "WT", "WP", + + /* Table 340 - Standard structure types for illustration elements */ + "Figure", + "Formula", + "Form", + + NULL +}; + +/* List of cairo specific tag names */ +static const char * _cairo_tag_stack_cairo_tag_list[] = +{ + CAIRO_TAG_DEST, + NULL +}; + +void +_cairo_tag_stack_init (cairo_tag_stack_t *stack) +{ + cairo_list_init (&stack->list); + stack->type = TAG_TREE_TYPE_NO_TAGS; + stack->size = 0; +} + +void +_cairo_tag_stack_fini (cairo_tag_stack_t *stack) +{ + while (! cairo_list_is_empty (&stack->list)) { + cairo_tag_stack_elem_t *elem; + + elem = cairo_list_first_entry (&stack->list, cairo_tag_stack_elem_t, link); + cairo_list_del (&elem->link); + free (elem->name); + free (elem->attributes); + free (elem); + } +} + +cairo_tag_stack_structure_type_t +_cairo_tag_stack_get_structure_type (cairo_tag_stack_t *stack) +{ + return stack->type; +} + +static cairo_bool_t +name_in_list (const char *name, const char **list) +{ + if (! name) + return FALSE; + + while (*list) { + if (strcmp (name, *list) == 0) + return TRUE; + list++; + } + + return FALSE; +} + +cairo_int_status_t +_cairo_tag_stack_push (cairo_tag_stack_t *stack, + const char *name, + const char *attributes) +{ + cairo_tag_stack_elem_t *elem; + + if (! name_in_list (name, _cairo_tag_stack_struct_pdf_list) && + ! name_in_list (name, _cairo_tag_stack_cairo_tag_list)) + { + stack->type = TAG_TYPE_INVALID; + return _cairo_error (CAIRO_STATUS_TAG_ERROR); + } + + if (stack->type == TAG_TREE_TYPE_NO_TAGS) { + if (name_in_list (name, _cairo_tag_stack_tagged_pdf_top_level_element_list)) + stack->type = TAG_TREE_TYPE_TAGGED; + else if (strcmp (name, "Link") == 0) + stack->type = TAG_TREE_TYPE_LINK_ONLY; + else if (name_in_list (name, _cairo_tag_stack_struct_pdf_list)) + stack->type = TAG_TREE_TYPE_STRUCTURE; + } else { + if (stack->type == TAG_TREE_TYPE_LINK_ONLY && + (strcmp (name, "Link") != 0) && + name_in_list (name, _cairo_tag_stack_struct_pdf_list)) + { + stack->type = TAG_TREE_TYPE_STRUCTURE; + } + } + + elem = _cairo_malloc (sizeof(cairo_tag_stack_elem_t)); + if (unlikely (elem == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + elem->name = strdup (name); + if (unlikely (elem->name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (attributes) { + elem->attributes = strdup (attributes); + if (unlikely (elem->attributes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + elem->attributes = NULL; + } + + elem->data = NULL; + + cairo_list_add_tail (&elem->link, &stack->list); + stack->size++; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_private void +_cairo_tag_stack_set_top_data (cairo_tag_stack_t *stack, + void *data) +{ + cairo_tag_stack_elem_t *top; + + top = _cairo_tag_stack_top_elem (stack); + if (top) + top->data = data; +} + +cairo_int_status_t +_cairo_tag_stack_pop (cairo_tag_stack_t *stack, + const char *name, + cairo_tag_stack_elem_t **elem) +{ + cairo_tag_stack_elem_t *top; + + top = _cairo_tag_stack_top_elem (stack); + if (!top) { + stack->type = TAG_TYPE_INVALID; + return _cairo_error (CAIRO_STATUS_TAG_ERROR); + } + + cairo_list_del (&top->link); + stack->size--; + if (strcmp (top->name, name) != 0) { + stack->type = TAG_TYPE_INVALID; + _cairo_tag_stack_free_elem (top); + return _cairo_error (CAIRO_STATUS_TAG_ERROR); + } + + if (elem) + *elem = top; + else + _cairo_tag_stack_free_elem (top); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_tag_stack_elem_t * +_cairo_tag_stack_top_elem (cairo_tag_stack_t *stack) +{ + if (cairo_list_is_empty (&stack->list)) + return NULL; + + return cairo_list_last_entry (&stack->list, cairo_tag_stack_elem_t, link); +} + +void +_cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem) +{ + free (elem->name); + free (elem->attributes); + free (elem); +} + +cairo_tag_type_t +_cairo_tag_get_type (const char *name) +{ + if (! name_in_list (name, _cairo_tag_stack_struct_pdf_list) && + ! name_in_list (name, _cairo_tag_stack_cairo_tag_list)) + return TAG_TYPE_INVALID; + + if (strcmp(name, "Link") == 0) + return (TAG_TYPE_LINK | TAG_TYPE_STRUCTURE); + + if (strcmp(name, "cairo.dest") == 0) + return TAG_TYPE_DEST; + + return TAG_TYPE_STRUCTURE; +} diff --git a/gfx/cairo/cairo/src/cairo-tee-surface.c b/gfx/cairo/cairo/src/cairo-tee-surface.c index bca07716f5b8..7a94c9bca4c0 100644 --- a/gfx/cairo/cairo/src/cairo-tee-surface.c +++ b/gfx/cairo/cairo/src/cairo-tee-surface.c @@ -42,9 +42,13 @@ #include "cairo-tee.h" +#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" +#include "cairo-image-surface-inline.h" typedef struct _cairo_tee_surface { cairo_surface_t base; @@ -115,6 +119,14 @@ _cairo_tee_surface_finish (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } +static cairo_surface_t * +_cairo_tee_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_tee_surface_t *surface = abstract_surface; + return _cairo_surface_get_source (surface->master.target, extents); +} + static cairo_status_t _cairo_tee_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, @@ -194,67 +206,26 @@ _cairo_tee_surface_get_font_options (void *abstract_surface, _cairo_surface_wrapper_get_font_options (&surface->master, options); } -static const cairo_pattern_t * -_cairo_tee_surface_match_source (cairo_tee_surface_t *surface, - const cairo_pattern_t *source, - int index, - cairo_surface_wrapper_t *dest, - cairo_surface_pattern_t *temp) -{ - cairo_surface_t *s; - cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s); - if (status == CAIRO_STATUS_SUCCESS && - cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) { - cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index); - if (tee_surf->status == CAIRO_STATUS_SUCCESS && - tee_surf->backend == dest->target->backend) { - status = _cairo_pattern_init_copy (&temp->base, source); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_destroy (temp->surface); - temp->surface = tee_surf; - cairo_surface_reference (temp->surface); - return &temp->base; - } - } - } - - return source; -} - static cairo_int_status_t _cairo_tee_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; + cairo_int_status_t status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); - status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } + status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip); if (unlikely (status)) return status; } - return CAIRO_STATUS_SUCCESS; + return _cairo_surface_wrapper_paint (&surface->master, op, source, clip); } static cairo_int_status_t @@ -262,138 +233,96 @@ _cairo_tee_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_mask (&surface->master, - op, matched_source, mask, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_mask (&slaves[n], - op, matched_source, mask, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } + op, source, mask, clip); if (unlikely (status)) return status; } - return CAIRO_STATUS_SUCCESS; + return _cairo_surface_wrapper_mask (&surface->master, + op, source, mask, clip); } static cairo_int_status_t _cairo_tee_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_stroke (&surface->master, - op, matched_source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_stroke (&slaves[n], - op, matched_source, + op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } if (unlikely (status)) return status; } - return CAIRO_STATUS_SUCCESS; + return _cairo_surface_wrapper_stroke (&surface->master, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); } static cairo_int_status_t _cairo_tee_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; int n, num_slaves; - cairo_status_t status; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; - - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_fill (&surface->master, - op, matched_source, - path, fill_rule, - tolerance, antialias, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - return status; num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_fill (&slaves[n], - op, matched_source, + op, source, path, fill_rule, tolerance, antialias, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } if (unlikely (status)) return status; } - return CAIRO_STATUS_SUCCESS; + return _cairo_surface_wrapper_fill (&surface->master, + op, source, + path, fill_rule, + tolerance, antialias, + clip); } static cairo_bool_t @@ -414,119 +343,78 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_tee_surface_t *surface = abstract_surface; cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; int n, num_slaves; - cairo_status_t status; cairo_glyph_t *glyphs_copy; - const cairo_pattern_t *matched_source; - cairo_surface_pattern_t temp; /* XXX: This copying is ugly. */ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); if (unlikely (glyphs_copy == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp); - status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, - matched_source, - utf8, utf8_len, - glyphs_copy, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } - if (unlikely (status)) - goto CLEANUP; - num_slaves = _cairo_array_num_elements (&surface->slaves); slaves = _cairo_array_index (&surface->slaves, 0); for (n = 0; n < num_slaves; n++) { memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp); status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, - matched_source, + source, utf8, utf8_len, glyphs_copy, num_glyphs, clusters, num_clusters, cluster_flags, scaled_font, clip); - if (matched_source == &temp.base) { - _cairo_pattern_fini (&temp.base); - } if (unlikely (status)) goto CLEANUP; } - CLEANUP: + memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op, + source, + utf8, utf8_len, + glyphs_copy, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); +CLEANUP: free (glyphs_copy); return status; } -static cairo_status_t -_cairo_tee_surface_flush (void *abstract_surface) -{ - cairo_tee_surface_t *surface = abstract_surface; - cairo_surface_wrapper_t *slaves; - int n, num_slaves; - cairo_status_t status; - - status = _cairo_surface_wrapper_flush(&surface->master); - if (unlikely (status)) - return status; - - 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_flush(&slaves[n]); - if (unlikely (status)) - return status; - } - - return CAIRO_STATUS_SUCCESS; -} - static const cairo_surface_backend_t cairo_tee_surface_backend = { CAIRO_SURFACE_TYPE_TEE, - _cairo_tee_surface_create_similar, _cairo_tee_surface_finish, + + _cairo_default_context_create, /* XXX */ + + _cairo_tee_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_tee_surface_source, _cairo_tee_surface_acquire_source_image, _cairo_tee_surface_release_source_image, - NULL, NULL, /* dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + _cairo_tee_surface_snapshot, NULL, /* copy_page */ NULL, /* show_page */ _cairo_tee_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_tee_surface_get_font_options, - _cairo_tee_surface_flush, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ _cairo_tee_surface_paint, _cairo_tee_surface_mask, _cairo_tee_surface_stroke, _cairo_tee_surface_fill, - NULL, /* replaced by show_text_glyphs */ - - _cairo_tee_surface_snapshot, - NULL, /* is_similar */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + + NULL, /* show_glyphs */ _cairo_tee_surface_has_show_text_glyphs, _cairo_tee_surface_show_text_glyphs @@ -540,19 +428,17 @@ cairo_tee_surface_create (cairo_surface_t *master) if (unlikely (master->status)) return _cairo_surface_create_in_error (master->status); - surface = malloc (sizeof (cairo_tee_surface_t)); + surface = _cairo_malloc (sizeof (cairo_tee_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_tee_surface_backend, master->device, - master->content); + master->content, + TRUE); /* is_vector */ _cairo_surface_wrapper_init (&surface->master, master); - /* we trust that these are already set and remain constant */ - surface->base.device_transform = master->device_transform; - surface->base.device_transform_inverse = master->device_transform_inverse; _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t)); @@ -605,26 +491,25 @@ cairo_tee_surface_remove (cairo_surface_t *abstract_surface, cairo_tee_surface_t *surface; cairo_surface_wrapper_t *slaves; int n, num_slaves; - cairo_status_t status; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (abstract_surface->backend != &cairo_tee_surface_backend) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } surface = (cairo_tee_surface_t *) abstract_surface; if (target == surface->master.target) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); return; } @@ -636,8 +521,8 @@ cairo_tee_surface_remove (cairo_surface_t *abstract_surface, } if (n == num_slaves) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); return; } @@ -649,7 +534,7 @@ cairo_tee_surface_remove (cairo_surface_t *abstract_surface, cairo_surface_t * cairo_tee_surface_index (cairo_surface_t *abstract_surface, - int index) + unsigned int index) { cairo_tee_surface_t *surface; diff --git a/gfx/cairo/cairo/src/cairo-tee.h b/gfx/cairo/cairo/src/cairo-tee.h index 9c048c6fe1dc..9125a3a4a2b0 100644 --- a/gfx/cairo/cairo/src/cairo-tee.h +++ b/gfx/cairo/cairo/src/cairo-tee.h @@ -55,7 +55,7 @@ cairo_tee_surface_remove (cairo_surface_t *surface, cairo_public cairo_surface_t * cairo_tee_surface_index (cairo_surface_t *surface, - int index); + unsigned int index); CAIRO_END_DECLS diff --git a/gfx/cairo/cairo/src/cairo-time-private.h b/gfx/cairo/cairo/src/cairo-time-private.h new file mode 100644 index 000000000000..06dc912b4314 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-time-private.h @@ -0,0 +1,94 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright (C) 2011 Andrea Canciani + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + * Authors: Andrea Canciani + * + */ + +#ifndef CAIRO_TIME_PRIVATE_H +#define CAIRO_TIME_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-wideint-private.h" + +/* Make the base type signed for easier arithmetic */ +typedef cairo_int64_t cairo_time_t; + +#define _cairo_time_add _cairo_int64_add +#define _cairo_time_sub _cairo_int64_sub +#define _cairo_time_gt _cairo_int64_gt +#define _cairo_time_lt _cairo_int64_lt + +#define _cairo_time_to_double _cairo_int64_to_double +#define _cairo_time_from_double _cairo_double_to_int64 + +cairo_private int +_cairo_time_cmp (const void *a, + const void *b); + +cairo_private double +_cairo_time_to_s (cairo_time_t t); + +cairo_private cairo_time_t +_cairo_time_from_s (double t); + +cairo_private cairo_time_t +_cairo_time_get (void); + +static cairo_always_inline cairo_time_t +_cairo_time_get_delta (cairo_time_t t) +{ + cairo_time_t now; + + now = _cairo_time_get (); + + return _cairo_time_sub (now, t); +} + +static cairo_always_inline double +_cairo_time_to_ns (cairo_time_t t) +{ + return 1.e9 * _cairo_time_to_s (t); +} + +static cairo_always_inline cairo_time_t +_cairo_time_max (cairo_time_t a, cairo_time_t b) +{ + if (_cairo_int64_gt (a, b)) + return a; + else + return b; +} + +static cairo_always_inline cairo_time_t +_cairo_time_min (cairo_time_t a, cairo_time_t b) +{ + if (_cairo_int64_lt (a, b)) + return a; + else + return b; +} + +#endif diff --git a/gfx/cairo/cairo/src/cairo-time.c b/gfx/cairo/cairo/src/cairo-time.c new file mode 100644 index 000000000000..a0003fbfc2c5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-time.c @@ -0,0 +1,225 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2007 Netlabs + * Copyright (c) 2006 Mozilla Corporation + * Copyright (c) 2006 Red Hat, Inc. + * Copyright (c) 2011 Andrea Canciani + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * the authors not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The authors make no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Authors: Peter Weilbacher + * Vladimir Vukicevic + * Carl Worth + * Andrea Canciani + */ + +#include "cairoint.h" + +#include "cairo-time-private.h" + +#if HAVE_CLOCK_GETTIME +#if defined(CLOCK_MONOTONIC_RAW) +#define CAIRO_CLOCK CLOCK_MONOTONIC_RAW +#elif defined(CLOCK_MONOTONIC) +#define CAIRO_CLOCK CLOCK_MONOTONIC +#endif +#endif + +#if defined(__APPLE__) +#include + +static cairo_always_inline double +_cairo_time_1s (void) +{ + mach_timebase_info_data_t freq; + + mach_timebase_info (&freq); + + return 1000000000. * freq.denom / freq.numer; +} + +cairo_time_t +_cairo_time_get (void) +{ + return mach_absolute_time (); +} + +#elif defined(__OS2__) +#define INCL_BASE +#include + +static cairo_always_inline double +_cairo_time_1s (void) +{ + ULONG freq; + + DosTmrQueryFreq (&freq); + + return freq; +} + +cairo_time_t +_cairo_time_get (void) +{ + QWORD t; + cairo_int64_t r; + + DosTmrQueryTime (&t); + + r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.ulHi), 32); + r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.ulLo)); + + return r; +} + +#elif _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +static cairo_always_inline double +_cairo_time_1s (void) +{ + LARGE_INTEGER freq; + + QueryPerformanceFrequency (&freq); + + return freq.QuadPart; +} + +#ifndef HAVE_UINT64_T +static cairo_always_inline cairo_time_t +_cairo_time_from_large_integer (LARGE_INTEGER t) +{ + cairo_int64_t r; + + r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.HighPart), 32); + r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.LowPart)); + + return r; +} +#else +static cairo_always_inline cairo_time_t +_cairo_time_from_large_integer (LARGE_INTEGER t) +{ + return t.QuadPart; +} +#endif + +cairo_time_t +_cairo_time_get (void) +{ + LARGE_INTEGER t; + + QueryPerformanceCounter (&t); + + return _cairo_time_from_large_integer(t); +} + +#elif defined(CAIRO_CLOCK) +#include + +static cairo_always_inline double +_cairo_time_1s (void) +{ + return 1000000000; +} + +cairo_time_t +_cairo_time_get (void) +{ + struct timespec t; + cairo_time_t r; + + clock_gettime (CAIRO_CLOCK, &t); + + r = _cairo_double_to_int64 (_cairo_time_1s ()); + r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec)); + r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_nsec)); + + return r; +} + +#else +#include + +static cairo_always_inline double +_cairo_time_1s (void) +{ + return 1000000; +} + +cairo_time_t +_cairo_time_get (void) +{ + struct timeval t; + cairo_time_t r; + + gettimeofday (&t, NULL); + + r = _cairo_double_to_int64 (_cairo_time_1s ()); + r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec)); + r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_usec)); + + return r; +} + +#endif + +int +_cairo_time_cmp (const void *a, + const void *b) +{ + const cairo_time_t *ta = a, *tb = b; + return _cairo_int64_cmp (*ta, *tb); +} + +static double +_cairo_time_ticks_per_sec (void) +{ + static double ticks = 0; + + if (unlikely (ticks == 0)) + ticks = _cairo_time_1s (); + + return ticks; +} + +static double +_cairo_time_s_per_tick (void) +{ + static double s = 0; + + if (unlikely (s == 0)) + s = 1. / _cairo_time_ticks_per_sec (); + + return s; +} + +double +_cairo_time_to_s (cairo_time_t t) +{ + return _cairo_int64_to_double (t) * _cairo_time_s_per_tick (); +} + +cairo_time_t +_cairo_time_from_s (double t) +{ + return _cairo_double_to_int64 (t * _cairo_time_ticks_per_sec ()); +} diff --git a/gfx/cairo/cairo/src/cairo-tor-scan-converter.c b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c index dc8dad1c5035..e8142d5bcf62 100644 --- a/gfx/cairo/cairo/src/cairo-tor-scan-converter.c +++ b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c @@ -29,7 +29,7 @@ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 * of * - * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths + * https://gitweb.freedesktop.org/?p=users/joonas/glitter-paths */ /* Glitter-paths is a stand alone polygon rasteriser derived from * David Turner's reimplementation of Tor Anderssons's 15x17 @@ -97,10 +97,10 @@ #include "cairo-spans-private.h" #include "cairo-error-private.h" -#include #include #include #include +#include /*------------------------------------------------------------------------- * cairo specific config @@ -123,39 +123,6 @@ typedef cairo_status_t glitter_status_t; struct pool; struct cell_list; -static glitter_status_t -blit_with_span_renderer( - struct cell_list *coverages, - cairo_span_renderer_t *span_renderer, - struct pool *span_pool, - int y, - int height, - int xmin, - int xmax); - -static glitter_status_t -blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height); - -#define GLITTER_BLIT_COVERAGES_ARGS \ - cairo_span_renderer_t *span_renderer, \ - struct pool *span_pool - -#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \ - cairo_status_t status = blit_with_span_renderer (cells, \ - span_renderer, \ - span_pool, \ - y, height, \ - xmin, xmax); \ - if (unlikely (status)) \ - return status; \ -} while (0) - -#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \ - cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \ - if (unlikely (status)) \ - return status; \ -} while (0) - /*------------------------------------------------------------------------- * glitter-paths.h */ @@ -196,15 +163,6 @@ glitter_scan_converter_reset( int xmin, int ymin, int xmax, int ymax); -/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan - * converter. The coordinates represent pixel positions scaled by - * 2**GLITTER_PIXEL_BITS. If this function fails then the scan - * converter should be reset or destroyed. Dir must be +1 or -1, - * with the latter reversing the orientation of the edge. */ -I glitter_status_t -glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, - const cairo_edge_t *edge); - /* Render the polygon in the scan converter to the given A8 format * image raster. Only the pixels accessible as pixels[y*stride+x] for * x,y inside the clip box are written to, where xmin <= x < xmax, @@ -215,14 +173,6 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, * rule is used. * * The scan converter must be reset or destroyed after this call. */ -#ifndef GLITTER_BLIT_COVERAGES_ARGS -# define GLITTER_BLIT_COVERAGES_ARGS unsigned char *raster_pixels, long raster_stride -#endif -I glitter_status_t -glitter_scan_converter_render( - glitter_scan_converter_t *converter, - int nonzero_fill, - GLITTER_BLIT_COVERAGES_ARGS); /*------------------------------------------------------------------------- * glitter-paths.c: Implementation internal types @@ -287,7 +237,6 @@ typedef int grid_scaled_y_t; * vertices are given in grid scaled coordinates. The scale factor * comes from needing to accurately represent the area 0.5*dx*dy of a * triangle with base dx and height dy in grid scaled numbers. */ -typedef int grid_area_t; #define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ /* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ @@ -313,7 +262,7 @@ typedef int grid_area_t; struct quorem { int32_t quo; - int32_t rem; + int64_t rem; }; /* Header for a chunk of memory in a memory pool. */ @@ -328,9 +277,14 @@ struct _pool_chunk { * chunk in the pool header. */ struct _pool_chunk *prev_chunk; - /* Actual data starts here. Well aligned for pointers. */ + /* Actual data starts here. Well aligned even for 64 bit types. */ + int64_t data; }; +/* The int64_t data member of _pool_chunk just exists to enforce alignment, + * it shouldn't be included in the allocated size for the struct. */ +#define SIZEOF_POOL_CHUNK (sizeof(struct _pool_chunk) - sizeof(int64_t)) + /* A memory pool. This is supposed to be embedded on the stack or * within some other structure. It may optionally be followed by an * embedded array from which requests are fulfilled until @@ -339,6 +293,8 @@ struct pool { /* Chunk we're allocating from. */ struct _pool_chunk *current; + jmp_buf *jmp; + /* Free list of previously allocated chunks. All have >= default * capacity. */ struct _pool_chunk *first_free; @@ -348,14 +304,29 @@ struct pool { /* Header for the sentinel chunk. Directly following the pool * struct should be some space for embedded elements from which - * the sentinel chunk allocates from. */ - struct _pool_chunk sentinel[1]; + * the sentinel chunk allocates from. This is expressed as a char + * array so that the 'int64_t data' member of _pool_chunk isn't + * included. This way embedding struct pool in other structs works + * without wasting space. */ + char sentinel[SIZEOF_POOL_CHUNK]; }; /* A polygon edge. */ struct edge { /* Next in y-bucket or active list. */ - struct edge *next; + struct edge *next, *prev; + + /* The clipped y of the top of the edge. */ + grid_scaled_y_t ytop; + + /* Number of subsample rows remaining to scan convert of this + * edge. */ + grid_scaled_y_t height_left; + + /* Original sign of the edge: +1 for downwards, -1 for upwards + * edges. */ + int dir; + int cell; /* Current x coordinate while the edge is on the active * list. Initialised to the x coordinate of the top of the @@ -372,49 +343,24 @@ struct edge { * full row's worth of subsample rows at a time. */ struct quorem dxdy_full; - /* The clipped y of the top of the edge. */ - grid_scaled_y_t ytop; - /* y2-y1 after orienting the edge downwards. */ - grid_scaled_y_t dy; - - /* Number of subsample rows remaining to scan convert of this - * edge. */ - grid_scaled_y_t height_left; - - /* Original sign of the edge: +1 for downwards, -1 for upwards - * edges. */ - int dir; - int vertical; + int64_t dy; }; -/* Number of subsample rows per y-bucket. Must be GRID_Y. */ -#define EDGE_Y_BUCKET_HEIGHT GRID_Y - -#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT) - -struct bucket { - /* Unsorted list of edges starting within this bucket. */ - struct edge *edges; - - /* Set to non-zero if there are edges starting strictly within the - * bucket. */ - unsigned have_inside_edges; -}; +#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y) /* A collection of sorted and vertically clipped edges of the polygon. * Edges are moved from the polygon to an active list while scan * converting. */ struct polygon { - /* The clip extents. */ - grid_scaled_x_t xmin, xmax; + /* The vertical clip extents. */ grid_scaled_y_t ymin, ymax; /* Array of edges all starting in the same bucket. An edge is put * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when * it is added to the polygon. */ - struct bucket *y_buckets; - struct bucket y_buckets_embedded[64]; + struct edge **y_buckets; + struct edge *y_buckets_embedded[64]; struct { struct pool base[1]; @@ -466,23 +412,19 @@ struct polygon { struct cell { struct cell *next; int x; - grid_area_t uncovered_area; - grid_scaled_y_t covered_height; + int16_t uncovered_area; + int16_t covered_height; }; /* A cell list represents the scan line sparsely as cells ordered by * ascending x. It is geared towards scanning the cells in order * using an internal cursor. */ struct cell_list { - /* Points to the left-most cell in the scan line. */ - struct cell *head; - /* Sentinel node */ - struct cell tail; + /* Sentinel nodes */ + struct cell head, tail; - /* Cursor state for iterating through the cell list. Points to - * a pointer to the current cell: either &cell_list->head or the next - * field of the previous cell. */ - struct cell **cursor; + /* Cursor state for iterating through the cell list. */ + struct cell *cursor, *rewind; /* Cells in the cell list are owned by the cell list and are * allocated from this pool. */ @@ -501,13 +443,14 @@ struct cell_pair { * the x-coordinate of the intercept of the edge and the scan line. */ struct active_list { /* Leftmost edge on the current scan line. */ - struct edge *head; + struct edge head, tail; /* A lower bound on the height of the active edges is used to * estimate how soon some active edge ends. We can't advance the * scan conversion by a full pixel row if an edge ends somewhere * within it. */ grid_scaled_y_t min_height; + int is_vertical; }; struct glitter_scan_converter { @@ -515,43 +458,15 @@ struct glitter_scan_converter { struct active_list active[1]; struct cell_list coverages[1]; + cairo_half_open_span_t *spans; + cairo_half_open_span_t spans_embedded[64]; + /* Clip box. */ grid_scaled_x_t xmin, xmax; grid_scaled_y_t ymin, ymax; }; -/* Compute the floored division a/b. Assumes / and % perform symmetric - * division. */ -inline static struct quorem -floored_divrem(int a, int b) -{ - struct quorem qr; - qr.quo = a/b; - qr.rem = a%b; - if ((a^b)<0 && qr.rem) { - qr.quo -= 1; - qr.rem += b; - } - return qr; -} - -/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric - * division. */ -static struct quorem -floored_muldivrem(int x, int a, int b) -{ - struct quorem qr; - long long xa = (long long)x*a; - qr.quo = xa/b; - qr.rem = xa%b; - if ((xa>=0) != (b>=0) && qr.rem) { - qr.quo -= 1; - qr.rem += b; - } - return qr; -} - -static void +static struct _pool_chunk * _pool_chunk_init( struct _pool_chunk *p, struct _pool_chunk *prev_chunk, @@ -560,33 +475,32 @@ _pool_chunk_init( p->prev_chunk = prev_chunk; p->size = 0; p->capacity = capacity; -} - -static struct _pool_chunk * -_pool_chunk_create( - struct _pool_chunk *prev_chunk, - size_t size) -{ - struct _pool_chunk *p; - size_t size_with_head = size + sizeof(struct _pool_chunk); - if (size_with_head < size) - return NULL; - p = malloc(size_with_head); - if (p) - _pool_chunk_init(p, prev_chunk, size); return p; } -static void -pool_init( - struct pool *pool, - size_t default_capacity, - size_t embedded_capacity) +static struct _pool_chunk * +_pool_chunk_create(struct pool *pool, size_t size) { - pool->current = pool->sentinel; + struct _pool_chunk *p; + + p = _cairo_malloc (SIZEOF_POOL_CHUNK + size); + if (unlikely (NULL == p)) + longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _pool_chunk_init(p, pool->current, size); +} + +static void +pool_init(struct pool *pool, + jmp_buf *jmp, + size_t default_capacity, + size_t embedded_capacity) +{ + pool->jmp = jmp; + pool->current = (void*) pool->sentinel; pool->first_free = NULL; pool->default_capacity = default_capacity; - _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); + _pool_chunk_init(pool->current, NULL, embedded_capacity); } static void @@ -596,14 +510,13 @@ pool_fini(struct pool *pool) do { while (NULL != p) { struct _pool_chunk *prev = p->prev_chunk; - if (p != pool->sentinel) + if (p != (void *) pool->sentinel) free(p); p = prev; } p = pool->first_free; pool->first_free = NULL; } while (NULL != p); - pool_init(pool, 0, 0); } /* Satisfy an allocation by first allocating a new large enough chunk @@ -633,21 +546,18 @@ _pool_alloc_from_new_chunk( } } - if (NULL == chunk) { - chunk = _pool_chunk_create (pool->current, capacity); - if (unlikely (NULL == chunk)) - return NULL; - } + if (NULL == chunk) + chunk = _pool_chunk_create (pool, capacity); pool->current = chunk; - obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + obj = ((unsigned char*)&chunk->data + chunk->size); chunk->size += size; return obj; } /* Allocate size bytes from the pool. The first allocated address - * returned from a pool is aligned to sizeof(void*). Subsequent - * addresses will maintain alignment as long as multiples of void* are + * returned from a pool is aligned to 8 bytes. Subsequent + * addresses will maintain alignment as long as multiples of 8 are * allocated. Returns the address of a new memory area or %NULL on * allocation failures. The pool retains ownership of the returned * memory. */ @@ -657,7 +567,7 @@ pool_alloc (struct pool *pool, size_t size) struct _pool_chunk *chunk = pool->current; if (size <= chunk->capacity - chunk->size) { - void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + void *obj = ((unsigned char*)&chunk->data + chunk->size); chunk->size += size; return obj; } else { @@ -671,16 +581,16 @@ pool_reset (struct pool *pool) { /* Transfer all used chunks to the chunk free list. */ struct _pool_chunk *chunk = pool->current; - if (chunk != pool->sentinel) { - while (chunk->prev_chunk != pool->sentinel) { + if (chunk != (void *) pool->sentinel) { + while (chunk->prev_chunk != (void *) pool->sentinel) { chunk = chunk->prev_chunk; } chunk->prev_chunk = pool->first_free; pool->first_free = pool->current; } /* Reset the sentinel as the current chunk. */ - pool->current = pool->sentinel; - pool->sentinel->size = 0; + pool->current = (void *) pool->sentinel; + pool->current->size = 0; } /* Rewinds the cell list's cursor to the beginning. After rewinding @@ -691,24 +601,32 @@ cell_list_rewind (struct cell_list *cells) cells->cursor = &cells->head; } -/* Rewind the cell list if its cursor has been advanced past x. */ inline static void cell_list_maybe_rewind (struct cell_list *cells, int x) { - struct cell *tail = *cells->cursor; - if (tail->x > x) - cell_list_rewind (cells); + if (x < cells->cursor->x) { + cells->cursor = cells->rewind; + if (x < cells->cursor->x) + cells->cursor = &cells->head; + } +} + +inline static void +cell_list_set_rewind (struct cell_list *cells) +{ + cells->rewind = cells->cursor; } static void -cell_list_init(struct cell_list *cells) +cell_list_init(struct cell_list *cells, jmp_buf *jmp) { - pool_init(cells->cell_pool.base, + pool_init(cells->cell_pool.base, jmp, 256*sizeof(struct cell), sizeof(cells->cell_pool.embedded)); cells->tail.next = NULL; cells->tail.x = INT_MAX; - cells->head = &cells->tail; + cells->head.x = INT_MIN; + cells->head.next = &cells->tail; cell_list_rewind (cells); } @@ -724,27 +642,23 @@ inline static void cell_list_reset (struct cell_list *cells) { cell_list_rewind (cells); - cells->head = &cells->tail; + cells->head.next = &cells->tail; pool_reset (cells->cell_pool.base); } -static struct cell * +inline static struct cell * cell_list_alloc (struct cell_list *cells, - struct cell **cursor, struct cell *tail, int x) { struct cell *cell; cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); - if (unlikely (NULL == cell)) - return NULL; - - *cursor = cell; - cell->next = tail; + cell->next = tail->next; + tail->next = cell; cell->x = x; - cell->uncovered_area = 0; - cell->covered_height = 0; + *(uint32_t *)&cell->uncovered_area = 0; + return cell; } @@ -756,24 +670,23 @@ cell_list_alloc (struct cell_list *cells, inline static struct cell * cell_list_find (struct cell_list *cells, int x) { - struct cell **cursor = cells->cursor; - struct cell *tail; - - while (1) { - UNROLL3({ - tail = *cursor; - if (tail->x >= x) { - break; - } - cursor = &tail->next; - }); - } - cells->cursor = cursor; + struct cell *tail = cells->cursor; if (tail->x == x) return tail; - return cell_list_alloc (cells, cursor, tail, x); + while (1) { + UNROLL3({ + if (tail->next->x > x) + break; + tail = tail->next; + }); + } + + if (tail->x != x) + tail = cell_list_alloc (cells, tail, x); + return cells->cursor = tail; + } /* Find two cells at x1 and x2. This is exactly equivalent @@ -787,123 +700,80 @@ inline static struct cell_pair cell_list_find_pair(struct cell_list *cells, int x1, int x2) { struct cell_pair pair; - struct cell **cursor = cells->cursor; - struct cell *cell1; - struct cell *cell2; - struct cell *newcell; - /* Find first cell at x1. */ + pair.cell1 = cells->cursor; while (1) { UNROLL3({ - cell1 = *cursor; - if (cell1->x > x1) - break; - - if (cell1->x == x1) - goto found_first; - - cursor = &cell1->next; + if (pair.cell1->next->x > x1) + break; + pair.cell1 = pair.cell1->next; }); } + if (pair.cell1->x != x1) + pair.cell1 = cell_list_alloc (cells, pair.cell1, x1); - /* New first cell at x1. */ - newcell = pool_alloc (cells->cell_pool.base, - sizeof (struct cell)); - if (likely (NULL != newcell)) { - *cursor = newcell; - newcell->next = cell1; - newcell->x = x1; - newcell->uncovered_area = 0; - newcell->covered_height = 0; - } - cell1 = newcell; - found_first: - - /* Find second cell at x2. */ + pair.cell2 = pair.cell1; while (1) { UNROLL3({ - cell2 = *cursor; - if (cell2->x > x2) - break; - if (cell2->x == x2) - goto found_second; - cursor = &cell2->next; + if (pair.cell2->next->x > x2) + break; + pair.cell2 = pair.cell2->next; }); } + if (pair.cell2->x != x2) + pair.cell2 = cell_list_alloc (cells, pair.cell2, x2); - /* New second cell at x2. */ - newcell = pool_alloc (cells->cell_pool.base, - sizeof (struct cell)); - if (likely (NULL != newcell)) { - *cursor = newcell; - newcell->next = cell2; - newcell->x = x2; - newcell->uncovered_area = 0; - newcell->covered_height = 0; - } - cell2 = newcell; - found_second: - - cells->cursor = cursor; - pair.cell1 = cell1; - pair.cell2 = cell2; + cells->cursor = pair.cell2; return pair; } -/* Add an unbounded subpixel span covering subpixels >= x to the - * coverage cells. */ -static glitter_status_t -cell_list_add_unbounded_subspan (struct cell_list *cells, - grid_scaled_x_t x) -{ - struct cell *cell; - int ix, fx; - - GRID_X_TO_INT_FRAC(x, ix, fx); - - cell = cell_list_find (cells, ix); - if (likely (cell != NULL)) { - cell->uncovered_area += 2*fx; - cell->covered_height++; - return GLITTER_STATUS_SUCCESS; - } - - return GLITTER_STATUS_NO_MEMORY; -} - /* Add a subpixel span covering [x1, x2) to the coverage cells. */ -inline static glitter_status_t -cell_list_add_subspan( - struct cell_list *cells, - grid_scaled_x_t x1, - grid_scaled_x_t x2) +inline static void +cell_list_add_subspan(struct cell_list *cells, + grid_scaled_x_t x1, + grid_scaled_x_t x2) { int ix1, fx1; int ix2, fx2; + if (x1 == x2) + return; + GRID_X_TO_INT_FRAC(x1, ix1, fx1); GRID_X_TO_INT_FRAC(x2, ix2, fx2); if (ix1 != ix2) { struct cell_pair p; p = cell_list_find_pair(cells, ix1, ix2); - if (likely (p.cell1 != NULL && p.cell2 != NULL)) { - p.cell1->uncovered_area += 2*fx1; - ++p.cell1->covered_height; - p.cell2->uncovered_area -= 2*fx2; - --p.cell2->covered_height; - return GLITTER_STATUS_SUCCESS; - } + p.cell1->uncovered_area += 2*fx1; + ++p.cell1->covered_height; + p.cell2->uncovered_area -= 2*fx2; + --p.cell2->covered_height; } else { struct cell *cell = cell_list_find(cells, ix1); - if (likely (cell != NULL)) { - cell->uncovered_area += 2*(fx1-fx2); - return GLITTER_STATUS_SUCCESS; - } + cell->uncovered_area += 2*(fx1-fx2); } - return GLITTER_STATUS_NO_MEMORY; } +inline static void full_step (struct edge *e) +{ + if (e->dy == 0) + return; + + e->x.quo += e->dxdy_full.quo; + e->x.rem += e->dxdy_full.rem; + if (e->x.rem < 0) { + e->x.quo--; + e->x.rem += e->dy; + } else if (e->x.rem >= e->dy) { + ++e->x.quo; + e->x.rem -= e->dy; + } + + e->cell = e->x.quo + (e->x.rem >= e->dy/2); +} + + /* Adds the analytical coverage of an edge crossing the current pixel * row to the coverage cells and advances the edge's x position to the * following row. @@ -921,68 +791,91 @@ cell_list_add_subspan( * This function depends on being called with all edges from the * active list in the order they appear on the list (i.e. with * non-decreasing x-coordinate.) */ -static glitter_status_t -cell_list_render_edge( - struct cell_list *cells, - struct edge *edge, - int sign) +static void +cell_list_render_edge(struct cell_list *cells, + struct edge *edge, + int sign) { - grid_scaled_y_t y1, y2, dy; - grid_scaled_x_t dx; - int ix1, ix2; + struct quorem x1, x2; grid_scaled_x_t fx1, fx2; + int ix1, ix2; - struct quorem x1 = edge->x; - struct quorem x2 = x1; + x1 = edge->x; + full_step (edge); + x2 = edge->x; - if (! edge->vertical) { - x2.quo += edge->dxdy_full.quo; - x2.rem += edge->dxdy_full.rem; - if (x2.rem >= 0) { + /* Step back from the sample location (half-subrow) to the pixel origin */ + if (edge->dy) { + x1.quo -= edge->dxdy.quo / 2; + x1.rem -= edge->dxdy.rem / 2; + if (x1.rem < 0) { + --x1.quo; + x1.rem += edge->dy; + } else if (x1.rem >= edge->dy) { + ++x1.quo; + x1.rem -= edge->dy; + } + + x2.quo -= edge->dxdy.quo / 2; + x2.rem -= edge->dxdy.rem / 2; + if (x2.rem < 0) { + --x2.quo; + x2.rem += edge->dy; + } else if (x2.rem >= edge->dy) { ++x2.quo; x2.rem -= edge->dy; } - - edge->x = x2; } GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1); GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2); + cell_list_maybe_rewind(cells, MIN(ix1, ix2)); + /* Edge is entirely within a column? */ if (ix1 == ix2) { /* We always know that ix1 is >= the cell list cursor in this * case due to the no-intersections precondition. */ struct cell *cell = cell_list_find(cells, ix1); - if (unlikely (NULL == cell)) - return GLITTER_STATUS_NO_MEMORY; - cell->covered_height += sign*GRID_Y; cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; - return GLITTER_STATUS_SUCCESS; + return; } /* Orient the edge left-to-right. */ - dx = x2.quo - x1.quo; - if (dx >= 0) { - y1 = 0; - y2 = GRID_Y; - } else { - int tmp; - tmp = ix1; ix1 = ix2; ix2 = tmp; - tmp = fx1; fx1 = fx2; fx2 = tmp; - dx = -dx; - sign = -sign; - y1 = GRID_Y; - y2 = 0; + if (ix2 < ix1) { + struct quorem tx; + int t; + + t = ix1; + ix1 = ix2; + ix2 = t; + + t = fx1; + fx1 = fx2; + fx2 = t; + + tx = x1; + x1 = x2; + x2 = tx; } - dy = y2 - y1; /* Add coverage for all pixels [ix1,ix2] on this row crossed * by the edge. */ { struct cell_pair pair; - struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx); + struct quorem y; + int64_t tmp, dx; + int y_last; + + dx = (x2.quo - x1.quo) * edge->dy + (x2.rem - x1.rem); + + tmp = (ix1 + 1) * GRID_X * edge->dy; + tmp -= x1.quo * edge->dy + x1.rem; + tmp *= GRID_Y; + + y.quo = tmp / dx; + y.rem = tmp % dx; /* When rendering a previous edge on the active list we may * advance the cell list cursor past the leftmost pixel of the @@ -998,61 +891,50 @@ cell_list_render_edge( * * The left edge touches cells past the starting cell of the * right edge. Fortunately such cases are rare. - * - * The rewinding is never necessary if the current edge stays - * within a single column because we've checked before calling - * this function that the active list order won't change. */ - cell_list_maybe_rewind(cells, ix1); + */ pair = cell_list_find_pair(cells, ix1, ix1+1); - if (unlikely (!pair.cell1 || !pair.cell2)) - return GLITTER_STATUS_NO_MEMORY; - pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1); pair.cell1->covered_height += sign*y.quo; - y.quo += y1; + y_last = y.quo; if (ix1+1 < ix2) { - struct quorem dydx_full = floored_divrem(GRID_X*dy, dx); struct cell *cell = pair.cell2; + struct quorem dydx_full; + + dydx_full.quo = GRID_Y * GRID_X * edge->dy / dx; + dydx_full.rem = GRID_Y * GRID_X * edge->dy % dx; ++ix1; do { - grid_scaled_y_t y_skip = dydx_full.quo; + y.quo += dydx_full.quo; y.rem += dydx_full.rem; if (y.rem >= dx) { - ++y_skip; + y.quo++; y.rem -= dx; } - y.quo += y_skip; - - y_skip *= sign; - cell->uncovered_area += y_skip*GRID_X; - cell->covered_height += y_skip; + cell->uncovered_area += sign*(y.quo - y_last)*GRID_X; + cell->covered_height += sign*(y.quo - y_last); + y_last = y.quo; ++ix1; cell = cell_list_find(cells, ix1); - if (unlikely (NULL == cell)) - return GLITTER_STATUS_NO_MEMORY; } while (ix1 != ix2); pair.cell2 = cell; } - pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2; - pair.cell2->covered_height += sign*(y2 - y.quo); + pair.cell2->uncovered_area += sign*(GRID_Y - y_last)*fx2; + pair.cell2->covered_height += sign*(GRID_Y - y_last); } - - return GLITTER_STATUS_SUCCESS; } static void -polygon_init (struct polygon *polygon) +polygon_init (struct polygon *polygon, jmp_buf *jmp) { polygon->ymin = polygon->ymax = 0; - polygon->xmin = polygon->xmax = 0; polygon->y_buckets = polygon->y_buckets_embedded; - pool_init (polygon->edge_pool.base, + pool_init (polygon->edge_pool.base, jmp, 8192 - sizeof (struct _pool_chunk), sizeof (polygon->edge_pool.embedded)); } @@ -1071,18 +953,15 @@ polygon_fini (struct polygon *polygon) * [ymin,ymax). */ static glitter_status_t polygon_reset (struct polygon *polygon, - grid_scaled_x_t xmin, - grid_scaled_x_t xmax, grid_scaled_y_t ymin, grid_scaled_y_t ymax) { unsigned h = ymax - ymin; - unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1, - ymin); + unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin); pool_reset(polygon->edge_pool.base); - if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)) + if (unlikely (h > 0x7FFFFFFFU - GRID_Y)) goto bail_no_mem; /* even if you could, you wouldn't want to. */ if (polygon->y_buckets != polygon->y_buckets_embedded) @@ -1091,122 +970,47 @@ polygon_reset (struct polygon *polygon, polygon->y_buckets = polygon->y_buckets_embedded; if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { polygon->y_buckets = _cairo_malloc_ab (num_buckets, - sizeof (struct bucket)); + sizeof (struct edge *)); if (unlikely (NULL == polygon->y_buckets)) goto bail_no_mem; } - memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket)); + memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); polygon->ymin = ymin; polygon->ymax = ymax; - polygon->xmin = xmin; - polygon->xmax = xmax; return GLITTER_STATUS_SUCCESS; - bail_no_mem: +bail_no_mem: polygon->ymin = 0; polygon->ymax = 0; return GLITTER_STATUS_NO_MEMORY; } static void -_polygon_insert_edge_into_its_y_bucket( - struct polygon *polygon, - struct edge *e) +_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, + struct edge *e) { - unsigned j = e->ytop - polygon->ymin; - unsigned ix = j / EDGE_Y_BUCKET_HEIGHT; - unsigned offset = j % EDGE_Y_BUCKET_HEIGHT; - struct edge **ptail = &polygon->y_buckets[ix].edges; + unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); + struct edge **ptail = &polygon->y_buckets[ix]; e->next = *ptail; *ptail = e; - polygon->y_buckets[ix].have_inside_edges |= offset; -} - -inline static glitter_status_t -polygon_add_edge (struct polygon *polygon, - const cairo_edge_t *edge) -{ - struct edge *e; - grid_scaled_x_t dx; - grid_scaled_y_t dy; - grid_scaled_y_t ytop, ybot; - grid_scaled_y_t ymin = polygon->ymin; - grid_scaled_y_t ymax = polygon->ymax; - - assert (edge->bottom > edge->top); - - if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) - return GLITTER_STATUS_SUCCESS; - - e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); - if (unlikely (NULL == e)) - return GLITTER_STATUS_NO_MEMORY; - - dx = edge->line.p2.x - edge->line.p1.x; - dy = edge->line.p2.y - edge->line.p1.y; - e->dy = dy; - e->dir = edge->dir; - - ytop = edge->top >= ymin ? edge->top : ymin; - ybot = edge->bottom <= ymax ? edge->bottom : ymax; - e->ytop = ytop; - e->height_left = ybot - ytop; - - if (dx == 0) { - e->vertical = TRUE; - e->x.quo = edge->line.p1.x; - e->x.rem = 0; - e->dxdy.quo = 0; - e->dxdy.rem = 0; - e->dxdy_full.quo = 0; - e->dxdy_full.rem = 0; - - /* Drop edges to the right of the clip extents. */ - if (e->x.quo >= polygon->xmax) - return GLITTER_STATUS_SUCCESS; - - /* Offset vertical edges at the left side of the clip extents - * to just shy of the left side. We depend on this when - * checking for possible intersections within the clip - * rectangle. */ - if (e->x.quo <= polygon->xmin) { - e->x.quo = polygon->xmin - 1; - } - } else { - e->vertical = FALSE; - e->dxdy = floored_divrem (dx, dy); - if (ytop == edge->line.p1.y) { - e->x.quo = edge->line.p1.x; - e->x.rem = 0; - } else { - e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); - e->x.quo += edge->line.p1.x; - } - - if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0) - return GLITTER_STATUS_SUCCESS; - - if (e->height_left >= GRID_Y) { - e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy); - } else { - e->dxdy_full.quo = 0; - e->dxdy_full.rem = 0; - } - } - - _polygon_insert_edge_into_its_y_bucket (polygon, e); - - e->x.rem -= dy; /* Bias the remainder for faster - * edge advancement. */ - return GLITTER_STATUS_SUCCESS; } static void active_list_reset (struct active_list *active) { - active->head = NULL; + active->head.height_left = INT_MAX; + active->head.dy = 0; + active->head.cell = INT_MIN; + active->head.prev = NULL; + active->head.next = &active->tail; + active->tail.prev = &active->head; + active->tail.next = NULL; + active->tail.cell = INT_MAX; + active->tail.height_left = INT_MAX; + active->tail.dy = 0; active->min_height = 0; + active->is_vertical = 1; } static void @@ -1236,30 +1040,45 @@ active_list_init(struct active_list *active) static struct edge * merge_sorted_edges (struct edge *head_a, struct edge *head_b) { - struct edge *head, **next; + struct edge *head, **next, *prev; + int32_t x; - head = head_a; + prev = head_a->prev; next = &head; + if (head_a->cell <= head_b->cell) { + head = head_a; + } else { + head = head_b; + head_b->prev = prev; + goto start_with_b; + } - while (1) { - while (head_a != NULL && head_a->x.quo <= head_b->x.quo) { + do { + x = head_b->cell; + while (head_a != NULL && head_a->cell <= x) { + prev = head_a; next = &head_a->next; head_a = head_a->next; } + head_b->prev = prev; *next = head_b; if (head_a == NULL) return head; - while (head_b != NULL && head_b->x.quo <= head_a->x.quo) { +start_with_b: + x = head_a->cell; + while (head_b != NULL && head_b->cell <= x) { + prev = head_b; next = &head_b->next; head_b = head_b->next; } + head_a->prev = prev; *next = head_a; if (head_b == NULL) return head; - } + } while (1); } /* @@ -1280,8 +1099,8 @@ merge_sorted_edges (struct edge *head_a, struct edge *head_b) * (we start with a small sorted list and keep merging other lists of the same size to it). */ static struct edge * -sort_edges (struct edge *list, - unsigned int level, +sort_edges (struct edge *list, + unsigned int level, struct edge **head_out) { struct edge *head_other, *remaining; @@ -1289,60 +1108,61 @@ sort_edges (struct edge *list, head_other = list->next; - /* Single element list -> return */ if (head_other == NULL) { *head_out = list; return NULL; } - /* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges): - * - Initialize remaining to be the list containing the elements after the second in the input list. - * - Initialize *head_out to be the sorted list containing the first two element. - */ remaining = head_other->next; - if (list->x.quo <= head_other->x.quo) { + if (list->cell <= head_other->cell) { *head_out = list; - /* list->next = head_other; */ /* The input list is already like this. */ head_other->next = NULL; } else { *head_out = head_other; + head_other->prev = list->prev; head_other->next = list; + list->prev = head_other; list->next = NULL; } for (i = 0; i < level && remaining; i++) { - /* Extract a sorted list of the same size as *head_out - * (2^(i+1) elements) from the list of remaining elements. */ remaining = sort_edges (remaining, i, &head_other); *head_out = merge_sorted_edges (*head_out, head_other); } - /* *head_out now contains (at most) 2^(level+1) elements. */ - return remaining; } + static struct edge * +merge_unsorted_edges (struct edge *head, struct edge *unsorted) +{ + sort_edges (unsorted, UINT_MAX, &unsorted); + return merge_sorted_edges (head, unsorted); +} + /* Test if the edges on the active list can be safely advanced by a * full row without intersections or any edges ending. */ inline static int -active_list_can_step_full_row (struct active_list *active, - grid_scaled_x_t xmin) +can_do_full_row (struct active_list *active) { const struct edge *e; - grid_scaled_x_t prev_x = INT_MIN; + int prev_x = INT_MIN; /* Recomputes the minimum height of all edges on the active * list if we have been dropping edges. */ if (active->min_height <= 0) { int min_height = INT_MAX; + int is_vertical = 1; - e = active->head; + e = active->head.next; while (NULL != e) { if (e->height_left < min_height) min_height = e->height_left; + is_vertical &= e->dy == 0; e = e->next; } + active->is_vertical = is_vertical; active->min_height = min_height; } @@ -1350,31 +1170,28 @@ active_list_can_step_full_row (struct active_list *active, return 0; /* Check for intersections as no edges end during the next row. */ - e = active->head; - while (NULL != e) { - struct quorem x = e->x; + for (e = active->head.next; e != &active->tail; e = e->next) { + int cell; - if (! e->vertical) { + if (e->dy) { + struct quorem x = e->x; x.quo += e->dxdy_full.quo; x.rem += e->dxdy_full.rem; - if (x.rem >= 0) - ++x.quo; - } + if (x.rem < 0) { + x.quo--; + x.rem += e->dy; + } else if (x.rem >= e->dy) { + x.quo++; + x.rem -= e->dy; + } + cell = x.quo + (x.rem >= e->dy/2); + } else + cell = e->cell; - /* There's may be an intersection if the edge sort order might - * change. */ - if (x.quo <= prev_x) { - /* Ignore intersections to the left of the clip extents. - * This assumes that all vertical edges on or at the left - * side of the clip rectangle have been shifted slightly - * to the left in polygon_add_edge(). */ - if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin) - return 0; - } - else { - prev_x = x.quo; - } - e = e->next; + if (cell < prev_x) + return 0; + + prev_x = cell; } return 1; @@ -1383,361 +1200,163 @@ active_list_can_step_full_row (struct active_list *active, /* Merges edges on the given subpixel row from the polygon to the * active_list. */ inline static void -active_list_merge_edges_from_polygon( - struct active_list *active, - grid_scaled_y_t y, - struct polygon *polygon) +active_list_merge_edges_from_bucket(struct active_list *active, + struct edge *edges) { - /* Split off the edges on the current subrow and merge them into - * the active list. */ - unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin); - int min_height = active->min_height; - struct edge *subrow_edges = NULL; - struct edge **ptail = &polygon->y_buckets[ix].edges; - - while (1) { - struct edge *tail = *ptail; - if (NULL == tail) break; - - if (y == tail->ytop) { - *ptail = tail->next; - tail->next = subrow_edges; - subrow_edges = tail; - if (tail->height_left < min_height) - min_height = tail->height_left; - } else { - ptail = &tail->next; - } - } - if (subrow_edges) { - sort_edges (subrow_edges, UINT_MAX, &subrow_edges); - active->head = merge_sorted_edges (active->head, subrow_edges); - active->min_height = min_height; - } + active->head.next = merge_unsorted_edges (active->head.next, edges); } -/* Advance the edges on the active list by one subsample row by - * updating their x positions. Drop edges from the list that end. */ -inline static void -active_list_substep_edges( - struct active_list *active) +inline static int +polygon_fill_buckets (struct active_list *active, + struct edge *edge, + int y, + struct edge **buckets) { - struct edge **cursor = &active->head; - grid_scaled_x_t prev_x = INT_MIN; - struct edge *unsorted = NULL; + grid_scaled_y_t min_height = active->min_height; + int is_vertical = active->is_vertical; + int max_suby = 0; - while (1) { - struct edge *edge; - - UNROLL3({ - edge = *cursor; - if (NULL == edge) - break; - - if (0 != --edge->height_left) { - edge->x.quo += edge->dxdy.quo; - edge->x.rem += edge->dxdy.rem; - if (edge->x.rem >= 0) { - ++edge->x.quo; - edge->x.rem -= edge->dy; - } - - if (edge->x.quo < prev_x) { - *cursor = edge->next; - edge->next = unsorted; - unsorted = edge; - } else { - prev_x = edge->x.quo; - cursor = &edge->next; - } - - } else { - *cursor = edge->next; - } - }); + while (edge) { + struct edge *next = edge->next; + int suby = edge->ytop - y; + if (buckets[suby]) + buckets[suby]->prev = edge; + edge->next = buckets[suby]; + edge->prev = NULL; + buckets[suby] = edge; + if (edge->height_left < min_height) + min_height = edge->height_left; + is_vertical &= edge->dy == 0; + edge = next; + if (suby > max_suby) + max_suby = suby; } - if (unsorted) { - sort_edges (unsorted, UINT_MAX, &unsorted); - active->head = merge_sorted_edges (active->head, unsorted); - } + active->is_vertical = is_vertical; + active->min_height = min_height; + + return max_suby; } -inline static glitter_status_t -apply_nonzero_fill_rule_for_subrow (struct active_list *active, - struct cell_list *coverages) +static void step (struct edge *edge) { - struct edge *edge = active->head; - int winding = 0; - int xstart; - int xend; - int status; - - cell_list_rewind (coverages); - - while (NULL != edge) { - xstart = edge->x.quo; - winding = edge->dir; - while (1) { - edge = edge->next; - if (NULL == edge) - return cell_list_add_unbounded_subspan (coverages, xstart); - - winding += edge->dir; - if (0 == winding) { - if (edge->next == NULL || edge->next->x.quo != edge->x.quo) - break; - } - } - - xend = edge->x.quo; - status = cell_list_add_subspan (coverages, xstart, xend); - if (unlikely (status)) - return status; - - edge = edge->next; - } - - return GLITTER_STATUS_SUCCESS; -} - -static glitter_status_t -apply_evenodd_fill_rule_for_subrow (struct active_list *active, - struct cell_list *coverages) -{ - struct edge *edge = active->head; - int xstart; - int xend; - int status; - - cell_list_rewind (coverages); - - while (NULL != edge) { - xstart = edge->x.quo; - - while (1) { - edge = edge->next; - if (NULL == edge) - return cell_list_add_unbounded_subspan (coverages, xstart); - - if (edge->next == NULL || edge->next->x.quo != edge->x.quo) - break; - - edge = edge->next; - } - - xend = edge->x.quo; - status = cell_list_add_subspan (coverages, xstart, xend); - if (unlikely (status)) - return status; - - edge = edge->next; - } - - return GLITTER_STATUS_SUCCESS; -} - -static glitter_status_t -apply_nonzero_fill_rule_and_step_edges (struct active_list *active, - struct cell_list *coverages) -{ - struct edge **cursor = &active->head; - struct edge *left_edge; - int status; - - left_edge = *cursor; - while (NULL != left_edge) { - struct edge *right_edge; - int winding = left_edge->dir; - - left_edge->height_left -= GRID_Y; - if (left_edge->height_left) - cursor = &left_edge->next; - else - *cursor = left_edge->next; - - while (1) { - right_edge = *cursor; - if (NULL == right_edge) - return cell_list_render_edge (coverages, left_edge, +1); - - right_edge->height_left -= GRID_Y; - if (right_edge->height_left) - cursor = &right_edge->next; - else - *cursor = right_edge->next; - - winding += right_edge->dir; - if (0 == winding) { - if (right_edge->next == NULL || - right_edge->next->x.quo != right_edge->x.quo) - { - break; - } - } - - if (! right_edge->vertical) { - right_edge->x.quo += right_edge->dxdy_full.quo; - right_edge->x.rem += right_edge->dxdy_full.rem; - if (right_edge->x.rem >= 0) { - ++right_edge->x.quo; - right_edge->x.rem -= right_edge->dy; - } - } - } - - status = cell_list_render_edge (coverages, left_edge, +1); - if (unlikely (status)) - return status; - - status = cell_list_render_edge (coverages, right_edge, -1); - if (unlikely (status)) - return status; - - left_edge = *cursor; - } - - return GLITTER_STATUS_SUCCESS; -} - -static glitter_status_t -apply_evenodd_fill_rule_and_step_edges (struct active_list *active, - struct cell_list *coverages) -{ - struct edge **cursor = &active->head; - struct edge *left_edge; - int status; - - left_edge = *cursor; - while (NULL != left_edge) { - struct edge *right_edge; - int winding = left_edge->dir; - - left_edge->height_left -= GRID_Y; - if (left_edge->height_left) - cursor = &left_edge->next; - else - *cursor = left_edge->next; - - while (1) { - right_edge = *cursor; - if (NULL == right_edge) - return cell_list_render_edge (coverages, left_edge, +1); - - right_edge->height_left -= GRID_Y; - if (right_edge->height_left) - cursor = &right_edge->next; - else - *cursor = right_edge->next; - - winding += right_edge->dir; - if ((winding & 1) == 0) { - if (right_edge->next == NULL || - right_edge->next->x.quo != right_edge->x.quo) - { - break; - } - } - - if (! right_edge->vertical) { - right_edge->x.quo += right_edge->dxdy_full.quo; - right_edge->x.rem += right_edge->dxdy_full.rem; - if (right_edge->x.rem >= 0) { - ++right_edge->x.quo; - right_edge->x.rem -= right_edge->dy; - } - } - } - - status = cell_list_render_edge (coverages, left_edge, +1); - if (unlikely (status)) - return status; - - status = cell_list_render_edge (coverages, right_edge, -1); - if (unlikely (status)) - return status; - - left_edge = *cursor; - } - - return GLITTER_STATUS_SUCCESS; -} - -/* If the user hasn't configured a coverage blitter, use a default one - * that blits spans directly to an A8 raster. */ -#ifndef GLITTER_BLIT_COVERAGES - -inline static void -blit_span( - unsigned char *row_pixels, - int x, unsigned len, - grid_area_t coverage) -{ - int alpha = GRID_AREA_TO_ALPHA(coverage); - if (1 == len) { - row_pixels[x] = alpha; - } - else { - memset(row_pixels + x, alpha, len); - } -} - -#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \ - do { \ - int __y = y; \ - int __h = height; \ - do { \ - blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \ - } while (--__h); \ - } while (0) - -static void -blit_cells( - struct cell_list *cells, - unsigned char *row_pixels, - int xmin, int xmax) -{ - struct cell *cell = cells->head; - int prev_x = xmin; - int coverage = 0; - if (NULL == cell) + if (edge->dy == 0) return; - while (NULL != cell && cell->x < xmin) { - coverage += cell->covered_height; - cell = cell->next; - } - coverage *= GRID_X*2; - - for (; NULL != cell; cell = cell->next) { - int x = cell->x; - int area; - if (x >= xmax) - break; - if (x > prev_x && 0 != coverage) { - blit_span(row_pixels, prev_x, x - prev_x, coverage); - } - - coverage += cell->covered_height * GRID_X*2; - area = coverage - cell->uncovered_area; - if (area) { - blit_span(row_pixels, x, 1, area); - } - prev_x = x+1; + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem < 0) { + --edge->x.quo; + edge->x.rem += edge->dy; + } else if (edge->x.rem >= edge->dy) { + ++edge->x.quo; + edge->x.rem -= edge->dy; } - if (0 != coverage && prev_x < xmax) { - blit_span(row_pixels, prev_x, xmax - prev_x, coverage); + edge->cell = edge->x.quo + (edge->x.rem >= edge->dy/2); +} + +inline static void +sub_row (struct active_list *active, + struct cell_list *coverages, + unsigned int mask) +{ + struct edge *edge = active->head.next; + int xstart = INT_MIN, prev_x = INT_MIN; + int winding = 0; + + cell_list_rewind (coverages); + + while (&active->tail != edge) { + struct edge *next = edge->next; + int xend = edge->cell; + + if (--edge->height_left) { + step (edge); + + if (edge->cell < prev_x) { + struct edge *pos = edge->prev; + pos->next = next; + next->prev = pos; + do { + pos = pos->prev; + } while (edge->cell < pos->cell); + pos->next->prev = edge; + edge->next = pos->next; + edge->prev = pos; + pos->next = edge; + } else + prev_x = edge->cell; + active->min_height = -1; + } else { + edge->prev->next = next; + next->prev = edge->prev; + } + + winding += edge->dir; + if ((winding & mask) == 0) { + if (next->cell != xend) { + cell_list_add_subspan (coverages, xstart, xend); + xstart = INT_MIN; + } + } else if (xstart == INT_MIN) + xstart = xend; + + edge = next; + } +} + +inline static void dec (struct active_list *a, struct edge *e, int h) +{ + e->height_left -= h; + if (e->height_left == 0) { + e->prev->next = e->next; + e->next->prev = e->prev; + a->min_height = -1; } } -#endif /* GLITTER_BLIT_COVERAGES */ static void -_glitter_scan_converter_init(glitter_scan_converter_t *converter) +full_row (struct active_list *active, + struct cell_list *coverages, + unsigned int mask) { - polygon_init(converter->polygon); + struct edge *left = active->head.next; + + while (&active->tail != left) { + struct edge *right; + int winding; + + dec (active, left, GRID_Y); + + winding = left->dir; + right = left->next; + do { + dec (active, right, GRID_Y); + + winding += right->dir; + if ((winding & mask) == 0 && right->next->cell != right->cell) + break; + + full_step (right); + + right = right->next; + } while (1); + + cell_list_set_rewind (coverages); + cell_list_render_edge (coverages, left, +1); + cell_list_render_edge (coverages, right, -1); + + left = right->next; + } +} + +static void +_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp) +{ + polygon_init(converter->polygon, jmp); active_list_init(converter->active); - cell_list_init(converter->coverages); + cell_list_init(converter->coverages, jmp); converter->xmin=0; converter->ymin=0; converter->xmax=0; @@ -1745,14 +1364,18 @@ _glitter_scan_converter_init(glitter_scan_converter_t *converter) } static void -_glitter_scan_converter_fini(glitter_scan_converter_t *converter) +_glitter_scan_converter_fini(glitter_scan_converter_t *self) { - polygon_fini(converter->polygon); - cell_list_fini(converter->coverages); - converter->xmin=0; - converter->ymin=0; - converter->xmax=0; - converter->ymax=0; + if (self->spans != self->spans_embedded) + free (self->spans); + + polygon_fini(self->polygon); + cell_list_fini(self->coverages); + + self->xmin=0; + self->ymin=0; + self->xmax=0; + self->ymax=0; } static grid_scaled_t @@ -1775,15 +1398,26 @@ int_to_grid_scaled(int i, int scale) I glitter_status_t glitter_scan_converter_reset( - glitter_scan_converter_t *converter, - int xmin, int ymin, - int xmax, int ymax) + glitter_scan_converter_t *converter, + int xmin, int ymin, + int xmax, int ymax) { glitter_status_t status; + int max_num_spans; converter->xmin = 0; converter->xmax = 0; converter->ymin = 0; converter->ymax = 0; + max_num_spans = xmax - xmin + 1; + + if (max_num_spans > ARRAY_LENGTH(converter->spans_embedded)) { + converter->spans = _cairo_malloc_ab (max_num_spans, + sizeof (cairo_half_open_span_t)); + if (unlikely (converter->spans == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + converter->spans = converter->spans_embedded; + xmin = int_to_grid_scaled_x(xmin); ymin = int_to_grid_scaled_y(ymin); xmax = int_to_grid_scaled_x(xmax); @@ -1791,7 +1425,7 @@ glitter_scan_converter_reset( active_list_reset(converter->active); cell_list_reset(converter->coverages); - status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax); + status = polygon_reset(converter->polygon, ymin, ymax); if (status) return status; @@ -1822,260 +1456,184 @@ glitter_scan_converter_reset( #endif #define INPUT_TO_GRID_general(in, out, grid_scale) do { \ - long long tmp__ = (long long)(grid_scale) * (in); \ - tmp__ >>= GLITTER_INPUT_BITS; \ - (out) = tmp__; \ + long long tmp__ = (long long)(grid_scale) * (in); \ + tmp__ += 1 << (GLITTER_INPUT_BITS-1); \ + tmp__ >>= GLITTER_INPUT_BITS; \ + (out) = tmp__; \ } while (0) -I glitter_status_t +inline static void +polygon_add_edge (struct polygon *polygon, + const cairo_edge_t *edge) +{ + struct edge *e; + grid_scaled_y_t ytop, ybot; + const cairo_point_t *p1, *p2; + + INPUT_TO_GRID_Y (edge->top, ytop); + if (ytop < polygon->ymin) + ytop = polygon->ymin; + + INPUT_TO_GRID_Y (edge->bottom, ybot); + if (ybot > polygon->ymax) + ybot = polygon->ymax; + + if (ybot <= ytop) + return; + + e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); + + e->ytop = ytop; + e->height_left = ybot - ytop; + if (edge->line.p2.y > edge->line.p1.y) { + e->dir = edge->dir; + p1 = &edge->line.p1; + p2 = &edge->line.p2; + } else { + e->dir = -edge->dir; + p1 = &edge->line.p2; + p2 = &edge->line.p1; + } + + if (p2->x == p1->x) { + e->cell = p1->x; + e->x.quo = p1->x; + e->x.rem = 0; + e->dxdy.quo = e->dxdy.rem = 0; + e->dxdy_full.quo = e->dxdy_full.rem = 0; + e->dy = 0; + } else { + int64_t Ex, Ey, tmp; + + Ex = (int64_t)(p2->x - p1->x) * GRID_X; + Ey = (int64_t)(p2->y - p1->y) * GRID_Y * (2 << GLITTER_INPUT_BITS); + + e->dxdy.quo = Ex * (2 << GLITTER_INPUT_BITS) / Ey; + e->dxdy.rem = Ex * (2 << GLITTER_INPUT_BITS) % Ey; + + tmp = (int64_t)(2*ytop + 1) << GLITTER_INPUT_BITS; + tmp -= (int64_t)p1->y * GRID_Y * 2; + tmp *= Ex; + e->x.quo = tmp / Ey; + e->x.rem = tmp % Ey; + +#if GRID_X_BITS == GLITTER_INPUT_BITS + e->x.quo += p1->x; +#else + tmp = (int64_t)p1->x * GRID_X; + e->x.quo += tmp >> GLITTER_INPUT_BITS; + e->x.rem += ((tmp & ((1 << GLITTER_INPUT_BITS) - 1)) * Ey) / (1 << GLITTER_INPUT_BITS); +#endif + + if (e->x.rem < 0) { + e->x.quo--; + e->x.rem += Ey; + } else if (e->x.rem >= Ey) { + e->x.quo++; + e->x.rem -= Ey; + } + + if (e->height_left >= GRID_Y) { + tmp = Ex * (2 * GRID_Y << GLITTER_INPUT_BITS); + e->dxdy_full.quo = tmp / Ey; + e->dxdy_full.rem = tmp % Ey; + } else + e->dxdy_full.quo = e->dxdy_full.rem = 0; + + e->cell = e->x.quo + (e->x.rem >= Ey/2); + e->dy = Ey; + } + + _polygon_insert_edge_into_its_y_bucket (polygon, e); +} + +/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan + * converter. The coordinates represent pixel positions scaled by + * 2**GLITTER_PIXEL_BITS. If this function fails then the scan + * converter should be reset or destroyed. Dir must be +1 or -1, + * with the latter reversing the orientation of the edge. */ +I void glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, const cairo_edge_t *edge) { - cairo_edge_t e; - - INPUT_TO_GRID_Y (edge->top, e.top); - INPUT_TO_GRID_Y (edge->bottom, e.bottom); - if (e.top >= e.bottom) - return GLITTER_STATUS_SUCCESS; - - /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ - INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); - INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); - if (e.line.p1.y == e.line.p2.y) - return GLITTER_STATUS_SUCCESS; - - INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); - INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); - - e.dir = edge->dir; - - return polygon_add_edge (converter->polygon, &e); -} - -#ifndef GLITTER_BLIT_COVERAGES_BEGIN -# define GLITTER_BLIT_COVERAGES_BEGIN -#endif - -#ifndef GLITTER_BLIT_COVERAGES_END -# define GLITTER_BLIT_COVERAGES_END -#endif - -#ifndef GLITTER_BLIT_COVERAGES_EMPTY -# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax) -#endif - -static cairo_bool_t -active_list_is_vertical (struct active_list *active) -{ - struct edge *e; - - for (e = active->head; e != NULL; e = e->next) { - if (! e->vertical) - return FALSE; - } - - return TRUE; + polygon_add_edge (converter->polygon, edge); } static void step_edges (struct active_list *active, int count) { - struct edge **cursor = &active->head; struct edge *edge; - for (edge = *cursor; edge != NULL; edge = *cursor) { - edge->height_left -= GRID_Y * count; - if (edge->height_left) - cursor = &edge->next; - else - *cursor = edge->next; + count *= GRID_Y; + for (edge = active->head.next; edge != &active->tail; edge = edge->next) { + edge->height_left -= count; + if (! edge->height_left) { + edge->prev->next = edge->next; + edge->next->prev = edge->prev; + active->min_height = -1; + } } } -I glitter_status_t -glitter_scan_converter_render( - glitter_scan_converter_t *converter, - int nonzero_fill, - GLITTER_BLIT_COVERAGES_ARGS) -{ - int i, j; - int ymax_i = converter->ymax / GRID_Y; - int ymin_i = converter->ymin / GRID_Y; - int xmin_i, xmax_i; - grid_scaled_x_t xmin = converter->xmin; - int h = ymax_i - ymin_i; - struct polygon *polygon = converter->polygon; - struct cell_list *coverages = converter->coverages; - struct active_list *active = converter->active; - - xmin_i = converter->xmin / GRID_X; - xmax_i = converter->xmax / GRID_X; - if (xmin_i >= xmax_i) - return GLITTER_STATUS_SUCCESS; - - /* Let the coverage blitter initialise itself. */ - GLITTER_BLIT_COVERAGES_BEGIN; - - /* Render each pixel row. */ - for (i = 0; i < h; i = j) { - int do_full_step = 0; - glitter_status_t status = 0; - - j = i + 1; - - /* Determine if we can ignore this row or use the full pixel - * stepper. */ - if (polygon->y_buckets[i].edges == NULL) { - if (! active->head) { - for (; j < h && ! polygon->y_buckets[j].edges; j++) - ; - GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i); - continue; - } - do_full_step = active_list_can_step_full_row (active, xmin); - } - else if (! polygon->y_buckets[i].have_inside_edges) { - grid_scaled_y_t y = (i+ymin_i)*GRID_Y; - active_list_merge_edges_from_polygon (active, y, polygon); - do_full_step = active_list_can_step_full_row (active, xmin); - } - - if (do_full_step) { - /* Step by a full pixel row's worth. */ - if (nonzero_fill) { - status = apply_nonzero_fill_rule_and_step_edges (active, - coverages); - } else { - status = apply_evenodd_fill_rule_and_step_edges (active, - coverages); - } - - if (active_list_is_vertical (active)) { - while (j < h && - polygon->y_buckets[j].edges == NULL && - active->min_height >= 2*GRID_Y) - { - active->min_height -= GRID_Y; - j++; - } - if (j != i + 1) - step_edges (active, j - (i + 1)); - } - } else { - /* Supersample this row. */ - grid_scaled_y_t suby; - for (suby = 0; suby < GRID_Y; suby++) { - grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby; - - active_list_merge_edges_from_polygon (active, y, polygon); - - if (nonzero_fill) { - status |= apply_nonzero_fill_rule_for_subrow (active, - coverages); - } else { - status |= apply_evenodd_fill_rule_for_subrow (active, - coverages); - } - - active_list_substep_edges(active); - } - } - - if (unlikely (status)) - return status; - - GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i); - cell_list_reset (coverages); - - if (! active->head) - active->min_height = INT_MAX; - else - active->min_height -= GRID_Y; - } - - /* Clean up the coverage blitter. */ - GLITTER_BLIT_COVERAGES_END; - - return GLITTER_STATUS_SUCCESS; -} - -/*------------------------------------------------------------------------- - * cairo specific implementation: the coverage blitter and - * scan converter subclass. */ - static glitter_status_t -blit_with_span_renderer (struct cell_list *cells, - cairo_span_renderer_t *renderer, - struct pool *span_pool, - int y, int height, - int xmin, int xmax) +blit_a8 (struct cell_list *cells, + cairo_span_renderer_t *renderer, + cairo_half_open_span_t *spans, + int y, int height, + int xmin, int xmax) { - struct cell *cell = cells->head; - int prev_x = xmin; - int cover = 0; - cairo_half_open_span_t *spans; + struct cell *cell = cells->head.next; + int prev_x = xmin, last_x = -1; + int16_t cover = 0, last_cover = 0; unsigned num_spans; - if (cell == NULL) - return blit_empty_with_span_renderer (renderer, y, height); + if (cell == &cells->tail) + return CAIRO_STATUS_SUCCESS; /* Skip cells to the left of the clip region. */ - while (cell != NULL && cell->x < xmin) { + while (cell->x < xmin) { cover += cell->covered_height; cell = cell->next; } cover *= GRID_X*2; - /* Count number of cells remaining. */ - { - struct cell *next = cell; - num_spans = 1; - while (next != NULL) { - next = next->next; - ++num_spans; - } - num_spans = 2*num_spans; - } - - /* Allocate enough spans for the row. */ - pool_reset (span_pool); - spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans); - if (unlikely (spans == NULL)) - return GLITTER_STATUS_NO_MEMORY; - - num_spans = 0; - /* Form the spans from the coverages and areas. */ - for (; cell != NULL; cell = cell->next) { + num_spans = 0; + for (; cell->x < xmax; cell = cell->next) { int x = cell->x; - int area; + int16_t area; - if (x >= xmax) - break; - - if (x > prev_x) { + if (x > prev_x && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + last_cover = cover; + last_x = prev_x; ++num_spans; } cover += cell->covered_height*GRID_X*2; area = cover - cell->uncovered_area; - spans[num_spans].x = x; - spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); - ++num_spans; + if (area != last_cover) { + spans[num_spans].x = x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); + last_cover = area; + last_x = x; + ++num_spans; + } prev_x = x+1; } - if (prev_x <= xmax) { + if (prev_x <= xmax && cover != last_cover) { spans[num_spans].x = prev_x; spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + last_cover = cover; + last_x = prev_x; ++num_spans; } - if (prev_x < xmax && cover) { + if (last_x < xmax && last_cover) { spans[num_spans].x = xmax; spans[num_spans].coverage = 0; ++num_spans; @@ -2085,10 +1643,163 @@ blit_with_span_renderer (struct cell_list *cells, return renderer->render_rows (renderer, y, height, spans, num_spans); } +#define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0) static glitter_status_t -blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height) +blit_a1 (struct cell_list *cells, + cairo_span_renderer_t *renderer, + cairo_half_open_span_t *spans, + int y, int height, + int xmin, int xmax) { - return renderer->render_rows (renderer, y, height, NULL, 0); + struct cell *cell = cells->head.next; + int prev_x = xmin, last_x = -1; + int16_t cover = 0; + uint8_t coverage, last_cover = 0; + unsigned num_spans; + + if (cell == &cells->tail) + return CAIRO_STATUS_SUCCESS; + + /* Skip cells to the left of the clip region. */ + while (cell->x < xmin) { + cover += cell->covered_height; + cell = cell->next; + } + cover *= GRID_X*2; + + /* Form the spans from the coverages and areas. */ + num_spans = 0; + for (; cell->x < xmax; cell = cell->next) { + int x = cell->x; + int16_t area; + + coverage = GRID_AREA_TO_A1 (cover); + if (x > prev_x && coverage != last_cover) { + last_x = spans[num_spans].x = prev_x; + last_cover = spans[num_spans].coverage = coverage; + ++num_spans; + } + + cover += cell->covered_height*GRID_X*2; + area = cover - cell->uncovered_area; + + coverage = GRID_AREA_TO_A1 (area); + if (coverage != last_cover) { + last_x = spans[num_spans].x = x; + last_cover = spans[num_spans].coverage = coverage; + ++num_spans; + } + + prev_x = x+1; + } + + coverage = GRID_AREA_TO_A1 (cover); + if (prev_x <= xmax && coverage != last_cover) { + last_x = spans[num_spans].x = prev_x; + last_cover = spans[num_spans].coverage = coverage; + ++num_spans; + } + + if (last_x < xmax && last_cover) { + spans[num_spans].x = xmax; + spans[num_spans].coverage = 0; + ++num_spans; + } + if (num_spans == 1) + return CAIRO_STATUS_SUCCESS; + + /* Dump them into the renderer. */ + return renderer->render_rows (renderer, y, height, spans, num_spans); +} + + +I void +glitter_scan_converter_render(glitter_scan_converter_t *converter, + unsigned int winding_mask, + int antialias, + cairo_span_renderer_t *renderer) +{ + int i, j; + int ymax_i = converter->ymax / GRID_Y; + int ymin_i = converter->ymin / GRID_Y; + int xmin_i, xmax_i; + int h = ymax_i - ymin_i; + struct polygon *polygon = converter->polygon; + struct cell_list *coverages = converter->coverages; + struct active_list *active = converter->active; + struct edge *buckets[GRID_Y] = { 0 }; + + xmin_i = converter->xmin / GRID_X; + xmax_i = converter->xmax / GRID_X; + if (xmin_i >= xmax_i) + return; + + /* Render each pixel row. */ + for (i = 0; i < h; i = j) { + int do_full_row = 0; + + j = i + 1; + + /* Determine if we can ignore this row or use the full pixel + * stepper. */ + if (polygon_fill_buckets (active, + polygon->y_buckets[i], + (i+ymin_i)*GRID_Y, + buckets) == 0) { + if (buckets[0]) { + active_list_merge_edges_from_bucket (active, buckets[0]); + buckets[0] = NULL; + } + + if (active->head.next == &active->tail) { + active->min_height = INT_MAX; + active->is_vertical = 1; + for (; j < h && ! polygon->y_buckets[j]; j++) + ; + continue; + } + + do_full_row = can_do_full_row (active); + } + + if (do_full_row) { + /* Step by a full pixel row's worth. */ + full_row (active, coverages, winding_mask); + + if (active->is_vertical) { + while (j < h && + polygon->y_buckets[j] == NULL && + active->min_height >= 2*GRID_Y) + { + active->min_height -= GRID_Y; + j++; + } + if (j != i + 1) + step_edges (active, j - (i + 1)); + } + } else { + int sub; + + /* Subsample this row. */ + for (sub = 0; sub < GRID_Y; sub++) { + if (buckets[sub]) { + active_list_merge_edges_from_bucket (active, buckets[sub]); + buckets[sub] = NULL; + } + sub_row (active, coverages, winding_mask); + } + } + + if (antialias) + blit_a8 (coverages, renderer, converter->spans, + i+ymin_i, j-i, xmin_i, xmax_i); + else + blit_a1 (coverages, renderer, converter->spans, + i+ymin_i, j-i, xmin_i, xmax_i); + cell_list_reset (coverages); + + active->min_height -= GRID_Y; + } } struct _cairo_tor_scan_converter { @@ -2096,11 +1807,9 @@ struct _cairo_tor_scan_converter { glitter_scan_converter_t converter[1]; cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; - struct { - struct pool base[1]; - cairo_half_open_span_t embedded[32]; - } span_pool; + jmp_buf jmp; }; typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t; @@ -2113,50 +1822,24 @@ _cairo_tor_scan_converter_destroy (void *converter) return; } _glitter_scan_converter_fini (self->converter); - pool_fini (self->span_pool.base); free(self); } -static cairo_status_t -_cairo_tor_scan_converter_add_edge (void *converter, - const cairo_point_t *p1, - const cairo_point_t *p2, - int top, int bottom, - int dir) -{ - cairo_tor_scan_converter_t *self = converter; - cairo_status_t status; - cairo_edge_t edge; - - edge.line.p1 = *p1; - edge.line.p2 = *p2; - edge.top = top; - edge.bottom = bottom; - edge.dir = dir; - - status = glitter_scan_converter_add_edge (self->converter, &edge); - if (unlikely (status)) - return _cairo_scan_converter_set_error (self, _cairo_error (status)); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t +cairo_status_t _cairo_tor_scan_converter_add_polygon (void *converter, const cairo_polygon_t *polygon) { cairo_tor_scan_converter_t *self = converter; - cairo_status_t status; int i; - for (i = 0; i < polygon->num_edges; i++) { - status = glitter_scan_converter_add_edge (self->converter, - &polygon->edges[i]); - if (unlikely (status)) { - return _cairo_scan_converter_set_error (self, - _cairo_error (status)); - } - } +#if 0 + FILE *file = fopen ("polygon.txt", "w"); + _cairo_debug_print_polygon (file, polygon); + fclose (file); +#endif + + for (i = 0; i < polygon->num_edges; i++) + glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]); return CAIRO_STATUS_SUCCESS; } @@ -2168,13 +1851,13 @@ _cairo_tor_scan_converter_generate (void *converter, cairo_tor_scan_converter_t *self = converter; cairo_status_t status; - status = glitter_scan_converter_render (self->converter, - self->fill_rule == CAIRO_FILL_RULE_WINDING, - renderer, - self->span_pool.base); - if (unlikely (status)) + if ((status = setjmp (self->jmp))) return _cairo_scan_converter_set_error (self, _cairo_error (status)); + glitter_scan_converter_render (self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, + self->antialias != CAIRO_ANTIALIAS_NONE, + renderer); return CAIRO_STATUS_SUCCESS; } @@ -2183,33 +1866,29 @@ _cairo_tor_scan_converter_create (int xmin, int ymin, int xmax, int ymax, - cairo_fill_rule_t fill_rule) + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) { cairo_tor_scan_converter_t *self; cairo_status_t status; - self = calloc (1, sizeof(struct _cairo_tor_scan_converter)); + self = _cairo_malloc (sizeof(struct _cairo_tor_scan_converter)); if (unlikely (self == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail_nomem; } self->base.destroy = _cairo_tor_scan_converter_destroy; - self->base.add_edge = _cairo_tor_scan_converter_add_edge; - self->base.add_polygon = _cairo_tor_scan_converter_add_polygon; self->base.generate = _cairo_tor_scan_converter_generate; - pool_init (self->span_pool.base, - 250 * sizeof(self->span_pool.embedded[0]), - sizeof(self->span_pool.embedded)); - - _glitter_scan_converter_init (self->converter); + _glitter_scan_converter_init (self->converter, &self->jmp); status = glitter_scan_converter_reset (self->converter, xmin, ymin, xmax, ymax); if (unlikely (status)) goto bail; self->fill_rule = fill_rule; + self->antialias = antialias; return &self->base; diff --git a/gfx/cairo/cairo/src/cairo-tor22-scan-converter.c b/gfx/cairo/cairo/src/cairo-tor22-scan-converter.c new file mode 100644 index 000000000000..79c858e4e545 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tor22-scan-converter.c @@ -0,0 +1,1712 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* glitter-paths - polygon scan converter + * + * Copyright (c) 2008 M Joonas Pihlaja + * Copyright (c) 2007 David Turner + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/* This is the Glitter paths scan converter incorporated into cairo. + * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8 + * of + * + * https://gitweb.freedesktop.org/?p=users/joonas/glitter-paths + */ +/* Glitter-paths is a stand alone polygon rasteriser derived from + * David Turner's reimplementation of Tor Anderssons's 15x17 + * supersampling rasteriser from the Apparition graphics library. The + * main new feature here is cheaply choosing per-scan line between + * doing fully analytical coverage computation for an entire row at a + * time vs. using a supersampling approach. + * + * David Turner's code can be found at + * + * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2 + * + * In particular this file incorporates large parts of ftgrays_tor10.h + * from raster-comparison-20070813.tar.bz2 + */ +/* Overview + * + * A scan converter's basic purpose to take polygon edges and convert + * them into an RLE compressed A8 mask. This one works in two phases: + * gathering edges and generating spans. + * + * 1) As the user feeds the scan converter edges they are vertically + * clipped and bucketted into a _polygon_ data structure. The edges + * are also snapped from the user's coordinates to the subpixel grid + * coordinates used during scan conversion. + * + * user + * | + * | edges + * V + * polygon buckets + * + * 2) Generating spans works by performing a vertical sweep of pixel + * rows from top to bottom and maintaining an _active_list_ of edges + * that intersect the row. From the active list the fill rule + * determines which edges are the left and right edges of the start of + * each span, and their contribution is then accumulated into a pixel + * coverage list (_cell_list_) as coverage deltas. Once the coverage + * deltas of all edges are known we can form spans of constant pixel + * coverage by summing the deltas during a traversal of the cell list. + * At the end of a pixel row the cell list is sent to a coverage + * blitter for rendering to some target surface. + * + * The pixel coverages are computed by either supersampling the row + * and box filtering a mono rasterisation, or by computing the exact + * coverages of edges in the active list. The supersampling method is + * used whenever some edge starts or stops within the row or there are + * edge intersections in the row. + * + * polygon bucket for \ + * current pixel row | + * | | + * | activate new edges | Repeat GRID_Y times if we + * V \ are supersampling this row, + * active list / or just once if we're computing + * | | analytical coverage. + * | coverage deltas | + * V | + * pixel coverage list / + * | + * V + * coverage blitter + */ +#include "cairoint.h" +#include "cairo-spans-private.h" +#include "cairo-error-private.h" + +#include +#include +#include +#include + +/*------------------------------------------------------------------------- + * cairo specific config + */ +#define I static + +/* Prefer cairo's status type. */ +#define GLITTER_HAVE_STATUS_T 1 +#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS +#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY +typedef cairo_status_t glitter_status_t; + +/* The input coordinate scale and the rasterisation grid scales. */ +#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS +//#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS +//#define GRID_Y 15 +#define GRID_X_BITS 2 +#define GRID_Y_BITS 2 + +/* Set glitter up to use a cairo span renderer to do the coverage + * blitting. */ +struct pool; +struct cell_list; + +/*------------------------------------------------------------------------- + * glitter-paths.h + */ + +/* "Input scaled" numbers are fixed precision reals with multiplier + * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as + * pixel scaled numbers. These get converted to the internal grid + * scaled numbers as soon as possible. Internal overflow is possible + * if GRID_X/Y inside glitter-paths.c is larger than + * 1< +#include +#include + +/* All polygon coordinates are snapped onto a subsample grid. "Grid + * scaled" numbers are fixed precision reals with multiplier GRID_X or + * GRID_Y. */ +typedef int grid_scaled_t; +typedef int grid_scaled_x_t; +typedef int grid_scaled_y_t; + +/* Default x/y scale factors. + * You can either define GRID_X/Y_BITS to get a power-of-two scale + * or define GRID_X/Y separately. */ +#if !defined(GRID_X) && !defined(GRID_X_BITS) +# define GRID_X_BITS 8 +#endif +#if !defined(GRID_Y) && !defined(GRID_Y_BITS) +# define GRID_Y 15 +#endif + +/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ +#ifdef GRID_X_BITS +# define GRID_X (1 << GRID_X_BITS) +#endif +#ifdef GRID_Y_BITS +# define GRID_Y (1 << GRID_Y_BITS) +#endif + +/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into + * integer and fractional parts. The integer part is floored. */ +#if defined(GRID_X_TO_INT_FRAC) + /* do nothing */ +#elif defined(GRID_X_BITS) +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) +#else +# define GRID_X_TO_INT_FRAC(x, i, f) \ + _GRID_TO_INT_FRAC_general(x, i, f, GRID_X) +#endif + +#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \ + (i) = (t) / (m); \ + (f) = (t) % (m); \ + if ((f) < 0) { \ + --(i); \ + (f) += (m); \ + } \ +} while (0) + +#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \ + (f) = (t) & ((1 << (b)) - 1); \ + (i) = (t) >> (b); \ +} while (0) + +/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want + * to be able to represent exactly areas of subpixel trapezoids whose + * vertices are given in grid scaled coordinates. The scale factor + * comes from needing to accurately represent the area 0.5*dx*dy of a + * triangle with base dx and height dy in grid scaled numbers. */ +#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */ + +/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */ +#if GRID_XY == 510 +# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1) +#elif GRID_XY == 255 +# define GRID_AREA_TO_ALPHA(c) (c) +#elif GRID_XY == 64 +# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6)) +#elif GRID_XY == 32 +# define GRID_AREA_TO_ALPHA(c) (((c) << 3) | -(((c) & 0x20) >> 5)) +#elif GRID_XY == 128 +# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255) +#elif GRID_XY == 256 +# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255) +#elif GRID_XY == 15 +# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c)) +#elif GRID_XY == 2*256*15 +# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9) +#else +# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY) +#endif + +#define UNROLL3(x) x x x + +struct quorem { + int32_t quo; + int32_t rem; +}; + +/* Header for a chunk of memory in a memory pool. */ +struct _pool_chunk { + /* # bytes used in this chunk. */ + size_t size; + + /* # bytes total in this chunk */ + size_t capacity; + + /* Pointer to the previous chunk or %NULL if this is the sentinel + * chunk in the pool header. */ + struct _pool_chunk *prev_chunk; + + /* Actual data starts here. Well aligned for pointers. */ +}; + +/* A memory pool. This is supposed to be embedded on the stack or + * within some other structure. It may optionally be followed by an + * embedded array from which requests are fulfilled until + * malloc needs to be called to allocate a first real chunk. */ +struct pool { + /* Chunk we're allocating from. */ + struct _pool_chunk *current; + + jmp_buf *jmp; + + /* Free list of previously allocated chunks. All have >= default + * capacity. */ + struct _pool_chunk *first_free; + + /* The default capacity of a chunk. */ + size_t default_capacity; + + /* Header for the sentinel chunk. Directly following the pool + * struct should be some space for embedded elements from which + * the sentinel chunk allocates from. */ + struct _pool_chunk sentinel[1]; +}; + +/* A polygon edge. */ +struct edge { + /* Next in y-bucket or active list. */ + struct edge *next, *prev; + + /* Number of subsample rows remaining to scan convert of this + * edge. */ + grid_scaled_y_t height_left; + + /* Original sign of the edge: +1 for downwards, -1 for upwards + * edges. */ + int dir; + int vertical; + + /* Current x coordinate while the edge is on the active + * list. Initialised to the x coordinate of the top of the + * edge. The quotient is in grid_scaled_x_t units and the + * remainder is mod dy in grid_scaled_y_t units.*/ + struct quorem x; + + /* Advance of the current x when moving down a subsample line. */ + struct quorem dxdy; + + /* The clipped y of the top of the edge. */ + grid_scaled_y_t ytop; + + /* y2-y1 after orienting the edge downwards. */ + grid_scaled_y_t dy; +}; + +#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y) + +/* A collection of sorted and vertically clipped edges of the polygon. + * Edges are moved from the polygon to an active list while scan + * converting. */ +struct polygon { + /* The vertical clip extents. */ + grid_scaled_y_t ymin, ymax; + + /* Array of edges all starting in the same bucket. An edge is put + * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when + * it is added to the polygon. */ + struct edge **y_buckets; + struct edge *y_buckets_embedded[64]; + + struct { + struct pool base[1]; + struct edge embedded[32]; + } edge_pool; +}; + +/* A cell records the effect on pixel coverage of polygon edges + * passing through a pixel. It contains two accumulators of pixel + * coverage. + * + * Consider the effects of a polygon edge on the coverage of a pixel + * it intersects and that of the following one. The coverage of the + * following pixel is the height of the edge multiplied by the width + * of the pixel, and the coverage of the pixel itself is the area of + * the trapezoid formed by the edge and the right side of the pixel. + * + * +-----------------------+-----------------------+ + * | | | + * | | | + * |_______________________|_______________________| + * | \...................|.......................|\ + * | \..................|.......................| | + * | \.................|.......................| | + * | \....covered.....|.......................| | + * | \....area.......|.......................| } covered height + * | \..............|.......................| | + * |uncovered\.............|.......................| | + * | area \............|.......................| | + * |___________\...........|.......................|/ + * | | | + * | | | + * | | | + * +-----------------------+-----------------------+ + * + * Since the coverage of the following pixel will always be a multiple + * of the width of the pixel, we can store the height of the covered + * area instead. The coverage of the pixel itself is the total + * coverage minus the area of the uncovered area to the left of the + * edge. As it's faster to compute the uncovered area we only store + * that and subtract it from the total coverage later when forming + * spans to blit. + * + * The heights and areas are signed, with left edges of the polygon + * having positive sign and right edges having negative sign. When + * two edges intersect they swap their left/rightness so their + * contribution above and below the intersection point must be + * computed separately. */ +struct cell { + struct cell *next; + int x; + int16_t uncovered_area; + int16_t covered_height; +}; + +/* A cell list represents the scan line sparsely as cells ordered by + * ascending x. It is geared towards scanning the cells in order + * using an internal cursor. */ +struct cell_list { + /* Sentinel nodes */ + struct cell head, tail; + + /* Cursor state for iterating through the cell list. */ + struct cell *cursor, *rewind; + + /* Cells in the cell list are owned by the cell list and are + * allocated from this pool. */ + struct { + struct pool base[1]; + struct cell embedded[32]; + } cell_pool; +}; + +struct cell_pair { + struct cell *cell1; + struct cell *cell2; +}; + +/* The active list contains edges in the current scan line ordered by + * the x-coordinate of the intercept of the edge and the scan line. */ +struct active_list { + /* Leftmost edge on the current scan line. */ + struct edge head, tail; + + /* A lower bound on the height of the active edges is used to + * estimate how soon some active edge ends. We can't advance the + * scan conversion by a full pixel row if an edge ends somewhere + * within it. */ + grid_scaled_y_t min_height; + int is_vertical; +}; + +struct glitter_scan_converter { + struct polygon polygon[1]; + struct active_list active[1]; + struct cell_list coverages[1]; + + cairo_half_open_span_t *spans; + cairo_half_open_span_t spans_embedded[64]; + + /* Clip box. */ + grid_scaled_x_t xmin, xmax; + grid_scaled_y_t ymin, ymax; +}; + +/* Compute the floored division a/b. Assumes / and % perform symmetric + * division. */ +inline static struct quorem +floored_divrem(int a, int b) +{ + struct quorem qr; + qr.quo = a/b; + qr.rem = a%b; + if ((a^b)<0 && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric + * division. */ +static struct quorem +floored_muldivrem(int x, int a, int b) +{ + struct quorem qr; + long long xa = (long long)x*a; + qr.quo = xa/b; + qr.rem = xa%b; + if ((xa>=0) != (b>=0) && qr.rem) { + qr.quo -= 1; + qr.rem += b; + } + return qr; +} + +static struct _pool_chunk * +_pool_chunk_init( + struct _pool_chunk *p, + struct _pool_chunk *prev_chunk, + size_t capacity) +{ + p->prev_chunk = prev_chunk; + p->size = 0; + p->capacity = capacity; + return p; +} + +static struct _pool_chunk * +_pool_chunk_create(struct pool *pool, size_t size) +{ + struct _pool_chunk *p; + + p = _cairo_malloc (size + sizeof(struct _pool_chunk)); + if (unlikely (NULL == p)) + longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + return _pool_chunk_init(p, pool->current, size); +} + +static void +pool_init(struct pool *pool, + jmp_buf *jmp, + size_t default_capacity, + size_t embedded_capacity) +{ + pool->jmp = jmp; + pool->current = pool->sentinel; + pool->first_free = NULL; + pool->default_capacity = default_capacity; + _pool_chunk_init(pool->sentinel, NULL, embedded_capacity); +} + +static void +pool_fini(struct pool *pool) +{ + struct _pool_chunk *p = pool->current; + do { + while (NULL != p) { + struct _pool_chunk *prev = p->prev_chunk; + if (p != pool->sentinel) + free(p); + p = prev; + } + p = pool->first_free; + pool->first_free = NULL; + } while (NULL != p); +} + +/* Satisfy an allocation by first allocating a new large enough chunk + * and adding it to the head of the pool's chunk list. This function + * is called as a fallback if pool_alloc() couldn't do a quick + * allocation from the current chunk in the pool. */ +static void * +_pool_alloc_from_new_chunk( + struct pool *pool, + size_t size) +{ + struct _pool_chunk *chunk; + void *obj; + size_t capacity; + + /* If the allocation is smaller than the default chunk size then + * try getting a chunk off the free list. Force alloc of a new + * chunk for large requests. */ + capacity = size; + chunk = NULL; + if (size < pool->default_capacity) { + capacity = pool->default_capacity; + chunk = pool->first_free; + if (chunk) { + pool->first_free = chunk->prev_chunk; + _pool_chunk_init(chunk, pool->current, chunk->capacity); + } + } + + if (NULL == chunk) + chunk = _pool_chunk_create (pool, capacity); + pool->current = chunk; + + obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; +} + +/* Allocate size bytes from the pool. The first allocated address + * returned from a pool is aligned to sizeof(void*). Subsequent + * addresses will maintain alignment as long as multiples of void* are + * allocated. Returns the address of a new memory area or %NULL on + * allocation failures. The pool retains ownership of the returned + * memory. */ +inline static void * +pool_alloc (struct pool *pool, size_t size) +{ + struct _pool_chunk *chunk = pool->current; + + if (size <= chunk->capacity - chunk->size) { + void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size); + chunk->size += size; + return obj; + } else { + return _pool_alloc_from_new_chunk(pool, size); + } +} + +/* Relinquish all pool_alloced memory back to the pool. */ +static void +pool_reset (struct pool *pool) +{ + /* Transfer all used chunks to the chunk free list. */ + struct _pool_chunk *chunk = pool->current; + if (chunk != pool->sentinel) { + while (chunk->prev_chunk != pool->sentinel) { + chunk = chunk->prev_chunk; + } + chunk->prev_chunk = pool->first_free; + pool->first_free = pool->current; + } + /* Reset the sentinel as the current chunk. */ + pool->current = pool->sentinel; + pool->sentinel->size = 0; +} + +/* Rewinds the cell list's cursor to the beginning. After rewinding + * we're good to cell_list_find() the cell any x coordinate. */ +inline static void +cell_list_rewind (struct cell_list *cells) +{ + cells->cursor = &cells->head; +} + +inline static void +cell_list_maybe_rewind (struct cell_list *cells, int x) +{ + if (x < cells->cursor->x) { + cells->cursor = cells->rewind; + if (x < cells->cursor->x) + cells->cursor = &cells->head; + } +} + +inline static void +cell_list_set_rewind (struct cell_list *cells) +{ + cells->rewind = cells->cursor; +} + +static void +cell_list_init(struct cell_list *cells, jmp_buf *jmp) +{ + pool_init(cells->cell_pool.base, jmp, + 256*sizeof(struct cell), + sizeof(cells->cell_pool.embedded)); + cells->tail.next = NULL; + cells->tail.x = INT_MAX; + cells->head.x = INT_MIN; + cells->head.next = &cells->tail; + cell_list_rewind (cells); +} + +static void +cell_list_fini(struct cell_list *cells) +{ + pool_fini (cells->cell_pool.base); +} + +/* Empty the cell list. This is called at the start of every pixel + * row. */ +inline static void +cell_list_reset (struct cell_list *cells) +{ + cell_list_rewind (cells); + cells->head.next = &cells->tail; + pool_reset (cells->cell_pool.base); +} + +inline static struct cell * +cell_list_alloc (struct cell_list *cells, + struct cell *tail, + int x) +{ + struct cell *cell; + + cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell)); + cell->next = tail->next; + tail->next = cell; + cell->x = x; + *(uint32_t *)&cell->uncovered_area = 0; + + return cell; +} + +/* Find a cell at the given x-coordinate. Returns %NULL if a new cell + * needed to be allocated but couldn't be. Cells must be found with + * non-decreasing x-coordinate until the cell list is rewound using + * cell_list_rewind(). Ownership of the returned cell is retained by + * the cell list. */ +inline static struct cell * +cell_list_find (struct cell_list *cells, int x) +{ + struct cell *tail = cells->cursor; + + if (tail->x == x) + return tail; + + while (1) { + UNROLL3({ + if (tail->next->x > x) + break; + tail = tail->next; + }); + } + + if (tail->x != x) + tail = cell_list_alloc (cells, tail, x); + return cells->cursor = tail; + +} + +/* Find two cells at x1 and x2. This is exactly equivalent + * to + * + * pair.cell1 = cell_list_find(cells, x1); + * pair.cell2 = cell_list_find(cells, x2); + * + * except with less function call overhead. */ +inline static struct cell_pair +cell_list_find_pair(struct cell_list *cells, int x1, int x2) +{ + struct cell_pair pair; + + pair.cell1 = cells->cursor; + while (1) { + UNROLL3({ + if (pair.cell1->next->x > x1) + break; + pair.cell1 = pair.cell1->next; + }); + } + if (pair.cell1->x != x1) + pair.cell1 = cell_list_alloc (cells, pair.cell1, x1); + + pair.cell2 = pair.cell1; + while (1) { + UNROLL3({ + if (pair.cell2->next->x > x2) + break; + pair.cell2 = pair.cell2->next; + }); + } + if (pair.cell2->x != x2) + pair.cell2 = cell_list_alloc (cells, pair.cell2, x2); + + cells->cursor = pair.cell2; + return pair; +} + +/* Add a subpixel span covering [x1, x2) to the coverage cells. */ +inline static void +cell_list_add_subspan(struct cell_list *cells, + grid_scaled_x_t x1, + grid_scaled_x_t x2) +{ + int ix1, fx1; + int ix2, fx2; + + if (x1 == x2) + return; + + GRID_X_TO_INT_FRAC(x1, ix1, fx1); + GRID_X_TO_INT_FRAC(x2, ix2, fx2); + + if (ix1 != ix2) { + struct cell_pair p; + p = cell_list_find_pair(cells, ix1, ix2); + p.cell1->uncovered_area += 2*fx1; + ++p.cell1->covered_height; + p.cell2->uncovered_area -= 2*fx2; + --p.cell2->covered_height; + } else { + struct cell *cell = cell_list_find(cells, ix1); + cell->uncovered_area += 2*(fx1-fx2); + } +} + +/* Adds the analytical coverage of an edge crossing the current pixel + * row to the coverage cells and advances the edge's x position to the + * following row. + * + * This function is only called when we know that during this pixel row: + * + * 1) The relative order of all edges on the active list doesn't + * change. In particular, no edges intersect within this row to pixel + * precision. + * + * 2) No new edges start in this row. + * + * 3) No existing edges end mid-row. + * + * This function depends on being called with all edges from the + * active list in the order they appear on the list (i.e. with + * non-decreasing x-coordinate.) */ +static void +cell_list_render_edge(struct cell_list *cells, + struct edge *edge, + int sign) +{ + grid_scaled_x_t fx; + struct cell *cell; + int ix; + + GRID_X_TO_INT_FRAC(edge->x.quo, ix, fx); + + /* We always know that ix1 is >= the cell list cursor in this + * case due to the no-intersections precondition. */ + cell = cell_list_find(cells, ix); + cell->covered_height += sign*GRID_Y; + cell->uncovered_area += sign*2*fx*GRID_Y; +} + +static void +polygon_init (struct polygon *polygon, jmp_buf *jmp) +{ + polygon->ymin = polygon->ymax = 0; + polygon->y_buckets = polygon->y_buckets_embedded; + pool_init (polygon->edge_pool.base, jmp, + 8192 - sizeof (struct _pool_chunk), + sizeof (polygon->edge_pool.embedded)); +} + +static void +polygon_fini (struct polygon *polygon) +{ + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + pool_fini (polygon->edge_pool.base); +} + +/* Empties the polygon of all edges. The polygon is then prepared to + * receive new edges and clip them to the vertical range + * [ymin,ymax). */ +static glitter_status_t +polygon_reset (struct polygon *polygon, + grid_scaled_y_t ymin, + grid_scaled_y_t ymax) +{ + unsigned h = ymax - ymin; + unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin); + + pool_reset(polygon->edge_pool.base); + + if (unlikely (h > 0x7FFFFFFFU - GRID_Y)) + goto bail_no_mem; /* even if you could, you wouldn't want to. */ + + if (polygon->y_buckets != polygon->y_buckets_embedded) + free (polygon->y_buckets); + + polygon->y_buckets = polygon->y_buckets_embedded; + if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) { + polygon->y_buckets = _cairo_malloc_ab (num_buckets, + sizeof (struct edge *)); + if (unlikely (NULL == polygon->y_buckets)) + goto bail_no_mem; + } + memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *)); + + polygon->ymin = ymin; + polygon->ymax = ymax; + return GLITTER_STATUS_SUCCESS; + +bail_no_mem: + polygon->ymin = 0; + polygon->ymax = 0; + return GLITTER_STATUS_NO_MEMORY; +} + +static void +_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon, + struct edge *e) +{ + unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin); + struct edge **ptail = &polygon->y_buckets[ix]; + e->next = *ptail; + *ptail = e; +} + +inline static void +polygon_add_edge (struct polygon *polygon, + const cairo_edge_t *edge) +{ + struct edge *e; + grid_scaled_x_t dx; + grid_scaled_y_t dy; + grid_scaled_y_t ytop, ybot; + grid_scaled_y_t ymin = polygon->ymin; + grid_scaled_y_t ymax = polygon->ymax; + + if (unlikely (edge->top >= ymax || edge->bottom <= ymin)) + return; + + e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge)); + + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + e->dy = dy; + e->dir = edge->dir; + + ytop = edge->top >= ymin ? edge->top : ymin; + ybot = edge->bottom <= ymax ? edge->bottom : ymax; + e->ytop = ytop; + e->height_left = ybot - ytop; + + if (dx == 0) { + e->vertical = TRUE; + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + e->dxdy.quo = 0; + e->dxdy.rem = 0; + } else { + e->vertical = FALSE; + e->dxdy = floored_divrem (dx, dy); + if (ytop == edge->line.p1.y) { + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + } else { + e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy); + e->x.quo += edge->line.p1.x; + } + } + + _polygon_insert_edge_into_its_y_bucket (polygon, e); + + e->x.rem -= dy; /* Bias the remainder for faster + * edge advancement. */ +} + +static void +active_list_reset (struct active_list *active) +{ + active->head.vertical = 1; + active->head.height_left = INT_MAX; + active->head.x.quo = INT_MIN; + active->head.prev = NULL; + active->head.next = &active->tail; + active->tail.prev = &active->head; + active->tail.next = NULL; + active->tail.x.quo = INT_MAX; + active->tail.height_left = INT_MAX; + active->tail.vertical = 1; + active->min_height = 0; + active->is_vertical = 1; +} + +static void +active_list_init(struct active_list *active) +{ + active_list_reset(active); +} + +/* + * Merge two sorted edge lists. + * Input: + * - head_a: The head of the first list. + * - head_b: The head of the second list; head_b cannot be NULL. + * Output: + * Returns the head of the merged list. + * + * Implementation notes: + * To make it fast (in particular, to reduce to an insertion sort whenever + * one of the two input lists only has a single element) we iterate through + * a list until its head becomes greater than the head of the other list, + * then we switch their roles. As soon as one of the two lists is empty, we + * just attach the other one to the current list and exit. + * Writes to memory are only needed to "switch" lists (as it also requires + * attaching to the output list the list which we will be iterating next) and + * to attach the last non-empty list. + */ +static struct edge * +merge_sorted_edges (struct edge *head_a, struct edge *head_b) +{ + struct edge *head, **next, *prev; + int32_t x; + + prev = head_a->prev; + next = &head; + if (head_a->x.quo <= head_b->x.quo) { + head = head_a; + } else { + head = head_b; + head_b->prev = prev; + goto start_with_b; + } + + do { + x = head_b->x.quo; + while (head_a != NULL && head_a->x.quo <= x) { + prev = head_a; + next = &head_a->next; + head_a = head_a->next; + } + + head_b->prev = prev; + *next = head_b; + if (head_a == NULL) + return head; + +start_with_b: + x = head_a->x.quo; + while (head_b != NULL && head_b->x.quo <= x) { + prev = head_b; + next = &head_b->next; + head_b = head_b->next; + } + + head_a->prev = prev; + *next = head_a; + if (head_b == NULL) + return head; + } while (1); +} + +/* + * Sort (part of) a list. + * Input: + * - list: The list to be sorted; list cannot be NULL. + * - limit: Recursion limit. + * Output: + * - head_out: The head of the sorted list containing the first 2^(level+1) elements of the + * input list; if the input list has fewer elements, head_out be a sorted list + * containing all the elements of the input list. + * Returns the head of the list of unprocessed elements (NULL if the sorted list contains + * all the elements of the input list). + * + * Implementation notes: + * Special case single element list, unroll/inline the sorting of the first two elements. + * Some tail recursion is used since we iterate on the bottom-up solution of the problem + * (we start with a small sorted list and keep merging other lists of the same size to it). + */ +static struct edge * +sort_edges (struct edge *list, + unsigned int level, + struct edge **head_out) +{ + struct edge *head_other, *remaining; + unsigned int i; + + head_other = list->next; + + if (head_other == NULL) { + *head_out = list; + return NULL; + } + + remaining = head_other->next; + if (list->x.quo <= head_other->x.quo) { + *head_out = list; + head_other->next = NULL; + } else { + *head_out = head_other; + head_other->prev = list->prev; + head_other->next = list; + list->prev = head_other; + list->next = NULL; + } + + for (i = 0; i < level && remaining; i++) { + remaining = sort_edges (remaining, i, &head_other); + *head_out = merge_sorted_edges (*head_out, head_other); + } + + return remaining; +} + +static struct edge * +merge_unsorted_edges (struct edge *head, struct edge *unsorted) +{ + sort_edges (unsorted, UINT_MAX, &unsorted); + return merge_sorted_edges (head, unsorted); +} + +/* Test if the edges on the active list can be safely advanced by a + * full row without intersections or any edges ending. */ +inline static int +can_do_full_row (struct active_list *active) +{ + const struct edge *e; + + /* Recomputes the minimum height of all edges on the active + * list if we have been dropping edges. */ + if (active->min_height <= 0) { + int min_height = INT_MAX; + int is_vertical = 1; + + e = active->head.next; + while (NULL != e) { + if (e->height_left < min_height) + min_height = e->height_left; + is_vertical &= e->vertical; + e = e->next; + } + + active->is_vertical = is_vertical; + active->min_height = min_height; + } + + if (active->min_height < GRID_Y) + return 0; + + return active->is_vertical; +} + +/* Merges edges on the given subpixel row from the polygon to the + * active_list. */ +inline static void +active_list_merge_edges_from_bucket(struct active_list *active, + struct edge *edges) +{ + active->head.next = merge_unsorted_edges (active->head.next, edges); +} + +inline static void +polygon_fill_buckets (struct active_list *active, + struct edge *edge, + int y, + struct edge **buckets) +{ + grid_scaled_y_t min_height = active->min_height; + int is_vertical = active->is_vertical; + + while (edge) { + struct edge *next = edge->next; + int suby = edge->ytop - y; + if (buckets[suby]) + buckets[suby]->prev = edge; + edge->next = buckets[suby]; + edge->prev = NULL; + buckets[suby] = edge; + if (edge->height_left < min_height) + min_height = edge->height_left; + is_vertical &= edge->vertical; + edge = next; + } + + active->is_vertical = is_vertical; + active->min_height = min_height; +} + +inline static void +sub_row (struct active_list *active, + struct cell_list *coverages, + unsigned int mask) +{ + struct edge *edge = active->head.next; + int xstart = INT_MIN, prev_x = INT_MIN; + int winding = 0; + + cell_list_rewind (coverages); + + while (&active->tail != edge) { + struct edge *next = edge->next; + int xend = edge->x.quo; + + if (--edge->height_left) { + edge->x.quo += edge->dxdy.quo; + edge->x.rem += edge->dxdy.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } + + if (edge->x.quo < prev_x) { + struct edge *pos = edge->prev; + pos->next = next; + next->prev = pos; + do { + pos = pos->prev; + } while (edge->x.quo < pos->x.quo); + pos->next->prev = edge; + edge->next = pos->next; + edge->prev = pos; + pos->next = edge; + } else + prev_x = edge->x.quo; + } else { + edge->prev->next = next; + next->prev = edge->prev; + } + + winding += edge->dir; + if ((winding & mask) == 0) { + if (next->x.quo != xend) { + cell_list_add_subspan (coverages, xstart, xend); + xstart = INT_MIN; + } + } else if (xstart == INT_MIN) + xstart = xend; + + edge = next; + } +} + +inline static void dec (struct edge *e, int h) +{ + e->height_left -= h; + if (e->height_left == 0) { + e->prev->next = e->next; + e->next->prev = e->prev; + } +} + +static void +full_row (struct active_list *active, + struct cell_list *coverages, + unsigned int mask) +{ + struct edge *left = active->head.next; + + while (&active->tail != left) { + struct edge *right; + int winding; + + dec (left, GRID_Y); + + winding = left->dir; + right = left->next; + do { + dec (right, GRID_Y); + + winding += right->dir; + if ((winding & mask) == 0 && right->next->x.quo != right->x.quo) + break; + + right = right->next; + } while (1); + + cell_list_set_rewind (coverages); + cell_list_render_edge (coverages, left, +1); + cell_list_render_edge (coverages, right, -1); + + left = right->next; + } +} + +static void +_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp) +{ + polygon_init(converter->polygon, jmp); + active_list_init(converter->active); + cell_list_init(converter->coverages, jmp); + converter->xmin=0; + converter->ymin=0; + converter->xmax=0; + converter->ymax=0; +} + +static void +_glitter_scan_converter_fini(glitter_scan_converter_t *self) +{ + if (self->spans != self->spans_embedded) + free (self->spans); + + polygon_fini(self->polygon); + cell_list_fini(self->coverages); + + self->xmin=0; + self->ymin=0; + self->xmax=0; + self->ymax=0; +} + +static grid_scaled_t +int_to_grid_scaled(int i, int scale) +{ + /* Clamp to max/min representable scaled number. */ + if (i >= 0) { + if (i >= INT_MAX/scale) + i = INT_MAX/scale; + } + else { + if (i <= INT_MIN/scale) + i = INT_MIN/scale; + } + return i*scale; +} + +#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X) +#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y) + +I glitter_status_t +glitter_scan_converter_reset( + glitter_scan_converter_t *converter, + int xmin, int ymin, + int xmax, int ymax) +{ + glitter_status_t status; + int max_num_spans; + + converter->xmin = 0; converter->xmax = 0; + converter->ymin = 0; converter->ymax = 0; + + max_num_spans = xmax - xmin + 1; + + if (max_num_spans > ARRAY_LENGTH(converter->spans_embedded)) { + converter->spans = _cairo_malloc_ab (max_num_spans, + sizeof (cairo_half_open_span_t)); + if (unlikely (converter->spans == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + converter->spans = converter->spans_embedded; + + xmin = int_to_grid_scaled_x(xmin); + ymin = int_to_grid_scaled_y(ymin); + xmax = int_to_grid_scaled_x(xmax); + ymax = int_to_grid_scaled_y(ymax); + + active_list_reset(converter->active); + cell_list_reset(converter->coverages); + status = polygon_reset(converter->polygon, ymin, ymax); + if (status) + return status; + + converter->xmin = xmin; + converter->xmax = xmax; + converter->ymin = ymin; + converter->ymax = ymax; + return GLITTER_STATUS_SUCCESS; +} + +/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale) + * These macros convert an input coordinate in the client's + * device space to the rasterisation grid. + */ +/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use + * shifts if possible, and something saneish if not. + */ +#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS) +#else +# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y) +#endif + +#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS +# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS) +#else +# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X) +#endif + +#define INPUT_TO_GRID_general(in, out, grid_scale) do { \ + long long tmp__ = (long long)(grid_scale) * (in); \ + tmp__ >>= GLITTER_INPUT_BITS; \ + (out) = tmp__; \ +} while (0) + +/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan + * converter. The coordinates represent pixel positions scaled by + * 2**GLITTER_PIXEL_BITS. If this function fails then the scan + * converter should be reset or destroyed. Dir must be +1 or -1, + * with the latter reversing the orientation of the edge. */ +I void +glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, + const cairo_edge_t *edge) +{ + cairo_edge_t e; + + INPUT_TO_GRID_Y (edge->top, e.top); + INPUT_TO_GRID_Y (edge->bottom, e.bottom); + if (e.top >= e.bottom) + return; + + /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */ + INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y); + INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y); + if (e.line.p1.y == e.line.p2.y) + e.line.p2.y++; /* Fudge to prevent div-by-zero */ + + INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x); + INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x); + + e.dir = edge->dir; + + polygon_add_edge (converter->polygon, &e); +} + +static void +step_edges (struct active_list *active, int count) +{ + struct edge *edge; + + count *= GRID_Y; + for (edge = active->head.next; edge != &active->tail; edge = edge->next) { + edge->height_left -= count; + if (! edge->height_left) { + edge->prev->next = edge->next; + edge->next->prev = edge->prev; + } + } +} + +static glitter_status_t +blit_a8 (struct cell_list *cells, + cairo_span_renderer_t *renderer, + cairo_half_open_span_t *spans, + int y, int height, + int xmin, int xmax) +{ + struct cell *cell = cells->head.next; + int prev_x = xmin, last_x = -1; + int16_t cover = 0, last_cover = 0; + unsigned num_spans; + + if (cell == &cells->tail) + return CAIRO_STATUS_SUCCESS; + + /* Skip cells to the left of the clip region. */ + while (cell->x < xmin) { + cover += cell->covered_height; + cell = cell->next; + } + cover *= GRID_X*2; + + /* Form the spans from the coverages and areas. */ + num_spans = 0; + for (; cell->x < xmax; cell = cell->next) { + int x = cell->x; + int16_t area; + + if (x > prev_x && cover != last_cover) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + last_cover = cover; + last_x = prev_x; + ++num_spans; + } + + cover += cell->covered_height*GRID_X*2; + area = cover - cell->uncovered_area; + + if (area != last_cover) { + spans[num_spans].x = x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area); + last_cover = area; + last_x = x; + ++num_spans; + } + + prev_x = x+1; + } + + if (prev_x <= xmax && cover != last_cover) { + spans[num_spans].x = prev_x; + spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover); + last_cover = cover; + last_x = prev_x; + ++num_spans; + } + + if (last_x < xmax && last_cover) { + spans[num_spans].x = xmax; + spans[num_spans].coverage = 0; + ++num_spans; + } + + /* Dump them into the renderer. */ + return renderer->render_rows (renderer, y, height, spans, num_spans); +} + +#define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0) +static glitter_status_t +blit_a1 (struct cell_list *cells, + cairo_span_renderer_t *renderer, + cairo_half_open_span_t *spans, + int y, int height, + int xmin, int xmax) +{ + struct cell *cell = cells->head.next; + int prev_x = xmin, last_x = -1; + int16_t cover = 0; + uint8_t coverage, last_cover = 0; + unsigned num_spans; + + if (cell == &cells->tail) + return CAIRO_STATUS_SUCCESS; + + /* Skip cells to the left of the clip region. */ + while (cell->x < xmin) { + cover += cell->covered_height; + cell = cell->next; + } + cover *= GRID_X*2; + + /* Form the spans from the coverages and areas. */ + num_spans = 0; + for (; cell->x < xmax; cell = cell->next) { + int x = cell->x; + int16_t area; + + coverage = GRID_AREA_TO_A1 (cover); + if (x > prev_x && coverage != last_cover) { + last_x = spans[num_spans].x = prev_x; + last_cover = spans[num_spans].coverage = coverage; + ++num_spans; + } + + cover += cell->covered_height*GRID_X*2; + area = cover - cell->uncovered_area; + + coverage = GRID_AREA_TO_A1 (area); + if (coverage != last_cover) { + last_x = spans[num_spans].x = x; + last_cover = spans[num_spans].coverage = coverage; + ++num_spans; + } + + prev_x = x+1; + } + + coverage = GRID_AREA_TO_A1 (cover); + if (prev_x <= xmax && coverage != last_cover) { + last_x = spans[num_spans].x = prev_x; + last_cover = spans[num_spans].coverage = coverage; + ++num_spans; + } + + if (last_x < xmax && last_cover) { + spans[num_spans].x = xmax; + spans[num_spans].coverage = 0; + ++num_spans; + } + if (num_spans == 1) + return CAIRO_STATUS_SUCCESS; + + /* Dump them into the renderer. */ + return renderer->render_rows (renderer, y, height, spans, num_spans); +} + + +I void +glitter_scan_converter_render(glitter_scan_converter_t *converter, + unsigned int winding_mask, + int antialias, + cairo_span_renderer_t *renderer) +{ + int i, j; + int ymax_i = converter->ymax / GRID_Y; + int ymin_i = converter->ymin / GRID_Y; + int xmin_i, xmax_i; + int h = ymax_i - ymin_i; + struct polygon *polygon = converter->polygon; + struct cell_list *coverages = converter->coverages; + struct active_list *active = converter->active; + struct edge *buckets[GRID_Y] = { 0 }; + + xmin_i = converter->xmin / GRID_X; + xmax_i = converter->xmax / GRID_X; + if (xmin_i >= xmax_i) + return; + + /* Render each pixel row. */ + for (i = 0; i < h; i = j) { + int do_full_row = 0; + + j = i + 1; + + /* Determine if we can ignore this row or use the full pixel + * stepper. */ + if (! polygon->y_buckets[i]) { + if (active->head.next == &active->tail) { + active->min_height = INT_MAX; + active->is_vertical = 1; + for (; j < h && ! polygon->y_buckets[j]; j++) + ; + continue; + } + + do_full_row = can_do_full_row (active); + } + + if (do_full_row) { + /* Step by a full pixel row's worth. */ + full_row (active, coverages, winding_mask); + + if (active->is_vertical) { + while (j < h && + polygon->y_buckets[j] == NULL && + active->min_height >= 2*GRID_Y) + { + active->min_height -= GRID_Y; + j++; + } + if (j != i + 1) + step_edges (active, j - (i + 1)); + } + } else { + int sub; + + polygon_fill_buckets (active, + polygon->y_buckets[i], + (i+ymin_i)*GRID_Y, + buckets); + + /* Subsample this row. */ + for (sub = 0; sub < GRID_Y; sub++) { + if (buckets[sub]) { + active_list_merge_edges_from_bucket (active, buckets[sub]); + buckets[sub] = NULL; + } + + sub_row (active, coverages, winding_mask); + } + } + + if (antialias) + blit_a8 (coverages, renderer, converter->spans, + i+ymin_i, j-i, xmin_i, xmax_i); + else + blit_a1 (coverages, renderer, converter->spans, + i+ymin_i, j-i, xmin_i, xmax_i); + cell_list_reset (coverages); + + active->min_height -= GRID_Y; + } +} + +struct _cairo_tor22_scan_converter { + cairo_scan_converter_t base; + + glitter_scan_converter_t converter[1]; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + + jmp_buf jmp; +}; + +typedef struct _cairo_tor22_scan_converter cairo_tor22_scan_converter_t; + +static void +_cairo_tor22_scan_converter_destroy (void *converter) +{ + cairo_tor22_scan_converter_t *self = converter; + if (self == NULL) { + return; + } + _glitter_scan_converter_fini (self->converter); + free(self); +} + +cairo_status_t +_cairo_tor22_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon) +{ + cairo_tor22_scan_converter_t *self = converter; + int i; + +#if 0 + FILE *file = fopen ("polygon.txt", "w"); + _cairo_debug_print_polygon (file, polygon); + fclose (file); +#endif + + for (i = 0; i < polygon->num_edges; i++) + glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tor22_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_tor22_scan_converter_t *self = converter; + cairo_status_t status; + + if ((status = setjmp (self->jmp))) + return _cairo_scan_converter_set_error (self, _cairo_error (status)); + + glitter_scan_converter_render (self->converter, + self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1, + self->antialias != CAIRO_ANTIALIAS_NONE, + renderer); + return CAIRO_STATUS_SUCCESS; +} + +cairo_scan_converter_t * +_cairo_tor22_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) +{ + cairo_tor22_scan_converter_t *self; + cairo_status_t status; + + self = _cairo_malloc (sizeof(struct _cairo_tor22_scan_converter)); + if (unlikely (self == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto bail_nomem; + } + + self->base.destroy = _cairo_tor22_scan_converter_destroy; + self->base.generate = _cairo_tor22_scan_converter_generate; + + _glitter_scan_converter_init (self->converter, &self->jmp); + status = glitter_scan_converter_reset (self->converter, + xmin, ymin, xmax, ymax); + if (unlikely (status)) + goto bail; + + self->fill_rule = fill_rule; + self->antialias = antialias; + + return &self->base; + + bail: + self->base.destroy(&self->base); + bail_nomem: + return _cairo_scan_converter_create_in_error (status); +} diff --git a/gfx/cairo/cairo/src/cairo-toy-font-face.c b/gfx/cairo/cairo/src/cairo-toy-font-face.c index 4c690da534c1..f51dab5ab767 100644 --- a/gfx/cairo/cairo/src/cairo-toy-font-face.c +++ b/gfx/cairo/cairo/src/cairo-toy-font-face.c @@ -39,7 +39,7 @@ * Behdad Esfahbod */ -#define _BSD_SOURCE /* for strdup() */ +#define _DEFAULT_SOURCE /* for strdup() */ #include "cairoint.h" #include "cairo-error-private.h" @@ -148,7 +148,6 @@ _cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, hash += ((unsigned long) slant) * 1607; hash += ((unsigned long) weight) * 1451; - assert (hash != 0); key->base.hash_entry.hash = hash; } @@ -209,7 +208,7 @@ static void _cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) { /* We assert here that we own font_face->family before casting - * away the const qualifer. */ + * away the const qualifier. */ assert (font_face->owns_family); free ((char*) font_face->family); @@ -303,20 +302,17 @@ cairo_toy_font_face_create (const char *family, &key.base.hash_entry); if (font_face != NULL) { if (font_face->base.status == CAIRO_STATUS_SUCCESS) { - /* We increment the reference count here manually to avoid - double-locking. */ - _cairo_reference_count_inc (&font_face->base.ref_count); + cairo_font_face_reference (&font_face->base); _cairo_toy_font_face_hash_table_unlock (); return &font_face->base; } /* remove the bad font from the hash table */ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); - font_face->base.hash_entry.hash = 0; } /* Otherwise create it and insert into hash table. */ - font_face = malloc (sizeof (cairo_toy_font_face_t)); + font_face = _cairo_malloc (sizeof (cairo_toy_font_face_t)); if (unlikely (font_face == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNWIND_HASH_TABLE_LOCK; @@ -346,32 +342,34 @@ cairo_toy_font_face_create (const char *family, } slim_hidden_def (cairo_toy_font_face_create); -static void +static cairo_bool_t _cairo_toy_font_face_destroy (void *abstract_face) { cairo_toy_font_face_t *font_face = abstract_face; cairo_hash_table_t *hash_table; - if (font_face == NULL || - CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count)) - return; - hash_table = _cairo_toy_font_face_hash_table_lock (); /* All created objects must have been mapped in the hash table. */ assert (hash_table != NULL); - if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { + if (! _cairo_reference_count_dec_and_test (&font_face->base.ref_count)) { /* somebody recreated the font whilst we waited for the lock */ _cairo_toy_font_face_hash_table_unlock (); - return; + return FALSE; } - if (font_face->base.hash_entry.hash != 0) + /* Font faces in SUCCESS status are guaranteed to be in the + * hashtable. Font faces in an error status are removed from the + * hashtable if they are found during a lookup, thus they should + * only be removed if they are in the hashtable. */ + if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) || + _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face) _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); _cairo_toy_font_face_hash_table_unlock (); _cairo_toy_font_face_fini (font_face); + return TRUE; } static cairo_status_t @@ -422,7 +420,7 @@ _cairo_font_face_is_toy (cairo_font_face_t *font_face) * cairo_toy_font_face_get_family: * @font_face: A toy font face * - * Gets the familly name of a toy font. + * Gets the family name of a toy font. * * Return value: The family name. This string is owned by the font face * and remains valid as long as the font face is alive (referenced). diff --git a/gfx/cairo/cairo/src/cairo-traps-compositor.c b/gfx/cairo/cairo/src/cairo-traps-compositor.c new file mode 100644 index 000000000000..3414fc2684a1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-traps-compositor.c @@ -0,0 +1,2351 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-box-inline.h" +#include "cairo-boxes-private.h" +#include "cairo-clip-inline.h" +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-compositor-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-inline.h" +#include "cairo-paginated-private.h" +#include "cairo-recording-surface-inline.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-observer-private.h" +#include "cairo-region-private.h" +#include "cairo-spans-private.h" +#include "cairo-traps-private.h" +#include "cairo-tristrip-private.h" + +typedef cairo_int_status_t +(*draw_func_t) (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip); + +static void do_unaligned_row(void (*blt)(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage), + void *closure, + const cairo_box_t *b, + int tx, int y, int h, + uint16_t coverage) +{ + int x1 = _cairo_fixed_integer_part (b->p1.x) - tx; + int x2 = _cairo_fixed_integer_part (b->p2.x) - tx; + if (x2 > x1) { + if (! _cairo_fixed_is_integer (b->p1.x)) { + blt(closure, x1, y, 1, h, + coverage * (256 - _cairo_fixed_fractional_part (b->p1.x))); + x1++; + } + + if (x2 > x1) + blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8)); + + if (! _cairo_fixed_is_integer (b->p2.x)) + blt(closure, x2, y, 1, h, + coverage * _cairo_fixed_fractional_part (b->p2.x)); + } else + blt(closure, x1, y, 1, h, + coverage * (b->p2.x - b->p1.x)); +} + +static void do_unaligned_box(void (*blt)(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage), + void *closure, + const cairo_box_t *b, int tx, int ty) +{ + int y1 = _cairo_fixed_integer_part (b->p1.y) - ty; + int y2 = _cairo_fixed_integer_part (b->p2.y) - ty; + if (y2 > y1) { + if (! _cairo_fixed_is_integer (b->p1.y)) { + do_unaligned_row(blt, closure, b, tx, y1, 1, + 256 - _cairo_fixed_fractional_part (b->p1.y)); + y1++; + } + + if (y2 > y1) + do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256); + + if (! _cairo_fixed_is_integer (b->p2.y)) + do_unaligned_row(blt, closure, b, tx, y2, 1, + _cairo_fixed_fractional_part (b->p2.y)); + } else + do_unaligned_row(blt, closure, b, tx, y1, 1, + b->p2.y - b->p1.y); +} + +struct blt_in { + const cairo_traps_compositor_t *compositor; + cairo_surface_t *dst; + cairo_boxes_t boxes; +}; + +static void blt_in(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct blt_in *info = closure; + cairo_color_t color; + + if (CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) + return; + + _cairo_box_from_integers (&info->boxes.chunks.base[0], x, y, w, h); + + _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff); + info->compositor->fill_boxes (info->dst, + CAIRO_OPERATOR_IN, &color, + &info->boxes); +} + +static void +add_rect_with_offset (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2, int dx, int dy) +{ + cairo_box_t box; + cairo_int_status_t status; + + box.p1.x = _cairo_fixed_from_int (x1 - dx); + box.p1.y = _cairo_fixed_from_int (y1 - dy); + box.p2.x = _cairo_fixed_from_int (x2 - dx); + box.p2.y = _cairo_fixed_from_int (y2 - dy); + + status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_INT_STATUS_SUCCESS); +} + +static cairo_int_status_t +combine_clip_as_traps (const cairo_traps_compositor_t *compositor, + cairo_surface_t *mask, + const cairo_clip_t *clip, + const cairo_rectangle_int_t *extents) +{ + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_traps_t traps; + cairo_surface_t *src; + cairo_box_t box; + cairo_rectangle_int_t fixup; + int src_x, src_y; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &antialias); + if (status) + return status; + + _cairo_traps_init (&traps); + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + fill_rule); + _cairo_polygon_fini (&polygon); + if (unlikely (status)) + return status; + + src = compositor->pattern_to_surface (mask, NULL, FALSE, + extents, NULL, + &src_x, &src_y); + if (unlikely (src->status)) { + _cairo_traps_fini (&traps); + return src->status; + } + + status = compositor->composite_traps (mask, CAIRO_OPERATOR_IN, src, + src_x, src_y, + extents->x, extents->y, + extents, + antialias, &traps); + + _cairo_traps_extents (&traps, &box); + _cairo_box_round_to_rectangle (&box, &fixup); + _cairo_traps_fini (&traps); + cairo_surface_destroy (src); + + if (unlikely (status)) + return status; + + if (! _cairo_rectangle_intersect (&fixup, extents)) + return CAIRO_STATUS_SUCCESS; + + if (fixup.width < extents->width || fixup.height < extents->height) { + cairo_boxes_t clear; + + _cairo_boxes_init (&clear); + + /* top */ + if (fixup.y != extents->y) { + add_rect_with_offset (&clear, + extents->x, extents->y, + extents->x + extents->width, + fixup.y, + extents->x, extents->y); + } + /* left */ + if (fixup.x != extents->x) { + add_rect_with_offset (&clear, + extents->x, fixup.y, + fixup.x, + fixup.y + fixup.height, + extents->x, extents->y); + } + /* right */ + if (fixup.x + fixup.width != extents->x + extents->width) { + add_rect_with_offset (&clear, + fixup.x + fixup.width, + fixup.y, + extents->x + extents->width, + fixup.y + fixup.height, + extents->x, extents->y); + } + /* bottom */ + if (fixup.y + fixup.height != extents->y + extents->height) { + add_rect_with_offset (&clear, + extents->x, + fixup.y + fixup.height, + extents->x + extents->width, + extents->y + extents->height, + extents->x, extents->y); + } + + status = compositor->fill_boxes (mask, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + + _cairo_boxes_fini (&clear); + } + + return status; +} + +static cairo_status_t +__clip_to_surface (const cairo_traps_compositor_t *compositor, + const cairo_composite_rectangles_t *composite, + const cairo_rectangle_int_t *extents, + cairo_surface_t **surface) +{ + cairo_surface_t *mask; + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_traps_t traps; + cairo_boxes_t clear; + cairo_surface_t *src; + int src_x, src_y; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = _cairo_clip_get_polygon (composite->clip, &polygon, + &fill_rule, &antialias); + if (status) + return status; + + _cairo_traps_init (&traps); + status = _cairo_bentley_ottmann_tessellate_polygon (&traps, + &polygon, + fill_rule); + _cairo_polygon_fini (&polygon); + if (unlikely (status)) + return status; + + mask = _cairo_surface_create_scratch (composite->surface, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + NULL); + if (unlikely (mask->status)) { + _cairo_traps_fini (&traps); + return status; + } + + src = compositor->pattern_to_surface (mask, NULL, FALSE, + extents, NULL, + &src_x, &src_y); + if (unlikely (status = src->status)) + goto error; + + status = compositor->acquire (mask); + if (unlikely (status)) + goto error; + + _cairo_boxes_init_from_rectangle (&clear, + 0, 0, + extents->width, + extents->height); + status = compositor->fill_boxes (mask, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + if (unlikely (status)) + goto error_release; + + status = compositor->composite_traps (mask, CAIRO_OPERATOR_ADD, src, + src_x, src_y, + extents->x, extents->y, + extents, + antialias, &traps); + if (unlikely (status)) + goto error_release; + + compositor->release (mask); + *surface = mask; +out: + cairo_surface_destroy (src); + _cairo_traps_fini (&traps); + return status; + +error_release: + compositor->release (mask); +error: + cairo_surface_destroy (mask); + goto out; +} + +static cairo_surface_t * +traps_get_clip_surface (const cairo_traps_compositor_t *compositor, + const cairo_composite_rectangles_t *composite, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *surface = NULL; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = __clip_to_surface (compositor, composite, extents, &surface); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + surface = _cairo_surface_create_scratch (composite->surface, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_WHITE); + if (unlikely (surface->status)) + return surface; + + status = _cairo_clip_combine_with_surface (composite->clip, surface, + extents->x, extents->y); + } + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + return surface; +} + +static void blt_unaligned_boxes(const cairo_traps_compositor_t *compositor, + cairo_surface_t *surface, + int dx, int dy, + cairo_box_t *boxes, + int num_boxes) +{ + struct blt_in info; + int i; + + info.compositor = compositor; + info.dst = surface; + _cairo_boxes_init (&info.boxes); + info.boxes.num_boxes = 1; + for (i = 0; i < num_boxes; i++) { + cairo_box_t *b = &boxes[i]; + + if (! _cairo_fixed_is_integer (b->p1.x) || + ! _cairo_fixed_is_integer (b->p1.y) || + ! _cairo_fixed_is_integer (b->p2.x) || + ! _cairo_fixed_is_integer (b->p2.y)) + { + do_unaligned_box(blt_in, &info, b, dx, dy); + } + } +} + +static cairo_surface_t * +create_composite_mask (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *draw_closure, + draw_func_t draw_func, + draw_func_t mask_func, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *surface, *src; + cairo_int_status_t status; + int src_x, src_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + surface = _cairo_surface_create_scratch (dst, CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (surface->status)) + return surface; + + src = compositor->pattern_to_surface (surface, + &_cairo_pattern_white.base, + FALSE, + &extents->bounded, + &extents->bounded, + &src_x, &src_y); + if (unlikely (src->status)) { + cairo_surface_destroy (surface); + return src; + } + + status = compositor->acquire (surface); + if (unlikely (status)) { + cairo_surface_destroy (src); + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + + if (!surface->is_clear) { + cairo_boxes_t clear; + + _cairo_boxes_init_from_rectangle (&clear, + 0, 0, + extents->bounded.width, + extents->bounded.height); + status = compositor->fill_boxes (surface, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + if (unlikely (status)) + goto error; + + surface->is_clear = TRUE; + } + + if (mask_func) { + status = mask_func (compositor, surface, draw_closure, + CAIRO_OPERATOR_SOURCE, src, src_x, src_y, + extents->bounded.x, extents->bounded.y, + &extents->bounded, extents->clip); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + surface->is_clear = FALSE; + goto out; + } + if (unlikely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + goto error; + } + + /* Is it worth setting the clip region here? */ + status = draw_func (compositor, surface, draw_closure, + CAIRO_OPERATOR_ADD, src, src_x, src_y, + extents->bounded.x, extents->bounded.y, + &extents->bounded, NULL); + if (unlikely (status)) + goto error; + + surface->is_clear = FALSE; + if (extents->clip->path != NULL) { + status = combine_clip_as_traps (compositor, surface, + extents->clip, &extents->bounded); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_clip_combine_with_surface (extents->clip, surface, + extents->bounded.x, + extents->bounded.y); + } + if (unlikely (status)) + goto error; + } else if (extents->clip->boxes) { + blt_unaligned_boxes(compositor, surface, + extents->bounded.x, extents->bounded.y, + extents->clip->boxes, extents->clip->num_boxes); + + } + +out: + compositor->release (surface); + cairo_surface_destroy (src); + return surface; + +error: + compositor->release (surface); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + cairo_surface_destroy (src); + return surface; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +clip_and_composite_with_mask (const cairo_traps_compositor_t *compositor, + const cairo_composite_rectangles_t*extents, + draw_func_t draw_func, + draw_func_t mask_func, + void *draw_closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, int src_y) +{ + cairo_surface_t *dst = extents->surface; + cairo_surface_t *mask; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + mask = create_composite_mask (compositor, dst, draw_closure, + draw_func, mask_func, + extents); + if (unlikely (mask->status)) + return mask->status; + + if (mask->is_clear) + goto skip; + + if (src != NULL || dst->content != CAIRO_CONTENT_ALPHA) { + compositor->composite (dst, op, src, mask, + extents->bounded.x + src_x, + extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } else { + compositor->composite (dst, op, mask, NULL, + 0, 0, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } + +skip: + cairo_surface_destroy (mask); + return CAIRO_STATUS_SUCCESS; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +clip_and_composite_combine (const cairo_traps_compositor_t *compositor, + const cairo_composite_rectangles_t*extents, + draw_func_t draw_func, + void *draw_closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, int src_y) +{ + cairo_surface_t *dst = extents->surface; + cairo_surface_t *tmp, *clip; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + tmp = _cairo_surface_create_scratch (dst, dst->content, + extents->bounded.width, + extents->bounded.height, + NULL); + if (unlikely (tmp->status)) + return tmp->status; + + status = compositor->acquire (tmp); + if (unlikely (status)) { + cairo_surface_destroy (tmp); + return status; + } + + compositor->composite (tmp, + dst->is_clear ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE, + dst, NULL, + extents->bounded.x, extents->bounded.y, + 0, 0, + 0, 0, + extents->bounded.width, extents->bounded.height); + + status = draw_func (compositor, tmp, draw_closure, op, + src, src_x, src_y, + extents->bounded.x, extents->bounded.y, + &extents->bounded, NULL); + + if (unlikely (status)) + goto cleanup; + + clip = traps_get_clip_surface (compositor, extents, &extents->bounded); + if (unlikely ((status = clip->status))) + goto cleanup; + + if (dst->is_clear) { + compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip, + 0, 0, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } else { + compositor->lerp (dst, tmp, clip, + 0, 0, + 0,0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } + cairo_surface_destroy (clip); + +cleanup: + compositor->release (tmp); + cairo_surface_destroy (tmp); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +clip_and_composite_source (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + draw_func_t draw_func, + draw_func_t mask_func, + void *draw_closure, + cairo_surface_t *src, + int src_x, + int src_y, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *mask; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* Create a surface that is mask IN clip */ + mask = create_composite_mask (compositor, dst, draw_closure, + draw_func, mask_func, + extents); + if (unlikely (mask->status)) + return mask->status; + + if (mask->is_clear) + goto skip; + + if (dst->is_clear) { + compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } else { + compositor->lerp (dst, src, mask, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + } + +skip: + cairo_surface_destroy (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +can_reduce_alpha_op (cairo_operator_t op) +{ + int iop = op; + switch (iop) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + return TRUE; + default: + return FALSE; + } +} + +static cairo_bool_t +reduce_alpha_op (cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *dst = extents->surface; + cairo_operator_t op = extents->op; + const cairo_pattern_t *pattern = &extents->source_pattern.base; + return dst->is_clear && + dst->content == CAIRO_CONTENT_ALPHA && + _cairo_pattern_is_opaque_solid (pattern) && + can_reduce_alpha_op (op); +} + +static cairo_status_t +fixup_unbounded_with_mask (const cairo_traps_compositor_t *compositor, + const cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *dst = extents->surface; + cairo_surface_t *mask; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* XXX can we avoid querying the clip surface again? */ + mask = traps_get_clip_surface (compositor, extents, &extents->unbounded); + if (unlikely (mask->status)) + return mask->status; + + /* top */ + if (extents->bounded.y != extents->unbounded.y) { + int x = extents->unbounded.x; + int y = extents->unbounded.y; + int width = extents->unbounded.width; + int height = extents->bounded.y - y; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + 0, 0, + 0, 0, + x, y, + width, height); + } + + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + int x = extents->unbounded.x; + int y = extents->bounded.y; + int width = extents->bounded.x - x; + int height = extents->bounded.height; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + 0, y - extents->unbounded.y, + 0, 0, + x, y, + width, height); + } + + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + int x = extents->bounded.x + extents->bounded.width; + int y = extents->bounded.y; + int width = extents->unbounded.x + extents->unbounded.width - x; + int height = extents->bounded.height; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + x - extents->unbounded.x, y - extents->unbounded.y, + 0, 0, + x, y, + width, height); + } + + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + int x = extents->unbounded.x; + int y = extents->bounded.y + extents->bounded.height; + int width = extents->unbounded.width; + int height = extents->unbounded.y + extents->unbounded.height - y; + + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL, + 0, y - extents->unbounded.y, + 0, 0, + x, y, + width, height); + } + + cairo_surface_destroy (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static void +add_rect (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2) +{ + cairo_box_t box; + cairo_int_status_t status; + + box.p1.x = _cairo_fixed_from_int (x1); + box.p1.y = _cairo_fixed_from_int (y1); + box.p2.x = _cairo_fixed_from_int (x2); + box.p2.y = _cairo_fixed_from_int (y2); + + status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_INT_STATUS_SUCCESS); +} + +static cairo_status_t +fixup_unbounded (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + cairo_boxes_t clear, tmp; + cairo_box_t box; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (extents->bounded.width == extents->unbounded.width && + extents->bounded.height == extents->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + + assert (extents->clip->path == NULL); + + /* subtract the drawn boxes from the unbounded area */ + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (boxes == NULL) { + if (extents->bounded.width == 0 || extents->bounded.height == 0) { + goto empty; + } else { + /* top */ + if (extents->bounded.y != extents->unbounded.y) { + add_rect (&clear, + extents->unbounded.x, extents->unbounded.y, + extents->unbounded.x + extents->unbounded.width, + extents->bounded.y); + } + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + add_rect (&clear, + extents->unbounded.x, extents->bounded.y, + extents->bounded.x, + extents->bounded.y + extents->bounded.height); + } + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + add_rect (&clear, + extents->bounded.x + extents->bounded.width, + extents->bounded.y, + extents->unbounded.x + extents->unbounded.width, + extents->bounded.y + extents->bounded.height); + } + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + add_rect (&clear, + extents->unbounded.x, + extents->bounded.y + extents->bounded.height, + extents->unbounded.x + extents->unbounded.width, + extents->unbounded.y + extents->unbounded.height); + } + } + } else if (boxes->num_boxes) { + _cairo_boxes_init (&tmp); + + assert (boxes->is_pixel_aligned); + + status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + tmp.chunks.next = NULL; + if (unlikely (status)) + goto error; + } else { +empty: + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + + status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_INT_STATUS_SUCCESS); + } + + /* Now intersect with the clip boxes */ + if (extents->clip->num_boxes) { + _cairo_boxes_init_for_array (&tmp, + extents->clip->boxes, + extents->clip->num_boxes); + status = _cairo_boxes_intersect (&clear, &tmp, &clear); + if (unlikely (status)) + goto error; + } + + status = compositor->fill_boxes (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + +error: + _cairo_boxes_fini (&clear); + return status; +} + +enum { + NEED_CLIP_REGION = 0x1, + NEED_CLIP_SURFACE = 0x2, + FORCE_CLIP_REGION = 0x4, +}; + +static cairo_bool_t +need_bounded_clip (cairo_composite_rectangles_t *extents) +{ + unsigned int flags = 0; + + if (extents->clip->num_boxes > 1 || + extents->mask.width > extents->unbounded.width || + extents->mask.height > extents->unbounded.height) + { + flags |= NEED_CLIP_REGION; + } + + if (extents->clip->num_boxes > 1 || + extents->mask.width > extents->bounded.width || + extents->mask.height > extents->bounded.height) + { + flags |= FORCE_CLIP_REGION; + } + + if (! _cairo_clip_is_region (extents->clip)) + flags |= NEED_CLIP_SURFACE; + + return flags; +} + +static cairo_bool_t +need_unbounded_clip (cairo_composite_rectangles_t *extents) +{ + unsigned int flags = 0; + if (! extents->is_bounded) { + flags |= NEED_CLIP_REGION; + if (! _cairo_clip_is_region (extents->clip)) + flags |= NEED_CLIP_SURFACE; + } + if (extents->clip->path != NULL) + flags |= NEED_CLIP_SURFACE; + return flags; +} + +static cairo_status_t +clip_and_composite (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + draw_func_t draw_func, + draw_func_t mask_func, + void *draw_closure, + unsigned int need_clip) +{ + cairo_surface_t *dst = extents->surface; + cairo_operator_t op = extents->op; + cairo_pattern_t *source = &extents->source_pattern.base; + cairo_surface_t *src; + int src_x, src_y; + cairo_region_t *clip_region = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (reduce_alpha_op (extents)) { + op = CAIRO_OPERATOR_ADD; + source = NULL; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + source = NULL; + } + + compositor->acquire (dst); + + if (need_clip & NEED_CLIP_REGION) { + const cairo_rectangle_int_t *limit; + + if ((need_clip & FORCE_CLIP_REGION) == 0) + limit = &extents->unbounded; + else + limit = &extents->destination; + + clip_region = _cairo_clip_get_region (extents->clip); + if (clip_region != NULL && + cairo_region_contains_rectangle (clip_region, + limit) == CAIRO_REGION_OVERLAP_IN) + clip_region = NULL; + + if (clip_region != NULL) { + status = compositor->set_clip_region (dst, clip_region); + if (unlikely (status)) { + compositor->release (dst); + return status; + } + } + } + + if (extents->bounded.width == 0 || extents->bounded.height == 0) + goto skip; + + src = compositor->pattern_to_surface (dst, source, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (unlikely (status = src->status)) + goto error; + + if (op == CAIRO_OPERATOR_SOURCE) { + status = clip_and_composite_source (compositor, dst, + draw_func, mask_func, draw_closure, + src, src_x, src_y, + extents); + } else { + if (need_clip & NEED_CLIP_SURFACE) { + if (extents->is_bounded) { + status = clip_and_composite_with_mask (compositor, extents, + draw_func, mask_func, + draw_closure, + op, src, src_x, src_y); + } else { + status = clip_and_composite_combine (compositor, extents, + draw_func, draw_closure, + op, src, src_x, src_y); + } + } else { + status = draw_func (compositor, + dst, draw_closure, + op, src, src_x, src_y, + 0, 0, + &extents->bounded, + extents->clip); + } + } + cairo_surface_destroy (src); + +skip: + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + if (need_clip & NEED_CLIP_SURFACE) + status = fixup_unbounded_with_mask (compositor, extents); + else + status = fixup_unbounded (compositor, extents, NULL); + } + +error: + if (clip_region) + compositor->set_clip_region (dst, NULL); + + compositor->release (dst); + + return status; +} + +/* meta-ops */ + +typedef struct { + cairo_traps_t traps; + cairo_antialias_t antialias; +} composite_traps_info_t; + +static cairo_int_status_t +composite_traps (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, int src_y, + int dst_x, int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + composite_traps_info_t *info = closure; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + return compositor->composite_traps (dst, op, src, + src_x - dst_x, src_y - dst_y, + dst_x, dst_y, + extents, + info->antialias, &info->traps); +} + +typedef struct { + cairo_tristrip_t strip; + cairo_antialias_t antialias; +} composite_tristrip_info_t; + +static cairo_int_status_t +composite_tristrip (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, int src_y, + int dst_x, int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + composite_tristrip_info_t *info = closure; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + return compositor->composite_tristrip (dst, op, src, + src_x - dst_x, src_y - dst_y, + dst_x, dst_y, + extents, + info->antialias, &info->strip); +} + +static cairo_bool_t +is_recording_pattern (const cairo_pattern_t *pattern) +{ + cairo_surface_t *surface; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return FALSE; + + surface = ((const cairo_surface_pattern_t *) pattern)->surface; + surface = _cairo_surface_get_source (surface, NULL); + return _cairo_surface_is_recording (surface); +} + +static cairo_surface_t * +recording_pattern_get_surface (const cairo_pattern_t *pattern) +{ + cairo_surface_t *surface; + + surface = ((const cairo_surface_pattern_t *) pattern)->surface; + return _cairo_surface_get_source (surface, NULL); +} + +static cairo_bool_t +recording_pattern_contains_sample (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *sample) +{ + cairo_recording_surface_t *surface; + + if (! is_recording_pattern (pattern)) + return FALSE; + + if (pattern->extend == CAIRO_EXTEND_NONE) + return TRUE; + + surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern); + if (surface->unbounded) + return TRUE; + + return _cairo_rectangle_contains_rectangle (&surface->extents, sample); +} + +static cairo_bool_t +op_reduces_to_source (cairo_composite_rectangles_t *extents) +{ + if (extents->op == CAIRO_OPERATOR_SOURCE) + return TRUE; + + if (extents->surface->is_clear) + return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD; + + return FALSE; +} + +static cairo_status_t +composite_aligned_boxes (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + cairo_operator_t op = extents->op; + cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (extents->clip); + cairo_bool_t op_is_source; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (need_clip_mask && + (! extents->is_bounded || extents->op == CAIRO_OPERATOR_SOURCE)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + op_is_source = op_reduces_to_source (extents); + + /* Are we just copying a recording surface? */ + if (! need_clip_mask && op_is_source && + recording_pattern_contains_sample (&extents->source_pattern.base, + &extents->source_sample_area)) + { + cairo_clip_t *recording_clip; + const cairo_pattern_t *source = &extents->source_pattern.base; + const cairo_matrix_t *m; + cairo_matrix_t matrix; + + /* XXX could also do tiling repeat modes... */ + + /* first clear the area about to be overwritten */ + if (! dst->is_clear) { + status = compositor->acquire (dst); + if (unlikely (status)) + return status; + + status = compositor->fill_boxes (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + boxes); + compositor->release (dst); + if (unlikely (status)) + return status; + } + + m = &source->matrix; + if (_cairo_surface_has_device_transform (dst)) { + cairo_matrix_multiply (&matrix, + &source->matrix, + &dst->device_transform); + m = &matrix; + } + + recording_clip = _cairo_clip_from_boxes (boxes); + status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source), + m, dst, recording_clip); + _cairo_clip_destroy (recording_clip); + + return status; + } + + status = compositor->acquire (dst); + if (unlikely (status)) + return status; + + if (! need_clip_mask && + (op == CAIRO_OPERATOR_CLEAR || + extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID)) + { + const cairo_color_t *color; + + if (op == CAIRO_OPERATOR_CLEAR) { + color = CAIRO_COLOR_TRANSPARENT; + } else { + color = &((cairo_solid_pattern_t *) &extents->source_pattern)->color; + if (op_is_source) + op = CAIRO_OPERATOR_SOURCE; + } + + status = compositor->fill_boxes (dst, op, color, boxes); + } + else + { + cairo_surface_t *src, *mask = NULL; + cairo_pattern_t *source = &extents->source_pattern.base; + int src_x, src_y; + int mask_x = 0, mask_y = 0; + + if (need_clip_mask) { + mask = traps_get_clip_surface (compositor, + extents, &extents->bounded); + if (unlikely (mask->status)) + return mask->status; + + mask_x = -extents->bounded.x; + mask_y = -extents->bounded.y; + + if (op == CAIRO_OPERATOR_CLEAR) { + source = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + } else if (op_is_source) + op = CAIRO_OPERATOR_SOURCE; + + src = compositor->pattern_to_surface (dst, source, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (likely (src->status == CAIRO_STATUS_SUCCESS)) { + status = compositor->composite_boxes (dst, op, src, mask, + src_x, src_y, + mask_x, mask_y, + 0, 0, + boxes, &extents->bounded); + cairo_surface_destroy (src); + } else + status = src->status; + + cairo_surface_destroy (mask); + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) + status = fixup_unbounded (compositor, extents, boxes); + + compositor->release (dst); + + return status; +} + +static cairo_status_t +upload_boxes (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dst = extents->surface; + const cairo_pattern_t *source = &extents->source_pattern.base; + cairo_surface_t *src; + cairo_rectangle_int_t limit; + cairo_int_status_t status; + int tx, ty; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + src = _cairo_pattern_get_source((cairo_surface_pattern_t *)source, + &limit); + if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that the data is entirely within the image */ + if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || + extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + tx += limit.x; + ty += limit.y; + + if (src->type == CAIRO_SURFACE_TYPE_IMAGE) + status = compositor->draw_image_boxes (dst, + (cairo_image_surface_t *)src, + boxes, tx, ty); + else + status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, + tx, ty); + + return status; +} + +static cairo_int_status_t +trim_extents_to_traps (cairo_composite_rectangles_t *extents, + cairo_traps_t *traps) +{ + cairo_box_t box; + + _cairo_traps_extents (traps, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_int_status_t +trim_extents_to_tristrip (cairo_composite_rectangles_t *extents, + cairo_tristrip_t *strip) +{ + cairo_box_t box; + + _cairo_tristrip_extents (strip, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_int_status_t +trim_extents_to_boxes (cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_box_t box; + + _cairo_boxes_extents (boxes, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_int_status_t +boxes_for_traps (cairo_boxes_t *boxes, + cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i, j; + + /* first check that the traps are rectilinear */ + if (antialias == CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + const cairo_trapezoid_t *t = &traps->traps[i]; + if (_cairo_fixed_integer_round_down (t->left.p1.x) != + _cairo_fixed_integer_round_down (t->left.p2.x) || + _cairo_fixed_integer_round_down (t->right.p1.x) != + _cairo_fixed_integer_round_down (t->right.p2.x)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + } else { + for (i = 0; i < traps->num_traps; i++) { + const cairo_trapezoid_t *t = &traps->traps[i]; + if (t->left.p1.x != t->left.p2.x || t->right.p1.x != t->right.p2.x) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + _cairo_boxes_init (boxes); + + boxes->chunks.base = (cairo_box_t *) traps->traps; + boxes->chunks.size = traps->num_traps; + + if (antialias != CAIRO_ANTIALIAS_NONE) { + for (i = j = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + if (x1 == x2 || y1 == y2) + continue; + + boxes->chunks.base[j].p1.x = x1; + boxes->chunks.base[j].p1.y = y1; + boxes->chunks.base[j].p2.x = x2; + boxes->chunks.base[j].p2.y = y2; + j++; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && + _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); + } + } + } else { + boxes->is_pixel_aligned = TRUE; + + for (i = j = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + /* round down here to match Pixman's behavior when using traps. */ + boxes->chunks.base[j].p1.x = _cairo_fixed_round_down (x1); + boxes->chunks.base[j].p1.y = _cairo_fixed_round_down (y1); + boxes->chunks.base[j].p2.x = _cairo_fixed_round_down (x2); + boxes->chunks.base[j].p2.y = _cairo_fixed_round_down (y2); + j += (boxes->chunks.base[j].p1.x != boxes->chunks.base[j].p2.x && + boxes->chunks.base[j].p1.y != boxes->chunks.base[j].p2.y); + } + } + boxes->chunks.count = j; + boxes->num_boxes = j; + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_status_t +clip_and_composite_boxes (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes); + +static cairo_status_t +clip_and_composite_polygon (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_polygon_t *polygon, + cairo_antialias_t antialias, + cairo_fill_rule_t fill_rule, + cairo_bool_t curvy) +{ + composite_traps_info_t traps; + cairo_surface_t *dst = extents->surface; + cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip); + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (polygon->num_edges == 0) { + status = CAIRO_INT_STATUS_SUCCESS; + + if (! extents->is_bounded) { + cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); + + if (clip_region && + cairo_region_contains_rectangle (clip_region, + &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) + clip_region = NULL; + + if (clip_region != NULL) { + status = compositor->set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + if (clip_surface) + status = fixup_unbounded_with_mask (compositor, extents); + else + status = fixup_unbounded (compositor, extents, NULL); + + if (clip_region != NULL) + compositor->set_clip_region (dst, NULL); + } + + return status; + } + + if (extents->clip->path != NULL && extents->is_bounded) { + cairo_polygon_t clipper; + cairo_fill_rule_t clipper_fill_rule; + cairo_antialias_t clipper_antialias; + + status = _cairo_clip_get_polygon (extents->clip, + &clipper, + &clipper_fill_rule, + &clipper_antialias); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + if (clipper_antialias == antialias) { + status = _cairo_polygon_intersect (polygon, fill_rule, + &clipper, clipper_fill_rule); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip); + _cairo_clip_destroy (extents->clip); + extents->clip = clip; + + fill_rule = CAIRO_FILL_RULE_WINDING; + } + _cairo_polygon_fini (&clipper); + } + } + } + + if (antialias == CAIRO_ANTIALIAS_NONE && curvy) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + status = _cairo_rasterise_polygon_to_boxes (polygon, fill_rule, &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + assert (boxes.is_pixel_aligned); + status = clip_and_composite_boxes (compositor, extents, &boxes); + } + _cairo_boxes_fini (&boxes); + if ((status != CAIRO_INT_STATUS_UNSUPPORTED)) + return status; + } + + _cairo_traps_init (&traps.traps); + + if (antialias == CAIRO_ANTIALIAS_NONE && curvy) { + status = _cairo_rasterise_polygon_to_traps (polygon, fill_rule, antialias, &traps.traps); + } else { + status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule); + } + if (unlikely (status)) + goto CLEANUP_TRAPS; + + status = trim_extents_to_traps (extents, &traps.traps); + if (unlikely (status)) + goto CLEANUP_TRAPS; + + /* Use a fast path if the trapezoids consist of a set of boxes. */ + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (1) { + cairo_boxes_t boxes; + + status = boxes_for_traps (&boxes, &traps.traps, antialias); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = clip_and_composite_boxes (compositor, extents, &boxes); + /* XXX need to reconstruct the traps! */ + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + } + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + unsigned int flags = 0; + + /* For unbounded operations, the X11 server will estimate the + * affected rectangle and apply the operation to that. However, + * there are cases where this is an overestimate (e.g. the + * clip-fill-{eo,nz}-unbounded test). + * + * The clip will trim that overestimate to our expectations. + */ + if (! extents->is_bounded) + flags |= FORCE_CLIP_REGION; + + traps.antialias = antialias; + status = clip_and_composite (compositor, extents, + composite_traps, NULL, &traps, + need_unbounded_clip (extents) | flags); + } + +CLEANUP_TRAPS: + _cairo_traps_fini (&traps.traps); + + return status; +} + +struct composite_opacity_info { + const cairo_traps_compositor_t *compositor; + uint8_t op; + cairo_surface_t *dst; + cairo_surface_t *src; + int src_x, src_y; + double opacity; +}; + +static void composite_opacity(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct composite_opacity_info *info = closure; + const cairo_traps_compositor_t *compositor = info->compositor; + cairo_surface_t *mask; + int mask_x, mask_y; + cairo_color_t color; + cairo_solid_pattern_t solid; + + _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage); + _cairo_pattern_init_solid (&solid, &color); + mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE, + &_cairo_unbounded_rectangle, + &_cairo_unbounded_rectangle, + &mask_x, &mask_y); + if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { + if (info->src) { + compositor->composite (info->dst, info->op, info->src, mask, + x + info->src_x, y + info->src_y, + mask_x, mask_y, + x, y, + w, h); + } else { + compositor->composite (info->dst, info->op, mask, NULL, + mask_x, mask_y, + 0, 0, + x, y, + w, h); + } + } + + cairo_surface_destroy (mask); +} + + +static cairo_int_status_t +composite_opacity_boxes (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + const cairo_solid_pattern_t *mask = closure; + struct composite_opacity_info info; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + info.compositor = compositor; + info.op = op; + info.dst = dst; + + info.src = src; + info.src_x = src_x; + info.src_y = src_y; + + info.opacity = mask->color.alpha / (double) 0xffff; + + /* XXX for lots of boxes create a clip region for the fully opaque areas */ + for (i = 0; i < clip->num_boxes; i++) + do_unaligned_box(composite_opacity, &info, + &clip->boxes[i], dst_x, dst_y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +composite_boxes (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + cairo_traps_t traps; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = _cairo_traps_init_boxes (&traps, closure); + if (unlikely (status)) + return status; + + status = compositor->composite_traps (dst, op, src, + src_x - dst_x, src_y - dst_y, + dst_x, dst_y, + extents, + CAIRO_ANTIALIAS_DEFAULT, &traps); + _cairo_traps_fini (&traps); + + return status; +} + +static cairo_status_t +clip_and_composite_boxes (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + status = trim_extents_to_boxes (extents, boxes); + if (unlikely (status)) + return status; + + if (boxes->is_pixel_aligned && extents->clip->path == NULL && + extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && + (op_reduces_to_source (extents) || + (extents->op == CAIRO_OPERATOR_OVER && + (extents->source_pattern.surface.surface->content & CAIRO_CONTENT_ALPHA) == 0))) + { + status = upload_boxes (compositor, extents, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ + if (extents->clip->path != NULL && extents->is_bounded) { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_clip_t *clip; + + clip = _cairo_clip_copy (extents->clip); + clip = _cairo_clip_intersect_boxes (clip, boxes); + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &antialias); + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *saved_clip = extents->clip; + extents->clip = clip; + + status = clip_and_composite_polygon (compositor, extents, &polygon, + antialias, fill_rule, FALSE); + + clip = extents->clip; + extents->clip = saved_clip; + + _cairo_polygon_fini (&polygon); + } + _cairo_clip_destroy (clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Use a fast path if the boxes are pixel aligned (or nearly aligned!) */ + if (boxes->is_pixel_aligned) { + status = composite_aligned_boxes (compositor, extents, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + return clip_and_composite (compositor, extents, + composite_boxes, NULL, boxes, + need_unbounded_clip (extents)); +} + +static cairo_int_status_t +composite_traps_as_boxes (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + composite_traps_info_t *info) +{ + cairo_boxes_t boxes; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return clip_and_composite_boxes (compositor, extents, &boxes); +} + +static cairo_int_status_t +clip_and_composite_traps (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + composite_traps_info_t *info, + unsigned flags) +{ + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = trim_extents_to_traps (extents, &info->traps); + if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) + return status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if ((flags & FORCE_CLIP_REGION) == 0) + status = composite_traps_as_boxes (compositor, extents, info); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* For unbounded operations, the X11 server will estimate the + * affected rectangle and apply the operation to that. However, + * there are cases where this is an overestimate (e.g. the + * clip-fill-{eo,nz}-unbounded test). + * + * The clip will trim that overestimate to our expectations. + */ + if (! extents->is_bounded) + flags |= FORCE_CLIP_REGION; + + status = clip_and_composite (compositor, extents, + composite_traps, NULL, info, + need_unbounded_clip (extents) | flags); + } + + return status; +} + +static cairo_int_status_t +clip_and_composite_tristrip (const cairo_traps_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + composite_tristrip_info_t *info) +{ + cairo_int_status_t status; + unsigned int flags = 0; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = trim_extents_to_tristrip (extents, &info->strip); + if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) + return status; + + if (! extents->is_bounded) + flags |= FORCE_CLIP_REGION; + + status = clip_and_composite (compositor, extents, + composite_tristrip, NULL, info, + need_unbounded_clip (extents) | flags); + + return status; +} + +struct composite_mask { + cairo_surface_t *mask; + int mask_x, mask_y; +}; + +static cairo_int_status_t +composite_mask (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + struct composite_mask *data = closure; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (src != NULL) { + compositor->composite (dst, op, src, data->mask, + extents->x + src_x, extents->y + src_y, + extents->x + data->mask_x, extents->y + data->mask_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + } else { + compositor->composite (dst, op, data->mask, NULL, + extents->x + data->mask_x, extents->y + data->mask_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + } + + return CAIRO_STATUS_SUCCESS; +} + +struct composite_box_info { + const cairo_traps_compositor_t *compositor; + cairo_surface_t *dst; + cairo_surface_t *src; + int src_x, src_y; + uint8_t op; +}; + +static void composite_box(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct composite_box_info *info = closure; + const cairo_traps_compositor_t *compositor = info->compositor; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) { + cairo_surface_t *mask; + cairo_color_t color; + cairo_solid_pattern_t solid; + int mask_x, mask_y; + + _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff); + _cairo_pattern_init_solid (&solid, &color); + + mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE, + &_cairo_unbounded_rectangle, + &_cairo_unbounded_rectangle, + &mask_x, &mask_y); + + if (likely (mask->status == CAIRO_STATUS_SUCCESS)) { + compositor->composite (info->dst, info->op, info->src, mask, + x + info->src_x, y + info->src_y, + mask_x, mask_y, + x, y, + w, h); + } + + cairo_surface_destroy (mask); + } else { + compositor->composite (info->dst, info->op, info->src, NULL, + x + info->src_x, y + info->src_y, + 0, 0, + x, y, + w, h); + } +} + +static cairo_int_status_t +composite_mask_clip_boxes (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + struct composite_mask *data = closure; + struct composite_box_info info; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + info.compositor = compositor; + info.op = CAIRO_OPERATOR_SOURCE; + info.dst = dst; + info.src = data->mask; + info.src_x = data->mask_x; + info.src_y = data->mask_y; + + info.src_x += dst_x; + info.src_y += dst_y; + + for (i = 0; i < clip->num_boxes; i++) + do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +composite_mask_clip (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, + int src_y, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + struct composite_mask *data = closure; + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + composite_traps_info_t info; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &info.antialias); + if (unlikely (status)) + return status; + + _cairo_traps_init (&info.traps); + status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps, + &polygon, + fill_rule); + _cairo_polygon_fini (&polygon); + if (unlikely (status)) + return status; + + status = composite_traps (compositor, dst, &info, + CAIRO_OPERATOR_SOURCE, + data->mask, + data->mask_x + dst_x, data->mask_y + dst_y, + dst_x, dst_y, + extents, NULL); + _cairo_traps_fini (&info.traps); + + return status; +} + +/* high-level compositor interface */ + +static cairo_int_status_t +_cairo_traps_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor; + cairo_boxes_t boxes; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + _cairo_clip_steal_boxes (extents->clip, &boxes); + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_clip_unsteal_boxes (extents->clip, &boxes); + + return status; +} + +static cairo_int_status_t +_cairo_traps_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && + extents->clip->path == NULL) { + status = clip_and_composite (compositor, extents, + composite_opacity_boxes, + composite_opacity_boxes, + &extents->mask_pattern, + need_unbounded_clip (extents)); + } else { + struct composite_mask data; + + data.mask = compositor->pattern_to_surface (extents->surface, + &extents->mask_pattern.base, + TRUE, + &extents->bounded, + &extents->mask_sample_area, + &data.mask_x, + &data.mask_y); + if (unlikely (data.mask->status)) + return data.mask->status; + + status = clip_and_composite (compositor, extents, + composite_mask, + extents->clip->path ? composite_mask_clip : composite_mask_clip_boxes, + &data, need_bounded_clip (extents)); + + cairo_surface_destroy (data.mask); + } + + return status; +} + +static cairo_int_status_t +_cairo_traps_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents->clip); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED && 0 && + _cairo_clip_is_region (extents->clip)) /* XXX */ + { + composite_tristrip_info_t info; + + info.antialias = antialias; + _cairo_tristrip_init_with_clip (&info.strip, extents->clip); + status = _cairo_path_fixed_stroke_to_tristrip (path, style, + ctm, ctm_inverse, + tolerance, + &info.strip); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_tristrip (compositor, extents, &info); + _cairo_tristrip_fini (&info.strip); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED && + path->has_curve_to && antialias == CAIRO_ANTIALIAS_NONE) { + cairo_polygon_t polygon; + + _cairo_polygon_init_with_clip (&polygon, extents->clip); + status = _cairo_path_fixed_stroke_to_polygon (path, style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_polygon (compositor, + extents, &polygon, + CAIRO_ANTIALIAS_NONE, + CAIRO_FILL_RULE_WINDING, + TRUE); + _cairo_polygon_fini (&polygon); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_int_status_t (*func) (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_traps_t *traps); + composite_traps_info_t info; + unsigned flags; + + if (antialias == CAIRO_ANTIALIAS_BEST || antialias == CAIRO_ANTIALIAS_GOOD) { + func = _cairo_path_fixed_stroke_polygon_to_traps; + flags = 0; + } else { + func = _cairo_path_fixed_stroke_to_traps; + flags = need_bounded_clip (extents) & ~NEED_CLIP_SURFACE; + } + + info.antialias = antialias; + _cairo_traps_init_with_clip (&info.traps, extents->clip); + status = func (path, style, ctm, ctm_inverse, tolerance, &info.traps); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_traps (compositor, extents, &info, flags); + _cairo_traps_fini (&info.traps); + } + + return status; +} + +static cairo_int_status_t +_cairo_traps_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents->clip); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_boxes (compositor, extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + +#if 0 + if (extents->mask.width > extents->unbounded.width || + extents->mask.height > extents->unbounded.height) + { + cairo_box_t limits; + _cairo_box_from_rectangle (&limits, &extents->unbounded); + _cairo_polygon_init (&polygon, &limits, 1); + } + else + { + _cairo_polygon_init (&polygon, NULL, 0); + } + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, + extents->clip->boxes, + extents->clip->num_boxes); + } +#else + _cairo_polygon_init_with_clip (&polygon, extents->clip); + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); +#endif + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = clip_and_composite_polygon (compositor, extents, &polygon, + antialias, fill_rule, path->has_curve_to); + } + _cairo_polygon_fini (&polygon); + } + + return status; +} + +static cairo_int_status_t +composite_glyphs (const cairo_traps_compositor_t *compositor, + cairo_surface_t *dst, + void *closure, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, int src_y, + int dst_x, int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + cairo_composite_glyphs_info_t *info = closure; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (op == CAIRO_OPERATOR_ADD && (dst->content & CAIRO_CONTENT_COLOR) == 0) + info->use_mask = 0; + + return compositor->composite_glyphs (dst, op, src, + src_x, src_y, + dst_x, dst_y, + info); +} + +static cairo_int_status_t +_cairo_traps_compositor_glyphs (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + status = compositor->check_composite (extents); + if (unlikely (status)) + return status; + + _cairo_scaled_font_freeze_cache (scaled_font); + status = compositor->check_composite_glyphs (extents, + scaled_font, glyphs, + &num_glyphs); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_composite_glyphs_info_t info; + + info.font = scaled_font; + info.glyphs = glyphs; + info.num_glyphs = num_glyphs; + info.use_mask = overlap || ! extents->is_bounded; + info.extents = extents->bounded; + + status = clip_and_composite (compositor, extents, + composite_glyphs, NULL, &info, + need_bounded_clip (extents) | FORCE_CLIP_REGION); + } + _cairo_scaled_font_thaw_cache (scaled_font); + + return status; +} + +void +_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor, + const cairo_compositor_t *delegate) +{ + compositor->base.delegate = delegate; + + compositor->base.paint = _cairo_traps_compositor_paint; + compositor->base.mask = _cairo_traps_compositor_mask; + compositor->base.fill = _cairo_traps_compositor_fill; + compositor->base.stroke = _cairo_traps_compositor_stroke; + compositor->base.glyphs = _cairo_traps_compositor_glyphs; +} diff --git a/gfx/cairo/cairo/src/cairo-traps-private.h b/gfx/cairo/cairo/src/cairo-traps-private.h new file mode 100644 index 000000000000..dcaf40d18638 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-traps-private.h @@ -0,0 +1,143 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Carl D. Worth + */ + +#ifndef CAIRO_TRAPS_PRIVATE_H +#define CAIRO_TRAPS_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-error-private.h" +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +struct _cairo_traps { + cairo_status_t status; + + cairo_box_t bounds; + const cairo_box_t *limits; + int num_limits; + + unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */ + unsigned int has_intersections : 1; + unsigned int is_rectilinear : 1; + unsigned int is_rectangular : 1; + + int num_traps; + int traps_size; + cairo_trapezoid_t *traps; + cairo_trapezoid_t traps_embedded[16]; +}; + +/* cairo-traps.c */ +cairo_private void +_cairo_traps_init (cairo_traps_t *traps); + +cairo_private void +_cairo_traps_init_with_clip (cairo_traps_t *traps, + const cairo_clip_t *clip); + +cairo_private void +_cairo_traps_limit (cairo_traps_t *traps, + const cairo_box_t *boxes, + int num_boxes); + +cairo_private cairo_status_t +_cairo_traps_init_boxes (cairo_traps_t *traps, + const cairo_boxes_t *boxes); + +cairo_private void +_cairo_traps_clear (cairo_traps_t *traps); + +cairo_private void +_cairo_traps_fini (cairo_traps_t *traps); + +#define _cairo_traps_status(T) (T)->status + +cairo_private void +_cairo_traps_translate (cairo_traps_t *traps, int x, int y); + +cairo_private void +_cairo_traps_tessellate_triangle_with_edges (cairo_traps_t *traps, + const cairo_point_t t[3], + const cairo_point_t edges[4]); + +cairo_private void +_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, + const cairo_point_t q[4]); + +cairo_private cairo_status_t +_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, + const cairo_point_t *top_left, + const cairo_point_t *bottom_right); + +cairo_private void +_cairo_traps_add_trap (cairo_traps_t *traps, + cairo_fixed_t top, cairo_fixed_t bottom, + const cairo_line_t *left, + const cairo_line_t *right); + +cairo_private int +_cairo_traps_contain (const cairo_traps_t *traps, + double x, double y); + +cairo_private void +_cairo_traps_extents (const cairo_traps_t *traps, + cairo_box_t *extents); + +cairo_private cairo_int_status_t +_cairo_traps_extract_region (cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_region_t **region); + +cairo_private cairo_bool_t +_cairo_traps_to_boxes (cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_boxes_t *boxes); + +cairo_private cairo_status_t +_cairo_traps_path (const cairo_traps_t *traps, + cairo_path_fixed_t *path); + +cairo_private cairo_int_status_t +_cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + cairo_traps_t *traps); + +CAIRO_END_DECLS + +#endif /* CAIRO_TRAPS_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-traps.c b/gfx/cairo/cairo/src/cairo-traps.c index 2fe6684db7dc..b59b96d43950 100644 --- a/gfx/cairo/cairo/src/cairo-traps.c +++ b/gfx/cairo/cairo/src/cairo-traps.c @@ -39,10 +39,14 @@ #include "cairoint.h" +#include "cairo-box-inline.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" +#include "cairo-line-private.h" #include "cairo-region-private.h" #include "cairo-slope-private.h" +#include "cairo-traps-private.h" +#include "cairo-spans-private.h" /* private functions */ @@ -71,8 +75,23 @@ _cairo_traps_limit (cairo_traps_t *traps, const cairo_box_t *limits, int num_limits) { + int i; + traps->limits = limits; traps->num_limits = num_limits; + + traps->bounds = limits[0]; + for (i = 1; i < num_limits; i++) + _cairo_box_add_box (&traps->bounds, &limits[i]); +} + +void +_cairo_traps_init_with_clip (cairo_traps_t *traps, + const cairo_clip_t *clip) +{ + _cairo_traps_init (traps); + if (clip) + _cairo_traps_limit (traps, clip->boxes, clip->num_boxes); } void @@ -94,7 +113,7 @@ _cairo_traps_fini (cairo_traps_t *traps) if (traps->traps != traps->traps_embedded) free (traps->traps); - VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t))); + VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); } /* make room for at least one more trap */ @@ -131,10 +150,15 @@ _cairo_traps_grow (cairo_traps_t *traps) void _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bottom, - cairo_line_t *left, cairo_line_t *right) + const cairo_line_t *left, + const cairo_line_t *right) { cairo_trapezoid_t *trap; + assert (left->p1.y != left->p2.y); + assert (right->p1.y != right->p2.y); + assert (bottom > top); + if (unlikely (traps->num_traps == traps->traps_size)) { if (unlikely (! _cairo_traps_grow (traps))) return; @@ -147,8 +171,298 @@ _cairo_traps_add_trap (cairo_traps_t *traps, trap->right = *right; } +static void +_cairo_traps_add_clipped_trap (cairo_traps_t *traps, + cairo_fixed_t _top, cairo_fixed_t _bottom, + const cairo_line_t *_left, + const cairo_line_t *_right) +{ + /* Note: With the goofy trapezoid specification, (where an + * arbitrary two points on the lines can specified for the left + * and right edges), these limit checks would not work in + * general. For example, one can imagine a trapezoid entirely + * within the limits, but with two points used to specify the left + * edge entirely to the right of the limits. Fortunately, for our + * purposes, cairo will never generate such a crazy + * trapezoid. Instead, cairo always uses for its points the + * extreme positions of the edge that are visible on at least some + * trapezoid. With this constraint, it's impossible for both + * points to be outside the limits while the relevant edge is + * entirely inside the limits. + */ + if (traps->num_limits) { + const cairo_box_t *b = &traps->bounds; + cairo_fixed_t top = _top, bottom = _bottom; + cairo_line_t left = *_left, right = *_right; + + /* Trivially reject if trapezoid is entirely to the right or + * to the left of the limits. */ + if (left.p1.x >= b->p2.x && left.p2.x >= b->p2.x) + return; + + if (right.p1.x <= b->p1.x && right.p2.x <= b->p1.x) + return; + + /* And reject if the trapezoid is entirely above or below */ + if (top >= b->p2.y || bottom <= b->p1.y) + return; + + /* Otherwise, clip the trapezoid to the limits. We only clip + * where an edge is entirely outside the limits. If we wanted + * to be more clever, we could handle cases where a trapezoid + * edge intersects the edge of the limits, but that would + * require slicing this trapezoid into multiple trapezoids, + * and I'm not sure the effort would be worth it. */ + if (top < b->p1.y) + top = b->p1.y; + + if (bottom > b->p2.y) + bottom = b->p2.y; + + if (left.p1.x <= b->p1.x && left.p2.x <= b->p1.x) + left.p1.x = left.p2.x = b->p1.x; + + if (right.p1.x >= b->p2.x && right.p2.x >= b->p2.x) + right.p1.x = right.p2.x = b->p2.x; + + /* Trivial discards for empty trapezoids that are likely to + * be produced by our tessellators (most notably convex_quad + * when given a simple rectangle). + */ + if (top >= bottom) + return; + + /* cheap colinearity check */ + if (right.p1.x <= left.p1.x && right.p1.y == left.p1.y && + right.p2.x <= left.p2.x && right.p2.y == left.p2.y) + return; + + _cairo_traps_add_trap (traps, top, bottom, &left, &right); + } else + _cairo_traps_add_trap (traps, _top, _bottom, _left, _right); +} + +static int +_compare_point_fixed_by_y (const void *av, const void *bv) +{ + const cairo_point_t *a = av, *b = bv; + int ret = a->y - b->y; + if (ret == 0) + ret = a->x - b->x; + return ret; +} + +void +_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, + const cairo_point_t q[4]) +{ + int a, b, c, d; + int i; + cairo_slope_t ab, ad; + cairo_bool_t b_left_of_d; + cairo_line_t left; + cairo_line_t right; + + /* Choose a as a point with minimal y */ + a = 0; + for (i = 1; i < 4; i++) + if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0) + a = i; + + /* b and d are adjacent to a, while c is opposite */ + b = (a + 1) % 4; + c = (a + 2) % 4; + d = (a + 3) % 4; + + /* Choose between b and d so that b.y is less than d.y */ + if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) { + b = (a + 3) % 4; + d = (a + 1) % 4; + } + + /* Without freedom left to choose anything else, we have four + * cases to tessellate. + * + * First, we have to determine the Y-axis sort of the four + * vertices, (either abcd or abdc). After that we need to determine + * which edges will be "left" and which will be "right" in the + * resulting trapezoids. This can be determined by computing a + * slope comparison of ab and ad to determine if b is left of d or + * not. + * + * Note that "left of" here is in the sense of which edges should + * be the left vs. right edges of the trapezoid. In particular, b + * left of d does *not* mean that b.x is less than d.x. + * + * This should hopefully be made clear in the lame ASCII art + * below. Since the same slope comparison is used in all cases, we + * compute it before testing for the Y-value sort. */ + + /* Note: If a == b then the ab slope doesn't give us any + * information. In that case, we can replace it with the ac (or + * equivalenly the bc) slope which gives us exactly the same + * information we need. At worst the names of the identifiers ab + * and b_left_of_d are inaccurate in this case, (would be ac, and + * c_left_of_d). */ + if (q[a].x == q[b].x && q[a].y == q[b].y) + _cairo_slope_init (&ab, &q[a], &q[c]); + else + _cairo_slope_init (&ab, &q[a], &q[b]); + + _cairo_slope_init (&ad, &q[a], &q[d]); + + b_left_of_d = _cairo_slope_compare (&ab, &ad) > 0; + + if (q[c].y <= q[d].y) { + if (b_left_of_d) { + /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad)) + * + * top bot left right + * _a a a + * / / /| |\ a.y b.y ab ad + * b / b | b \ + * / / | | \ \ b.y c.y bc ad + * c / c | c \ + * | / \| \ \ c.y d.y cd ad + * d d d + */ + left.p1 = q[a]; left.p2 = q[b]; + right.p1 = q[a]; right.p2 = q[d]; + _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); + left.p1 = q[b]; left.p2 = q[c]; + _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right); + left.p1 = q[c]; left.p2 = q[d]; + _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right); + } else { + /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad)) + * + * a a a_ + * /| |\ \ \ a.y b.y ad ab + * / b | b \ b + * / / | | \ \ b.y c.y ad bc + * / c | c \ c + * / / |/ \ | c.y d.y ad cd + * d d d + */ + left.p1 = q[a]; left.p2 = q[d]; + right.p1 = q[a]; right.p2 = q[b]; + _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); + right.p1 = q[b]; right.p2 = q[c]; + _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right); + right.p1 = q[c]; right.p2 = q[d]; + _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right); + } + } else { + if (b_left_of_d) { + /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad)) + * + * a a a + * // / \ |\ a.y b.y ab ad + * /b/ b \ b \ + * / / \ \ \ \ b.y d.y bc ad + * /d/ \ d \ d + * // \ / \| d.y c.y bc dc + * c c c + */ + left.p1 = q[a]; left.p2 = q[b]; + right.p1 = q[a]; right.p2 = q[d]; + _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); + left.p1 = q[b]; left.p2 = q[c]; + _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right); + right.p1 = q[d]; right.p2 = q[c]; + _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right); + } else { + /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad)) + * + * a a a + * /| / \ \\ a.y b.y ad ab + * / b / b \b\ + * / / / / \ \ b.y d.y ad bc + * d / d / \d\ + * |/ \ / \\ d.y c.y dc bc + * c c c + */ + left.p1 = q[a]; left.p2 = q[d]; + right.p1 = q[a]; right.p2 = q[b]; + _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); + right.p1 = q[b]; right.p2 = q[c]; + _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right); + left.p1 = q[d]; left.p2 = q[c]; + _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right); + } + } +} + +static void add_tri (cairo_traps_t *traps, + int y1, int y2, + const cairo_line_t *left, + const cairo_line_t *right) +{ + if (y2 < y1) { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + + if (_cairo_lines_compare_at_y (left, right, y1) > 0) { + const cairo_line_t *tmp = left; + left = right; + right = tmp; + } + + _cairo_traps_add_clipped_trap (traps, y1, y2, left, right); +} + +void +_cairo_traps_tessellate_triangle_with_edges (cairo_traps_t *traps, + const cairo_point_t t[3], + const cairo_point_t edges[4]) +{ + cairo_line_t lines[3]; + + if (edges[0].y <= edges[1].y) { + lines[0].p1 = edges[0]; + lines[0].p2 = edges[1]; + } else { + lines[0].p1 = edges[1]; + lines[0].p2 = edges[0]; + } + + if (edges[2].y <= edges[3].y) { + lines[1].p1 = edges[2]; + lines[1].p2 = edges[3]; + } else { + lines[1].p1 = edges[3]; + lines[1].p2 = edges[2]; + } + + if (t[1].y == t[2].y) { + add_tri (traps, t[0].y, t[1].y, &lines[0], &lines[1]); + return; + } + + if (t[1].y <= t[2].y) { + lines[2].p1 = t[1]; + lines[2].p2 = t[2]; + } else { + lines[2].p1 = t[2]; + lines[2].p2 = t[1]; + } + + if (((t[1].y - t[0].y) < 0) ^ ((t[2].y - t[0].y) < 0)) { + add_tri (traps, t[0].y, t[1].y, &lines[0], &lines[2]); + add_tri (traps, t[0].y, t[2].y, &lines[1], &lines[2]); + } else if (abs(t[1].y - t[0].y) < abs(t[2].y - t[0].y)) { + add_tri (traps, t[0].y, t[1].y, &lines[0], &lines[1]); + add_tri (traps, t[1].y, t[2].y, &lines[2], &lines[1]); + } else { + add_tri (traps, t[0].y, t[2].y, &lines[1], &lines[0]); + add_tri (traps, t[1].y, t[2].y, &lines[2], &lines[0]); + } +} + /** - * _cairo_traps_init_box: + * _cairo_traps_init_boxes: * @traps: a #cairo_traps_t * @box: an array box that will each be converted to a single trapezoid * to store in @traps. @@ -229,6 +543,9 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps, cairo_bool_t reversed; int n; + if (top >= traps->bounds.p2.y || bottom <= traps->bounds.p1.y) + return CAIRO_STATUS_SUCCESS; + /* support counter-clockwise winding for rectangular tessellation */ reversed = top_left->x > bottom_right->x; if (reversed) { @@ -236,6 +553,9 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps, left.p1.x = left.p2.x = bottom_right->x; } + if (left.p1.x >= traps->bounds.p2.x || right.p1.x <= traps->bounds.p1.x) + return CAIRO_STATUS_SUCCESS; + for (n = 0; n < traps->num_limits; n++) { const cairo_box_t *limits = &traps->limits[n]; cairo_line_t _left, _right; @@ -485,6 +805,44 @@ _cairo_traps_extents (const cairo_traps_t *traps, } } +static cairo_bool_t +_mono_edge_is_vertical (const cairo_line_t *line) +{ + return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); +} + +static cairo_bool_t +_traps_are_pixel_aligned (cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i; + + if (antialias == CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + if (! _mono_edge_is_vertical (&traps->traps[i].left) || + ! _mono_edge_is_vertical (&traps->traps[i].right)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } else { + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } + + return TRUE; +} /** * _cairo_traps_extract_region: @@ -502,6 +860,7 @@ _cairo_traps_extents (const cairo_traps_t *traps, **/ cairo_int_status_t _cairo_traps_extract_region (cairo_traps_t *traps, + cairo_antialias_t antialias, cairo_region_t **region) { cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; @@ -510,20 +869,12 @@ _cairo_traps_extract_region (cairo_traps_t *traps, int i, rect_count; /* we only treat this a hint... */ - if (! traps->maybe_region) + if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region) return CAIRO_INT_STATUS_UNSUPPORTED; - for (i = 0; i < traps->num_traps; i++) { - if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || - traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || - ! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } + if (! _traps_are_pixel_aligned (traps, antialias)) { + traps->maybe_region = FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; } if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { @@ -535,19 +886,30 @@ _cairo_traps_extract_region (cairo_traps_t *traps, rect_count = 0; for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part (traps->traps[i].top); - int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); + int x1, y1, x2, y2; - rects[rect_count].x = x1; - rects[rect_count].y = y1; - rects[rect_count].width = x2 - x1; - rects[rect_count].height = y2 - y1; + if (antialias == CAIRO_ANTIALIAS_NONE) { + x1 = _cairo_fixed_integer_round_down (traps->traps[i].left.p1.x); + y1 = _cairo_fixed_integer_round_down (traps->traps[i].top); + x2 = _cairo_fixed_integer_round_down (traps->traps[i].right.p1.x); + y2 = _cairo_fixed_integer_round_down (traps->traps[i].bottom); + } else { + x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); + y1 = _cairo_fixed_integer_part (traps->traps[i].top); + x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); + y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); + } - rect_count++; + if (x2 > x1 && y2 > y1) { + rects[rect_count].x = x1; + rects[rect_count].y = y1; + rects[rect_count].width = x2 - x1; + rects[rect_count].height = y2 - y1; + rect_count++; + } } + *region = cairo_region_create_rectangles (rects, rect_count); status = (*region)->status; @@ -557,6 +919,66 @@ _cairo_traps_extract_region (cairo_traps_t *traps, return status; } +cairo_bool_t +_cairo_traps_to_boxes (cairo_traps_t *traps, + cairo_antialias_t antialias, + cairo_boxes_t *boxes) +{ + int i; + + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x) + return FALSE; + } + + _cairo_boxes_init (boxes); + + boxes->num_boxes = traps->num_traps; + boxes->chunks.base = (cairo_box_t *) traps->traps; + boxes->chunks.count = traps->num_traps; + boxes->chunks.size = traps->num_traps; + + if (antialias != CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + boxes->chunks.base[i].p1.x = x1; + boxes->chunks.base[i].p1.y = y1; + boxes->chunks.base[i].p2.x = x2; + boxes->chunks.base[i].p2.y = y2; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && + _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); + } + } + } else { + boxes->is_pixel_aligned = TRUE; + + for (i = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + /* round down here to match Pixman's behavior when using traps. */ + boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1); + boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1); + boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2); + boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2); + } + } + + return TRUE; +} + /* moves trap points such that they become the actual corners of the trapezoid */ static void _sanitize_trap (cairo_trapezoid_t *t) @@ -603,3 +1025,99 @@ _cairo_traps_path (const cairo_traps_t *traps, return CAIRO_STATUS_SUCCESS; } + +void +_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps) +{ + cairo_box_t extents; + int n; + +#if 0 + if (traps->has_limits) { + printf ("%s: limits=(%d, %d, %d, %d)\n", + filename, + traps->limits.p1.x, traps->limits.p1.y, + traps->limits.p2.x, traps->limits.p2.y); + } +#endif + + _cairo_traps_extents (traps, &extents); + fprintf (file, "extents=(%d, %d, %d, %d)\n", + extents.p1.x, extents.p1.y, + extents.p2.x, extents.p2.y); + + for (n = 0; n < traps->num_traps; n++) { + fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n", + traps->traps[n].top, + traps->traps[n].bottom, + traps->traps[n].left.p1.x, + traps->traps[n].left.p1.y, + traps->traps[n].left.p2.x, + traps->traps[n].left.p2.y, + traps->traps[n].right.p1.x, + traps->traps[n].right.p1.y, + traps->traps[n].right.p2.x, + traps->traps[n].right.p2.y); + } +} + +struct cairo_trap_renderer { + cairo_span_renderer_t base; + cairo_traps_t *traps; +}; + +static cairo_status_t +span_to_traps (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + struct cairo_trap_renderer *r = abstract_renderer; + cairo_fixed_t top, bot; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + top = _cairo_fixed_from_int (y); + bot = _cairo_fixed_from_int (y + h); + do { + if (spans[0].coverage) { + cairo_fixed_t x0 = _cairo_fixed_from_int(spans[0].x); + cairo_fixed_t x1 = _cairo_fixed_from_int(spans[1].x); + cairo_line_t left = { { x0, top }, { x0, bot } }, + right = { { x1, top }, { x1, bot } }; + _cairo_traps_add_trap (r->traps, top, bot, &left, &right); + } + spans++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, + cairo_traps_t *traps) +{ + struct cairo_trap_renderer renderer; + cairo_scan_converter_t *converter; + cairo_int_status_t status; + cairo_rectangle_int_t r; + + TRACE ((stderr, "%s: fill_rule=%d, antialias=%d\n", + __FUNCTION__, fill_rule, antialias)); + assert(antialias == CAIRO_ANTIALIAS_NONE); + + renderer.traps = traps; + renderer.base.render_rows = span_to_traps; + + _cairo_box_round_to_rectangle (&polygon->extents, &r); + converter = _cairo_mono_scan_converter_create (r.x, r.y, + r.x + r.width, + r.y + r.height, + fill_rule); + status = _cairo_mono_scan_converter_add_polygon (converter, polygon); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = converter->generate (converter, &renderer.base); + converter->destroy (converter); + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-tristrip-private.h b/gfx/cairo/cairo/src/cairo-tristrip-private.h new file mode 100644 index 000000000000..ccd28799e826 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tristrip-private.h @@ -0,0 +1,94 @@ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_TRISTRIP_PRIVATE_H +#define CAIRO_TRISTRIP_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-error-private.h" +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +struct _cairo_tristrip { + cairo_status_t status; + + /* XXX clipping */ + + const cairo_box_t *limits; + int num_limits; + + int num_points; + int size_points; + cairo_point_t *points; + cairo_point_t points_embedded[64]; +}; + +cairo_private void +_cairo_tristrip_init (cairo_tristrip_t *strip); + +cairo_private void +_cairo_tristrip_limit (cairo_tristrip_t *strip, + const cairo_box_t *limits, + int num_limits); + +cairo_private void +_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip, + const cairo_clip_t *clip); + +cairo_private void +_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y); + +cairo_private void +_cairo_tristrip_move_to (cairo_tristrip_t *strip, + const cairo_point_t *point); + +cairo_private void +_cairo_tristrip_add_point (cairo_tristrip_t *strip, + const cairo_point_t *point); + +cairo_private void +_cairo_tristrip_extents (const cairo_tristrip_t *strip, + cairo_box_t *extents); + +cairo_private void +_cairo_tristrip_fini (cairo_tristrip_t *strip); + +#define _cairo_tristrip_status(T) ((T)->status) + +CAIRO_END_DECLS + +#endif /* CAIRO_TRISTRIP_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-tristrip.c b/gfx/cairo/cairo/src/cairo-tristrip.c new file mode 100644 index 000000000000..bcf3b237167c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tristrip.c @@ -0,0 +1,185 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* + * 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 Chris Wilson + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-tristrip-private.h" + +void +_cairo_tristrip_init (cairo_tristrip_t *strip) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (strip, sizeof (cairo_tristrip_t))); + + strip->status = CAIRO_STATUS_SUCCESS; + + strip->num_limits = 0; + strip->num_points = 0; + + strip->size_points = ARRAY_LENGTH (strip->points_embedded); + strip->points = strip->points_embedded; +} + +void +_cairo_tristrip_fini (cairo_tristrip_t *strip) +{ + if (strip->points != strip->points_embedded) + free (strip->points); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (strip, sizeof (cairo_tristrip_t))); +} + + +void +_cairo_tristrip_limit (cairo_tristrip_t *strip, + const cairo_box_t *limits, + int num_limits) +{ + strip->limits = limits; + strip->num_limits = num_limits; +} + +void +_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip, + const cairo_clip_t *clip) +{ + _cairo_tristrip_init (strip); + if (clip) + _cairo_tristrip_limit (strip, clip->boxes, clip->num_boxes); +} + +/* make room for at least one more trap */ +static cairo_bool_t +_cairo_tristrip_grow (cairo_tristrip_t *strip) +{ + cairo_point_t *points; + int new_size = 4 * strip->size_points; + + if (CAIRO_INJECT_FAULT ()) { + strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + if (strip->points == strip->points_embedded) { + points = _cairo_malloc_ab (new_size, sizeof (cairo_point_t)); + if (points != NULL) + memcpy (points, strip->points, sizeof (strip->points_embedded)); + } else { + points = _cairo_realloc_ab (strip->points, + new_size, sizeof (cairo_trapezoid_t)); + } + + if (unlikely (points == NULL)) { + strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + strip->points = points; + strip->size_points = new_size; + return TRUE; +} + +void +_cairo_tristrip_add_point (cairo_tristrip_t *strip, + const cairo_point_t *p) +{ + if (unlikely (strip->num_points == strip->size_points)) { + if (unlikely (! _cairo_tristrip_grow (strip))) + return; + } + + strip->points[strip->num_points++] = *p; +} + +/* Insert degenerate triangles to advance to the given point. The + * next point inserted must also be @p. */ +void +_cairo_tristrip_move_to (cairo_tristrip_t *strip, + const cairo_point_t *p) +{ + if (strip->num_points == 0) + return; + + _cairo_tristrip_add_point (strip, &strip->points[strip->num_points-1]); + _cairo_tristrip_add_point (strip, p); +#if 0 + /* and one more for luck! (to preserve cw/ccw ordering) */ + _cairo_tristrip_add_point (strip, p); +#endif +} + +void +_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y) +{ + cairo_fixed_t xoff, yoff; + cairo_point_t *p; + int i; + + xoff = _cairo_fixed_from_int (x); + yoff = _cairo_fixed_from_int (y); + + for (i = 0, p = strip->points; i < strip->num_points; i++, p++) { + p->x += xoff; + p->y += yoff; + } +} + +void +_cairo_tristrip_extents (const cairo_tristrip_t *strip, + cairo_box_t *extents) +{ + int i; + + if (strip->num_points == 0) { + extents->p1.x = extents->p1.y = 0; + extents->p2.x = extents->p2.y = 0; + return; + } + + extents->p2 = extents->p1 = strip->points[0]; + for (i = 1; i < strip->num_points; i++) { + const cairo_point_t *p = &strip->points[i]; + + if (p->x < extents->p1.x) + extents->p1.x = p->x; + else if (p->x > extents->p2.x) + extents->p2.x = p->x; + + if (p->y < extents->p1.y) + extents->p1.y = p->y; + else if (p->y > extents->p2.y) + extents->p2.y = p->y; + } +} diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset-private.h b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h index f0822611d814..d97cf916205d 100644 --- a/gfx/cairo/cairo/src/cairo-truetype-subset-private.h +++ b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h @@ -39,8 +39,6 @@ #include "cairoint.h" -CAIRO_BEGIN_DECLS - #if CAIRO_HAS_FONT_SUBSET /* The structs defined here should strictly follow the TrueType @@ -54,7 +52,7 @@ CAIRO_BEGIN_DECLS * if you add new tables/structs that should be packed. */ -#define MAKE_TT_TAG(a, b, c, d) (a<<24 | b<<16 | c<<8 | d) +#define MAKE_TT_TAG(a, b, c, d) ((int)((uint32_t)a<<24 | b<<16 | c<<8 | d)) #define TT_TAG_CFF MAKE_TT_TAG('C','F','F',' ') #define TT_TAG_cmap MAKE_TT_TAG('c','m','a','p') #define TT_TAG_cvt MAKE_TT_TAG('c','v','t',' ') @@ -66,6 +64,7 @@ CAIRO_BEGIN_DECLS #define TT_TAG_loca MAKE_TT_TAG('l','o','c','a') #define TT_TAG_maxp MAKE_TT_TAG('m','a','x','p') #define TT_TAG_name MAKE_TT_TAG('n','a','m','e') +#define TT_TAG_OS2 MAKE_TT_TAG('O','S','/','2') #define TT_TAG_post MAKE_TT_TAG('p','o','s','t') #define TT_TAG_prep MAKE_TT_TAG('p','r','e','p') @@ -176,6 +175,18 @@ typedef struct _tt_name { } tt_name_t; +/* bitmask for fsSelection field */ +#define TT_FS_SELECTION_ITALIC 1 +#define TT_FS_SELECTION_BOLD 32 + +/* _unused fields are defined in TT spec but not used by cairo */ +typedef struct _tt_os2 { + uint16_t _unused1[2]; + uint16_t usWeightClass; + uint16_t _unused2[28]; + uint16_t fsSelection; + uint16_t _unused3[11]; +} tt_os2_t; /* composite_glyph_t flags */ #define TT_ARG_1_AND_2_ARE_WORDS 0x0001 @@ -198,6 +209,4 @@ typedef struct _tt_glyph_data { #endif /* CAIRO_HAS_FONT_SUBSET */ -CAIRO_END_DECLS - #endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset.c b/gfx/cairo/cairo/src/cairo-truetype-subset.c index 1d8ab0afc272..f5f06defc95e 100644 --- a/gfx/cairo/cairo/src/cairo-truetype-subset.c +++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c @@ -40,8 +40,10 @@ * http://www.microsoft.com/typography/specs/default.htm */ -#define _BSD_SOURCE /* for snprintf(), strdup() */ +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET @@ -75,24 +77,24 @@ struct _cairo_truetype_font { struct { char *font_name; char *ps_name; - unsigned int num_glyphs; - int *widths; + int num_glyphs_in_face; /* glyphs in font */ long x_min, y_min, x_max, y_max; long ascent, descent; int units_per_em; } base; - subset_glyph_t *glyphs; + subset_glyph_t *glyphs; /* array size: num_glyphs_in_face + 2 */ const cairo_scaled_font_backend_t *backend; - int num_glyphs_in_face; + unsigned int num_glyphs; /* glyphs used */ + int *widths; /* array size: num_glyphs_in_face + 1 */ int checksum_index; cairo_array_t output; cairo_array_t string_offsets; unsigned long last_offset; unsigned long last_boundary; - int *parent_to_subset; + int *parent_to_subset; /* array size: num_glyphs_in_face + 1 */ cairo_status_t status; - + cairo_bool_t is_pdf; }; /* @@ -105,7 +107,6 @@ check (tt_hhea_t, 36); check (tt_maxp_t, 32); check (tt_name_record_t, 12); check (tt_name_t, 18); -check (tt_name_t, 18); check (tt_composite_glyph_t, 16); check (tt_glyph_data_t, 26); #undef check @@ -122,7 +123,8 @@ static cairo_status_t _cairo_truetype_font_set_error (cairo_truetype_font_t *font, cairo_status_t status) { - if (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED) + if (status == CAIRO_STATUS_SUCCESS || + status == (int)CAIRO_INT_STATUS_UNSUPPORTED) return status; _cairo_status_set_error (&font->status, status); @@ -132,9 +134,11 @@ _cairo_truetype_font_set_error (cairo_truetype_font_t *font, static cairo_status_t _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_bool_t is_pdf, cairo_truetype_font_t **font_return) { cairo_status_t status; + cairo_bool_t is_synthetic; cairo_truetype_font_t *font; const cairo_scaled_font_backend_t *backend; tt_head_t head; @@ -155,6 +159,16 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, * return CAIRO_INT_STATUS_UNSUPPORTED; */ + /* We need to use a fallback font if this font differs from the glyf outlines. */ + if (backend->is_synthetic) { + status = backend->is_synthetic (scaled_font_subset->scaled_font, &is_synthetic); + if (unlikely (status)) + return status; + + if (is_synthetic) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + size = sizeof (tt_head_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_head, 0, @@ -179,12 +193,12 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, if (unlikely (status)) return status; - font = malloc (sizeof (cairo_truetype_font_t)); + font = _cairo_malloc (sizeof (cairo_truetype_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = backend; - font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); + font->base.num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); font->scaled_font_subset = scaled_font_subset; font->last_offset = 0; @@ -194,19 +208,24 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, if (unlikely (status)) goto fail1; - font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t)); + /* Add 2: +1 case font does not contain .notdef, and +1 because an extra + * entry is required to contain the end location of the last glyph. + */ + font->glyphs = calloc (font->base.num_glyphs_in_face + 2, sizeof (subset_glyph_t)); if (unlikely (font->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } - font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int)); + /* Add 1 in case font does not contain .notdef */ + font->parent_to_subset = calloc (font->base.num_glyphs_in_face + 1, sizeof (int)); if (unlikely (font->parent_to_subset == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } - font->base.num_glyphs = 0; + font->is_pdf = is_pdf; + font->num_glyphs = 0; font->base.x_min = (int16_t) be16_to_cpu (head.x_min); font->base.y_min = (int16_t) be16_to_cpu (head.y_min); font->base.x_max = (int16_t) be16_to_cpu (head.x_max); @@ -227,7 +246,7 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, /* If the PS name is not found, create a CairoFont-x-y name. */ if (font->base.ps_name == NULL) { - font->base.ps_name = malloc (30); + font->base.ps_name = _cairo_malloc (30); if (unlikely (font->base.ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; @@ -238,8 +257,9 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, scaled_font_subset->subset_id); } - font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int)); - if (unlikely (font->base.widths == NULL)) { + /* Add 1 in case font does not contain .notdef */ + font->widths = calloc (font->base.num_glyphs_in_face + 1, sizeof (int)); + if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } @@ -257,13 +277,12 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, fail5: _cairo_array_fini (&font->string_offsets); - free (font->base.widths); + free (font->widths); fail4: free (font->base.ps_name); fail3: free (font->parent_to_subset); - if (font->base.font_name) - free (font->base.font_name); + free (font->base.font_name); fail2: free (font->glyphs); fail1: @@ -277,10 +296,9 @@ static void cairo_truetype_font_destroy (cairo_truetype_font_t *font) { _cairo_array_fini (&font->string_offsets); - free (font->base.widths); + free (font->widths); free (font->base.ps_name); - if (font->base.font_name) - free (font->base.font_name); + free (font->base.font_name); free (font->parent_to_subset); free (font->glyphs); _cairo_array_fini (&font->output); @@ -393,51 +411,100 @@ cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, return CAIRO_STATUS_SUCCESS; } +typedef struct _cmap_unicode_range { + unsigned int start; + unsigned int end; +} cmap_unicode_range_t; + +static cmap_unicode_range_t winansi_unicode_ranges[] = { + { 0x0020, 0x007f }, + { 0x00a0, 0x00ff }, + { 0x0152, 0x0153 }, + { 0x0160, 0x0161 }, + { 0x0178, 0x0178 }, + { 0x017d, 0x017e }, + { 0x0192, 0x0192 }, + { 0x02c6, 0x02c6 }, + { 0x02dc, 0x02dc }, + { 0x2013, 0x2026 }, + { 0x2030, 0x2030 }, + { 0x2039, 0x203a }, + { 0x20ac, 0x20ac }, + { 0x2122, 0x2122 }, +}; + static cairo_status_t cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font, unsigned long tag) { - unsigned int i; + int i; + unsigned int j; + int range_offset; + int num_ranges; + int entry_selector; + int length; + + num_ranges = ARRAY_LENGTH (winansi_unicode_ranges); + + length = 16 + (num_ranges + 1)*8; + for (i = 0; i < num_ranges; i++) + length += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2; + + entry_selector = 0; + while ((1 << entry_selector) <= (num_ranges + 1)) + entry_selector++; + + entry_selector--; cairo_truetype_font_write_be16 (font, 0); /* Table version */ - cairo_truetype_font_write_be16 (font, 2); /* Num tables */ + cairo_truetype_font_write_be16 (font, 1); /* Num tables */ cairo_truetype_font_write_be16 (font, 3); /* Platform */ - cairo_truetype_font_write_be16 (font, 0); /* Encoding */ - cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */ + cairo_truetype_font_write_be16 (font, 1); /* Encoding */ + cairo_truetype_font_write_be32 (font, 12); /* Offset to start of table */ - cairo_truetype_font_write_be16 (font, 1); /* Platform */ - cairo_truetype_font_write_be16 (font, 0); /* Encoding */ - cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */ - - /* Output a format 4 encoding table. */ + /* Output a format 4 encoding table for the winansi encoding */ cairo_truetype_font_write_be16 (font, 4); /* Format */ - cairo_truetype_font_write_be16 (font, 32); /* Length */ + cairo_truetype_font_write_be16 (font, length); /* Length */ cairo_truetype_font_write_be16 (font, 0); /* Version */ - cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */ - cairo_truetype_font_write_be16 (font, 4); /* searchrange */ - cairo_truetype_font_write_be16 (font, 1); /* entry selector */ - cairo_truetype_font_write_be16 (font, 0); /* rangeshift */ - cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */ - cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */ + cairo_truetype_font_write_be16 (font, num_ranges*2 + 2); /* 2*segcount */ + cairo_truetype_font_write_be16 (font, (1 << (entry_selector + 1))); /* searchrange */ + cairo_truetype_font_write_be16 (font, entry_selector); /* entry selector */ + cairo_truetype_font_write_be16 (font, num_ranges*2 + 2 - (1 << (entry_selector + 1))); /* rangeshift */ + for (i = 0; i < num_ranges; i++) + cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].end); /* end count[] */ + cairo_truetype_font_write_be16 (font, 0xffff); /* end count[] */ + cairo_truetype_font_write_be16 (font, 0); /* reserved */ - cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */ - cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */ - cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */ - cairo_truetype_font_write_be16 (font, 1); /* delta[1] */ - cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */ - cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */ - /* Output a format 6 encoding table. */ + for (i = 0; i < num_ranges; i++) + cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].start); /* startCode[] */ + cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[] */ - cairo_truetype_font_write_be16 (font, 6); - cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs); - cairo_truetype_font_write_be16 (font, 0); - cairo_truetype_font_write_be16 (font, 0); /* First character */ - cairo_truetype_font_write_be16 (font, font->base.num_glyphs); - for (i = 0; i < font->base.num_glyphs; i++) - cairo_truetype_font_write_be16 (font, i); + for (i = 0; i < num_ranges; i++) + cairo_truetype_font_write_be16 (font, 0x0000); /* delta[] */ + cairo_truetype_font_write_be16 (font, 1); /* delta[] */ + + range_offset = num_ranges*2 + 2; + for (i = 0; i < num_ranges; i++) { + cairo_truetype_font_write_be16 (font, range_offset); /* rangeOffset[] */ + range_offset += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2 - 2; + } + cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[] */ + + for (i = 0; i < num_ranges; i++) { + for (j = winansi_unicode_ranges[i].start; j < winansi_unicode_ranges[i].end + 1; j++) { + int ch = _cairo_unicode_to_winansi (j); + int glyph; + + if (ch > 0) + glyph = font->scaled_font_subset->latin_to_subset_glyph_index[ch]; + else + glyph = 0; + cairo_truetype_font_write_be16 (font, glyph); + } + } return font->status; } @@ -551,21 +618,23 @@ cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, return _cairo_truetype_font_set_error (font, status); if (be16_to_cpu (header.index_to_loc_format) == 0) - size = sizeof (int16_t) * (font->num_glyphs_in_face + 1); + size = sizeof (int16_t) * (font->base.num_glyphs_in_face + 1); else - size = sizeof (int32_t) * (font->num_glyphs_in_face + 1); + size = sizeof (int32_t) * (font->base.num_glyphs_in_face + 1); - u.bytes = malloc (size); + u.bytes = _cairo_malloc (size); if (unlikely (u.bytes == NULL)) return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_loca, 0, u.bytes, &size); - if (unlikely (status)) + if (unlikely (status)) { + free (u.bytes); return _cairo_truetype_font_set_error (font, status); + } start_offset = _cairo_array_num_elements (&font->output); - for (i = 0; i < font->base.num_glyphs; i++) { + for (i = 0; i < font->num_glyphs; i++) { index = font->glyphs[i].parent_index; if (be16_to_cpu (header.index_to_loc_format) == 0) { begin = be16_to_cpu (u.short_offsets[index]) * 2; @@ -597,16 +666,34 @@ cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, if (unlikely (status)) goto FAIL; - if (size != 0) { - status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + if (size > 1) { + tt_glyph_data_t *glyph_data; + int num_contours; + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_glyf, begin, buffer, &size); if (unlikely (status)) goto FAIL; - status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); - if (unlikely (status)) - goto FAIL; - } + glyph_data = (tt_glyph_data_t *) buffer; + num_contours = (int16_t)be16_to_cpu (glyph_data->num_contours); + if (num_contours < 0) { + status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); + if (unlikely (status)) + goto FAIL; + } else if (num_contours == 0) { + /* num_contours == 0 is undefined in the Opentype + * spec. There are some embedded fonts that have a + * space glyph with num_contours = 0 that fails on + * some printers. The spec requires glyphs without + * contours to have a 0 size glyph entry in the loca + * table. + * + * If num_contours == 0, truncate the glyph to 0 size. + */ + _cairo_array_truncate (&font->output, _cairo_array_num_elements (&font->output) - size); + } + } } status = cairo_truetype_font_align_output (font, &next); @@ -675,7 +762,7 @@ cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); - hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs)); + hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->num_glyphs)); return CAIRO_STATUS_SUCCESS; } @@ -705,7 +792,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, num_hmetrics = be16_to_cpu(hhea.num_hmetrics); - for (i = 0; i < font->base.num_glyphs; i++) { + for (i = 0; i < font->num_glyphs; i++) { long_entry_size = 2 * sizeof (int16_t); short_entry_size = sizeof (int16_t); status = cairo_truetype_font_allocate_write_buffer (font, @@ -739,7 +826,7 @@ cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } - font->base.widths[i] = be16_to_cpu (p[0]); + font->widths[i] = be16_to_cpu (p[0]); } return CAIRO_STATUS_SUCCESS; @@ -766,10 +853,10 @@ cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, if (be16_to_cpu (header.index_to_loc_format) == 0) { - for (i = 0; i < font->base.num_glyphs + 1; i++) + for (i = 0; i < font->num_glyphs + 1; i++) cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2); } else { - for (i = 0; i < font->base.num_glyphs + 1; i++) + for (i = 0; i < font->num_glyphs + 1; i++) cairo_truetype_font_write_be32 (font, font->glyphs[i].location); } @@ -797,7 +884,7 @@ cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); - maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs); + maxp->num_glyphs = cpu_to_be16 (font->num_glyphs); return CAIRO_STATUS_SUCCESS; } @@ -941,13 +1028,13 @@ cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, unsigned short glyph, unsigned short *out) { - if (glyph >= font->num_glyphs_in_face) + if (glyph >= font->base.num_glyphs_in_face) return CAIRO_INT_STATUS_UNSUPPORTED; if (font->parent_to_subset[glyph] == 0) { - font->parent_to_subset[glyph] = font->base.num_glyphs; - font->glyphs[font->base.num_glyphs].parent_index = glyph; - font->base.num_glyphs++; + font->parent_to_subset[glyph] = font->num_glyphs; + font->glyphs[font->num_glyphs].parent_index = glyph; + font->num_glyphs++; } *out = font->parent_to_subset[glyph]; @@ -985,8 +1072,9 @@ cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font, * The tables in the table directory must be listed in alphabetical * order. The "cvt", "fpgm", and "prep" are optional tables. They * will only be embedded in the subset if they exist in the source - * font. The pos parameter of cairo_truetype_font_add_truetype_table() - * specifies the position of the table in the table directory. + * font. "cmap" is only embedded for latin fonts. The pos parameter of + * cairo_truetype_font_add_truetype_table() specifies the position of + * the table in the table directory. */ static void cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) @@ -1000,23 +1088,25 @@ cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_cvt, 0, NULL, - &size) == CAIRO_STATUS_SUCCESS) + &size) == CAIRO_INT_STATUS_SUCCESS) has_cvt = TRUE; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_fpgm, 0, NULL, - &size) == CAIRO_STATUS_SUCCESS) + &size) == CAIRO_INT_STATUS_SUCCESS) has_fpgm = TRUE; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_prep, 0, NULL, - &size) == CAIRO_STATUS_SUCCESS) + &size) == CAIRO_INT_STATUS_SUCCESS) has_prep = TRUE; font->num_tables = 0; - pos = 1; + pos = 0; + if (font->is_pdf && font->scaled_font_subset->is_latin) + pos++; if (has_cvt) pos++; if (has_fpgm) @@ -1024,7 +1114,8 @@ cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos); pos = 0; - cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++); + if (font->is_pdf && font->scaled_font_subset->is_latin) + cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++); if (has_cvt) cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++); if (has_fpgm) @@ -1039,9 +1130,10 @@ cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos); } -cairo_status_t -_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, - cairo_scaled_font_subset_t *font_subset) +static cairo_status_t +cairo_truetype_subset_init_internal (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset, + cairo_bool_t is_pdf) { cairo_truetype_font_t *font = NULL; cairo_status_t status; @@ -1052,7 +1144,7 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, const unsigned long *string_offsets = NULL; unsigned long num_strings = 0; - status = _cairo_truetype_font_create (font_subset, &font); + status = _cairo_truetype_font_create (font_subset, is_pdf, &font); if (unlikely (status)) return status; @@ -1076,13 +1168,13 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, } if (font->base.font_name != NULL) { - truetype_subset->font_name = strdup (font->base.font_name); - if (unlikely (truetype_subset->font_name == NULL)) { + truetype_subset->family_name_utf8 = strdup (font->base.font_name); + if (unlikely (truetype_subset->family_name_utf8 == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } } else { - truetype_subset->font_name = NULL; + truetype_subset->family_name_utf8 = NULL; } /* The widths array returned must contain only widths for the @@ -1095,7 +1187,7 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) - truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em; + truetype_subset->widths[i] = (double)font->widths[i]/font->base.units_per_em; truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em; truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em; @@ -1105,7 +1197,7 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, truetype_subset->descent = (double)font->base.descent/font->base.units_per_em; if (length) { - truetype_subset->data = malloc (length); + truetype_subset->data = _cairo_malloc (length); if (unlikely (truetype_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; @@ -1118,7 +1210,7 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, if (num_strings) { offsets_length = num_strings * sizeof (unsigned long); - truetype_subset->string_offsets = malloc (offsets_length); + truetype_subset->string_offsets = _cairo_malloc (offsets_length); if (unlikely (truetype_subset->string_offsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail5; @@ -1140,8 +1232,7 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, fail4: free (truetype_subset->widths); fail3: - if (truetype_subset->font_name) - free (truetype_subset->font_name); + free (truetype_subset->family_name_utf8); fail2: free (truetype_subset->ps_name); fail1: @@ -1150,12 +1241,25 @@ _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, return status; } +cairo_status_t +_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset) +{ + return cairo_truetype_subset_init_internal (truetype_subset, font_subset, FALSE); +} + +cairo_status_t +_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset, + cairo_scaled_font_subset_t *font_subset) +{ + return cairo_truetype_subset_init_internal (truetype_subset, font_subset, TRUE); +} + void _cairo_truetype_subset_fini (cairo_truetype_subset_t *subset) { free (subset->ps_name); - if (subset->font_name) - free (subset->font_name); + free (subset->family_name_utf8); free (subset->widths); free (subset->data); free (subset->string_offsets); @@ -1170,32 +1274,30 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_segment_map_t *map; - tt_segment_map_t buf; + tt_segment_map_t map_header; unsigned int num_segments, i; unsigned long size; uint16_t *start_code; uint16_t *end_code; uint16_t *delta; uint16_t *range_offset; - uint16_t *glyph_array; uint16_t c; backend = scaled_font->backend; - size = 4; + size = 4; /* enough to read the two header fields we need */ status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, table_offset, - (unsigned char *) &buf, + (unsigned char *) &map_header, &size); if (unlikely (status)) return status; /* All table formats have the same first two words */ - map = &buf; - if (be16_to_cpu (map->format) != 4) + if (be16_to_cpu (map_header.format) != 4) return CAIRO_INT_STATUS_UNSUPPORTED; - size = be16_to_cpu (map->length); - map = malloc (size); + size = be16_to_cpu (map_header.length); + map = _cairo_malloc (size); if (unlikely (map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1217,15 +1319,17 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, start_code = &(end_code[num_segments + 1]); delta = &(start_code[num_segments]); range_offset = &(delta[num_segments]); - glyph_array = &(range_offset[num_segments]); /* search for glyph in segments with rangeOffset=0 */ for (i = 0; i < num_segments; i++) { + uint16_t start = be16_to_cpu (start_code[i]); + uint16_t end = be16_to_cpu (end_code[i]); + + if (start == 0xffff && end == 0xffff) + break; + c = index - be16_to_cpu (delta[i]); - if (range_offset[i] == 0 && - c >= be16_to_cpu (start_code[i]) && - c <= be16_to_cpu (end_code[i])) - { + if (range_offset[i] == 0 && c >= start && c <= end) { *ucs4 = c; goto found; } @@ -1233,9 +1337,15 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, /* search for glyph in segments with rangeOffset=1 */ for (i = 0; i < num_segments; i++) { + uint16_t start = be16_to_cpu (start_code[i]); + uint16_t end = be16_to_cpu (end_code[i]); + + if (start == 0xffff && end == 0xffff) + break; + if (range_offset[i] != 0) { uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2; - int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1; + int range_size = end - start + 1; uint16_t g_id_be = cpu_to_be16 (index); int j; @@ -1245,7 +1355,7 @@ _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, for (j = 0; j < range_size; j++) { if (glyph_ids[j] == g_id_be) { - *ucs4 = be16_to_cpu (start_code[i]) + j; + *ucs4 = start + j; goto found; } } @@ -1270,10 +1380,10 @@ _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, unsigned long index, uint32_t *ucs4) { - cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; const cairo_scaled_font_backend_t *backend; tt_cmap_t *cmap; - tt_cmap_t buf; + tt_cmap_t cmap_header; int num_tables, i; unsigned long size; @@ -1281,17 +1391,16 @@ _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; - size = 4; + size = 4; /* only read the header fields 'version' and 'num_tables' */ status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, 0, - (unsigned char *) &buf, + (unsigned char *) &cmap_header, &size); if (unlikely (status)) return status; - cmap = &buf; - num_tables = be16_to_cpu (cmap->num_tables); - size = 4 + num_tables*sizeof(tt_cmap_index_t); + num_tables = be16_to_cpu (cmap_header.num_tables); + size = 4 + num_tables * sizeof (tt_cmap_index_t); cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4); if (unlikely (cmap == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -1322,6 +1431,119 @@ cleanup: return status; } +/* + * Sanity check on font name length as some broken fonts may return very long + * strings of garbage. 127 is maximum length of a PS name. + */ +#define MAX_FONT_NAME_LENGTH 127 + +static cairo_status_t +find_name (tt_name_t *name, int name_id, int platform, int encoding, int language, char **str_out) +{ + tt_name_record_t *record; + int i, len; + char *str; + char *p; + cairo_bool_t has_tag; + cairo_status_t status; + + str = NULL; + for (i = 0; i < be16_to_cpu (name->num_records); i++) { + record = &(name->records[i]); + if (be16_to_cpu (record->name) == name_id && + be16_to_cpu (record->platform) == platform && + be16_to_cpu (record->encoding) == encoding && + (language == -1 || be16_to_cpu (record->language) == language)) { + + len = be16_to_cpu (record->length); + if (platform == 3 && len > MAX_FONT_NAME_LENGTH*2) /* UTF-16 name */ + break; + + if (len > MAX_FONT_NAME_LENGTH) + break; + + str = _cairo_malloc (len + 1); + if (str == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (str, + ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), + len); + str[be16_to_cpu (record->length)] = 0; + break; + } + } + if (str == NULL) { + *str_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + + if (platform == 3) { /* Win platform, unicode encoding */ + /* convert to utf8 */ + int size = 0; + char *utf8; + uint16_t *u = (uint16_t *) str; + int u_len = len/2; + + for (i = 0; i < u_len; i++) + size += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), NULL); + + utf8 = _cairo_malloc (size + 1); + if (utf8 == NULL) { + status =_cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + p = utf8; + for (i = 0; i < u_len; i++) + p += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), p); + *p = 0; + free (str); + str = utf8; + } else if (platform == 1) { /* Mac platform, Mac Roman encoding */ + /* Replace characters above 127 with underscores. We could use + * a lookup table to convert to unicode but since most fonts + * include a unicode name this is just a rarely used fallback. */ + for (i = 0; i < len; i++) { + if ((unsigned char)str[i] > 127) + str[i] = '_'; + } + } + + /* If font name is prefixed with a PDF subset tag, strip it off. */ + p = str; + len = strlen (str); + has_tag = FALSE; + if (len > 7 && p[6] == '+') { + has_tag = TRUE; + for (i = 0; i < 6; i++) { + if (p[i] < 'A' || p[i] > 'Z') { + has_tag = FALSE; + break; + } + } + } + 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; + free (str); + str = p; + } + + *str_out = str; + + return CAIRO_STATUS_SUCCESS; + + fail: + free (str); + + return status; +} + cairo_int_status_t _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, char **ps_name_out, @@ -1330,11 +1552,9 @@ _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_name_t *name; - tt_name_record_t *record; unsigned long size; - int i, j; char *ps_name = NULL; - char *font_name = NULL; + char *family_name = NULL; backend = scaled_font->backend; if (!backend->load_truetype_table) @@ -1348,86 +1568,114 @@ _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, if (status) return status; - name = malloc (size); + name = _cairo_malloc (size); if (name == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = backend->load_truetype_table (scaled_font, + status = backend->load_truetype_table (scaled_font, TT_TAG_name, 0, (unsigned char *) name, &size); if (status) goto fail; - /* Extract the font name and PS name from the name table. At - * present this just looks for the Mac platform/Roman encoded font - * name. It should be extended to use any suitable font name in - * the name table. - */ - for (i = 0; i < be16_to_cpu(name->num_records); i++) { - record = &(name->records[i]); - if ((be16_to_cpu (record->platform) == 1) && - (be16_to_cpu (record->encoding) == 0)) { + /* Find PS Name (name_id = 6). OT spec says PS name must be one of + * the following two encodings */ + status = find_name (name, 6, 3, 1, 0x409, &ps_name); /* win, unicode, english-us */ + if (unlikely(status)) + goto fail; - if (be16_to_cpu (record->name) == 4) { - font_name = malloc (be16_to_cpu(record->length) + 1); - if (font_name == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - strncpy(font_name, - ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), - be16_to_cpu (record->length)); - font_name[be16_to_cpu (record->length)] = 0; - } - - if (be16_to_cpu (record->name) == 6) { - ps_name = malloc (be16_to_cpu(record->length) + 1); - if (ps_name == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } - strncpy(ps_name, - ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), - be16_to_cpu (record->length)); - ps_name[be16_to_cpu (record->length)] = 0; - } - - if (font_name && ps_name) - break; - } + if (!ps_name) { + status = find_name (name, 6, 1, 0, 0, &ps_name); /* mac, roman, english */ + if (unlikely(status)) + goto fail; } + /* Find Family name (name_id = 1) */ + status = find_name (name, 1, 3, 1, 0x409, &family_name); /* win, unicode, english-us */ + if (unlikely(status)) + goto fail; + + if (!family_name) { + status = find_name (name, 1, 3, 0, 0x409, &family_name); /* win, symbol, english-us */ + if (unlikely(status)) + goto fail; + } + + if (!family_name) { + status = find_name (name, 1, 1, 0, 0, &family_name); /* mac, roman, english */ + if (unlikely(status)) + goto fail; + } + + if (!family_name) { + status = find_name (name, 1, 3, 1, -1, &family_name); /* win, unicode, any language */ + if (unlikely(status)) + goto fail; + } + + status = _cairo_escape_ps_name (&ps_name); + if (unlikely(status)) + goto fail; + free (name); - /* Ensure PS name does not contain any spaces */ - if (ps_name) { - for (i = 0, j = 0; ps_name[j]; j++) { - if (ps_name[j] == ' ') - continue; - ps_name[i++] = ps_name[j]; - } - ps_name[i] = '\0'; - } - *ps_name_out = ps_name; - *font_name_out = font_name; + *font_name_out = family_name; return CAIRO_STATUS_SUCCESS; fail: free (name); - - if (ps_name != NULL) - free (ps_name); - - if (font_name != NULL) - free (font_name); - + free (ps_name); + free (family_name); *ps_name_out = NULL; *font_name_out = NULL; return status; } +cairo_int_status_t +_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font, + int *weight, + cairo_bool_t *bold, + cairo_bool_t *italic) +{ + cairo_status_t status; + const cairo_scaled_font_backend_t *backend; + tt_os2_t os2; + unsigned long size; + uint16_t selection; + + backend = scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, + TT_TAG_OS2, 0, + NULL, + &size); + if (status) + return status; + + if (size < sizeof(os2)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = sizeof (os2); + status = backend->load_truetype_table (scaled_font, + TT_TAG_OS2, 0, + (unsigned char *) &os2, + &size); + if (status) + return status; + + *weight = be16_to_cpu (os2.usWeightClass); + selection = be16_to_cpu (os2.fsSelection); + *bold = (selection & TT_FS_SELECTION_BOLD) ? TRUE : FALSE; + *italic = (selection & TT_FS_SELECTION_ITALIC) ? TRUE : FALSE; + + return CAIRO_STATUS_SUCCESS; +} + #endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/gfx/cairo/cairo/src/cairo-type1-fallback.c b/gfx/cairo/cairo/src/cairo-type1-fallback.c index b93c42348ba9..0b8e66cd0b93 100644 --- a/gfx/cairo/cairo/src/cairo-type1-fallback.c +++ b/gfx/cairo/cairo/src/cairo-type1-fallback.c @@ -33,8 +33,10 @@ * Adrian Johnson */ -#define _BSD_SOURCE /* for snprintf(), strdup() */ +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET @@ -146,7 +148,7 @@ static void charstring_encode_command (cairo_array_t *data, int command) { cairo_status_t status; - int orig_size; + unsigned int orig_size; unsigned char buf[5]; unsigned char *p = buf; @@ -173,7 +175,7 @@ charstring_encode_integer (cairo_array_t *data, cairo_charstring_type_t type) { cairo_status_t status; - int orig_size; + unsigned int orig_size; unsigned char buf[10]; unsigned char *p = buf; @@ -407,7 +409,6 @@ cairo_type1_font_create_charstring (cairo_type1_font_t *font, path_info.type = type; if (emit_path) { status = _cairo_path_fixed_interpret (scaled_glyph->path, - CAIRO_DIRECTION_FORWARD, _charstring_move_to, _charstring_line_to, _charstring_curve_to, @@ -514,12 +515,27 @@ cairo_type1_font_write_header (cairo_type1_font_t *font, "} readonly def\n" "/Encoding 256 array\n" "0 1 255 {1 index exch /.notdef put} for\n"); - for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { - if (font->scaled_font_subset->glyph_names != NULL) { - _cairo_output_stream_printf (font->output, "dup %d /%s put\n", - i, font->scaled_font_subset->glyph_names[i]); - } else { - _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i); + if (font->scaled_font_subset->is_latin) { + for (i = 1; i < 256; i++) { + int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i]; + + if (subset_glyph > 0) { + if (font->scaled_font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (font->output, "dup %d /%s put\n", + i, font->scaled_font_subset->glyph_names[subset_glyph]); + } else { + _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, subset_glyph); + } + } + } + } else { + for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + if (font->scaled_font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (font->output, "dup %d /%s put\n", + i, font->scaled_font_subset->glyph_names[i]); + } else { + _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i); + } } } _cairo_output_stream_printf (font->output, @@ -612,7 +628,7 @@ cairo_type1_font_write_private_dict (cairo_type1_font_t *font, fail: status2 = _cairo_output_stream_destroy (encrypted_output); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; @@ -744,7 +760,7 @@ _cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, length = font->header_size + font->data_size + font->trailer_size; - type1_subset->data = malloc (length); + type1_subset->data = _cairo_malloc (length); if (unlikely (type1_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; @@ -755,10 +771,10 @@ _cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, len = snprintf(type1_subset->data + font->bbox_position, font->bbox_max_chars, "%d %d %d %d", - (int)type1_subset->x_min, - (int)type1_subset->y_min, - (int)type1_subset->x_max, - (int)type1_subset->y_max); + (int)font->x_min, + (int)font->y_min, + (int)font->x_max, + (int)font->y_max); type1_subset->data[font->bbox_position + len] = ' '; type1_subset->header_length = font->header_size; diff --git a/gfx/cairo/cairo/src/cairo-type1-glyph-names.c b/gfx/cairo/cairo/src/cairo-type1-glyph-names.c new file mode 100644 index 000000000000..80ccb96269ea --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-type1-glyph-names.c @@ -0,0 +1,410 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Kristian Høgsberg + */ + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-type1-private.h" +#include "cairo-scaled-font-subsets-private.h" + +#if 0 +/* + * The three tables that follow are generated using this perl code: + */ + +@ps_standard_encoding = ( + # 0 + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 16 + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 32 + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + # 48 + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + # 64 + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + # 80 + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + # 96 + "quoteleft", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + # 112 + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", NULL, + # 128 + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 144 + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 160 + NULL, "exclamdown", "cent", "sterling", + "fraction", "yen", "florin", "section", + "currency", "quotesingle", "quotedblleft", "guillemotleft", + "guilsinglleft","guilsinglright","fi", "fl", + # 176 + NULL, "endash", "dagger", "daggerdbl", + "periodcentered",NULL, "paragraph", "bullet", + "quotesinglbase","quotedblbase","quotedblright","guillemotright", + "ellipsis", "perthousand", NULL, "questiondown", + # 192 + NULL, "grave", "acute", "circumflex", + "tilde", "macron", "breve", "dotaccent", + "dieresis", NULL, "ring", "cedilla", + NULL, "hungarumlaut", "ogonek", "caron", + # 208 + "emdash", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 224 + NULL, "AE", NULL, "ordfeminine", + NULL, NULL, NULL, NULL, + "Lslash", "Oslash", "OE", "ordmasculine", + NULL, NULL, NULL, NULL, + # 240 + NULL, "ae", NULL, NULL, + NULL, "dotlessi", NULL, NULL, + "lslash", "oslash", "oe", "germandbls", + NULL, NULL, NULL, NULL + ); + +@winansi_encoding = ( + # 0 + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 16 + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + # 32 + "space", "exclam", "quotedbl", "numbersign", + "dollar", "percent", "ampersand", "quotesingle", + "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", + # 48 + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", + # 64 + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + # 80 + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", + # 96 + "grave", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "l", "m", "n", "o", + # 112 + "p", "q", "r", "s", + "t", "u", "v", "w", + "x", "y", "z", "braceleft", + "bar", "braceright", "asciitilde", NULL, + # 128 + "Euro", NULL, "quotesinglbase","florin", + "quotedblbase", "ellipsis", "dagger", "daggerdbl", + "circumflex", "perthousand", "Scaron", "guilsinglleft", + "OE", NULL, "Zcaron", NULL, + # 144 + NULL, "quoteleft", "quoteright", "quotedblleft", + "quotedblright","bullet", "endash", "emdash", + "tilde", "trademark", "scaron", "guilsinglright", + "oe", NULL, "zcaron", "Ydieresis", + # 160 + NULL, "exclamdown", "cent", "sterling", + "currency", "yen", "brokenbar", "section", + "dieresis", "copyright", "ordfeminine", "guillemotleft", + # 173 is also "hyphen" but we leave this NULL to avoid duplicate names + "logicalnot", NULL, "registered", "macron", + # 176 + "degree", "plusminus", "twosuperior", "threesuperior", + "acute", "mu", "paragraph", "periodcentered", + "cedilla", "onesuperior", "ordmasculine", "guillemotright", + "onequarter", "onehalf", "threequarters","questiondown", + # 192 + "Agrave", "Aacute", "Acircumflex", "Atilde", + "Adieresis", "Aring", "AE", "Ccedilla", + "Egrave", "Eacute", "Ecircumflex", "Edieresis", + "Igrave", "Iacute", "Icircumflex", "Idieresis", + # 208 + "Eth", "Ntilde", "Ograve", "Oacute", + "Ocircumflex", "Otilde", "Odieresis", "multiply", + "Oslash", "Ugrave", "Uacute", "Ucircumflex", + "Udieresis", "Yacute", "Thorn", "germandbls", + # 224 + "agrave", "aacute", "acircumflex", "atilde", + "adieresis", "aring", "ae", "ccedilla", + "egrave", "eacute", "ecircumflex", "edieresis", + "igrave", "iacute", "icircumflex", "idieresis", + # 240 + "eth", "ntilde", "ograve", "oacute", + "ocircumflex", "otilde", "odieresis", "divide", + "oslash", "ugrave", "uacute", "ucircumflex", + "udieresis", "yacute", "thorn", "ydieresis" +); + +sub print_offsets { + $s = qq(); + for $sym (@_) { + if (! ($sym eq NULL)) { + $ss = qq( $hash{$sym}/*$sym*/,); + } else { + $ss = qq( 0,); + } + if (length($s) + length($ss) > 78) { + print qq( $s\n); + $s = ""; + } + $s .= $ss; + } + print qq( $s\n); +} + +@combined = (@ps_standard_encoding, @winansi_encoding); +print "static const char glyph_name_symbol[] = {\n"; +%hash = (); +$s = qq( "\\0"); +$offset = 1; +for $sym (@combined) { + if (! ($sym eq NULL)) { + if (! exists $hash{$sym}) { + $hash{$sym} = $offset; + $offset += length($sym) + 1; + $ss = qq( "$sym\\0"); + if (length($s) + length($ss) > 78) { + print qq( $s\n); + $s = ""; + } + $s .= $ss; + } + } +} +print qq( $s\n); +print "};\n\n"; + +print "static const int16_t ps_standard_encoding_offset[256] = {\n"; +print_offsets(@ps_standard_encoding); +print "};\n"; + +print "static const int16_t winansi_encoding_offset[256] = {\n"; +print_offsets(@winansi_encoding); +print "};\n"; + +exit; +#endif + +static const char glyph_name_symbol[] = { + "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0" + "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0" + "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0" + "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0" + "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0" + "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0" + "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0" + "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0" + "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0" + "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0" + "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0" + "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0" + "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0" + "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0" + "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0" + "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0" + "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0" + "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0" + "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0" + "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0" + "oslash\0" "oe\0" "germandbls\0" "Euro\0" "Scaron\0" "Zcaron\0" "trademark\0" + "scaron\0" "zcaron\0" "Ydieresis\0" "brokenbar\0" "copyright\0" + "logicalnot\0" "registered\0" "degree\0" "plusminus\0" "twosuperior\0" + "threesuperior\0" "mu\0" "onesuperior\0" "onequarter\0" "onehalf\0" + "threequarters\0" "Agrave\0" "Aacute\0" "Acircumflex\0" "Atilde\0" + "Adieresis\0" "Aring\0" "Ccedilla\0" "Egrave\0" "Eacute\0" "Ecircumflex\0" + "Edieresis\0" "Igrave\0" "Iacute\0" "Icircumflex\0" "Idieresis\0" "Eth\0" + "Ntilde\0" "Ograve\0" "Oacute\0" "Ocircumflex\0" "Otilde\0" "Odieresis\0" + "multiply\0" "Ugrave\0" "Uacute\0" "Ucircumflex\0" "Udieresis\0" "Yacute\0" + "Thorn\0" "agrave\0" "aacute\0" "acircumflex\0" "atilde\0" "adieresis\0" + "aring\0" "ccedilla\0" "egrave\0" "eacute\0" "ecircumflex\0" "edieresis\0" + "igrave\0" "iacute\0" "icircumflex\0" "idieresis\0" "eth\0" "ntilde\0" + "ograve\0" "oacute\0" "ocircumflex\0" "otilde\0" "odieresis\0" "divide\0" + "ugrave\0" "uacute\0" "ucircumflex\0" "udieresis\0" "yacute\0" "thorn\0" + "ydieresis\0" +}; + +static const int16_t ps_standard_encoding_offset[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, + 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/, + 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, + 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, + 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, + 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, + 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, + 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, + 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, + 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, + 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, + 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/, + 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, + 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, + 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, + 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, + 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/, + 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/, + 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/, + 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/, + 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/, + 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/, + 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0, + 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/, + 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/, + 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/, + 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/, + 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0, + 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/, + 900/*germandbls*/, 0, 0, 0, 0, +}; + +static const int16_t winansi_encoding_offset[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, + 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 498/*quotesingle*/, + 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, + 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, + 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, + 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, + 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, + 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, + 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, + 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, + 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, + 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 719/*grave*/, + 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, + 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, + 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, + 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, + 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 911/*Euro*/, 0, + 628/*quotesinglbase*/, 474/*florin*/, 643/*quotedblbase*/, 685/*ellipsis*/, + 579/*dagger*/, 586/*daggerdbl*/, 731/*circumflex*/, 694/*perthousand*/, + 916/*Scaron*/, 537/*guilsinglleft*/, 855/*OE*/, 0, 923/*Zcaron*/, 0, 0, + 338/*quoteleft*/, 59/*quoteright*/, 510/*quotedblleft*/, + 656/*quotedblright*/, 621/*bullet*/, 572/*endash*/, 819/*emdash*/, + 742/*tilde*/, 930/*trademark*/, 940/*scaron*/, 551/*guilsinglright*/, + 897/*oe*/, 0, 947/*zcaron*/, 954/*Ydieresis*/, 0, 436/*exclamdown*/, + 447/*cent*/, 452/*sterling*/, 489/*currency*/, 470/*yen*/, 964/*brokenbar*/, + 481/*section*/, 771/*dieresis*/, 974/*copyright*/, 829/*ordfeminine*/, + 523/*guillemotleft*/, 984/*logicalnot*/, 0, 995/*registered*/, 748/*macron*/, + 1006/*degree*/, 1013/*plusminus*/, 1023/*twosuperior*/, + 1035/*threesuperior*/, 725/*acute*/, 1049/*mu*/, 611/*paragraph*/, + 596/*periodcentered*/, 785/*cedilla*/, 1052/*onesuperior*/, + 858/*ordmasculine*/, 670/*guillemotright*/, 1064/*onequarter*/, + 1075/*onehalf*/, 1083/*threequarters*/, 706/*questiondown*/, 1097/*Agrave*/, + 1104/*Aacute*/, 1111/*Acircumflex*/, 1123/*Atilde*/, 1130/*Adieresis*/, + 1140/*Aring*/, 826/*AE*/, 1146/*Ccedilla*/, 1155/*Egrave*/, 1162/*Eacute*/, + 1169/*Ecircumflex*/, 1181/*Edieresis*/, 1191/*Igrave*/, 1198/*Iacute*/, + 1205/*Icircumflex*/, 1217/*Idieresis*/, 1227/*Eth*/, 1231/*Ntilde*/, + 1238/*Ograve*/, 1245/*Oacute*/, 1252/*Ocircumflex*/, 1264/*Otilde*/, + 1271/*Odieresis*/, 1281/*multiply*/, 848/*Oslash*/, 1290/*Ugrave*/, + 1297/*Uacute*/, 1304/*Ucircumflex*/, 1316/*Udieresis*/, 1326/*Yacute*/, + 1333/*Thorn*/, 900/*germandbls*/, 1339/*agrave*/, 1346/*aacute*/, + 1353/*acircumflex*/, 1365/*atilde*/, 1372/*adieresis*/, 1382/*aring*/, + 871/*ae*/, 1388/*ccedilla*/, 1397/*egrave*/, 1404/*eacute*/, + 1411/*ecircumflex*/, 1423/*edieresis*/, 1433/*igrave*/, 1440/*iacute*/, + 1447/*icircumflex*/, 1459/*idieresis*/, 1469/*eth*/, 1473/*ntilde*/, + 1480/*ograve*/, 1487/*oacute*/, 1494/*ocircumflex*/, 1506/*otilde*/, + 1513/*odieresis*/, 1523/*divide*/, 890/*oslash*/, 1530/*ugrave*/, + 1537/*uacute*/, 1544/*ucircumflex*/, 1556/*udieresis*/, 1566/*yacute*/, + 1573/*thorn*/, 1579/*ydieresis*/, +}; + +const char * +_cairo_ps_standard_encoding_to_glyphname (int glyph) +{ + if (ps_standard_encoding_offset[glyph]) + return glyph_name_symbol + ps_standard_encoding_offset[glyph]; + else + return NULL; +} + +const char * +_cairo_winansi_to_glyphname (int glyph) +{ + if (winansi_encoding_offset[glyph]) + return glyph_name_symbol + winansi_encoding_offset[glyph]; + else + return NULL; +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/gfx/cairo/cairo/src/cairo-type1-subset.c b/gfx/cairo/cairo/src/cairo-type1-subset.c index 20172a30476f..a51e34f62b08 100644 --- a/gfx/cairo/cairo/src/cairo-type1-subset.c +++ b/gfx/cairo/cairo/src/cairo-type1-subset.c @@ -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 © 2006 Red Hat, Inc @@ -39,8 +40,10 @@ */ -#define _BSD_SOURCE /* for snprintf(), strdup() */ +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET @@ -49,30 +52,28 @@ #include "cairo-scaled-font-subsets-private.h" #include "cairo-output-stream-private.h" -/* XXX: Eventually, we need to handle other font backends */ -#if CAIRO_HAS_FT_FONT - -#include "cairo-ft-private.h" - -#include -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_TYPE1_TABLES_H - #include -extern FT_Error mozilla_LoadFTGlyph(FT_Face aFace, uint32_t aGlyphIndex, int32_t aFlags); +#define TYPE1_STACKSIZE 24 /* Defined in Type 1 Font Format */ + + +typedef struct { + int subset_index; + double width; + const char *encrypted_charstring; + int encrypted_charstring_length; +} glyph_data_t; typedef struct _cairo_type1_font_subset { cairo_scaled_font_subset_t *scaled_font_subset; struct { - cairo_unscaled_font_t *unscaled_font; unsigned int font_id; char *base_font; unsigned int num_glyphs; double x_min, y_min, x_max, y_max; double ascent, descent; + double units_per_em; const char *data; unsigned long header_size; @@ -80,121 +81,96 @@ typedef struct _cairo_type1_font_subset { unsigned long trailer_size; } base; - FT_Face face; int num_glyphs; + /* The glyphs and glyph_names arrays are indexed by the order of + * the Charstrings in the font. This is not necessarily the same + * order as the glyph index. The index_to_glyph_name() font backend + * function is used to map the glyph index to the glyph order in + * the Charstrings. */ + + glyph_data_t *glyphs; + char **glyph_names; + cairo_array_t glyphs_array; + cairo_array_t glyph_names_array; + + int num_subrs; + cairo_bool_t subset_subrs; struct { - int subset_index; - double width; - char *name; - } *glyphs; + const char *subr_string; + int subr_length; + const char *np; + int np_length; + cairo_bool_t used; + } *subrs; + + /* Indexed by subset_index this maps to the glyph order in the + * glyph_names and glyphs arrays. Has font->num_golyphs + * elements. */ + int *subset_index_to_glyphs; cairo_output_stream_t *output; cairo_array_t contents; - const char *rd, *nd; + const char *rd, *nd, *np; + + int lenIV; char *type1_data; unsigned int type1_length; char *type1_end; char *header_segment; - int header_segment_size; + unsigned int header_segment_size; char *eexec_segment; - int eexec_segment_size; + unsigned int eexec_segment_size; cairo_bool_t eexec_segment_is_ascii; char *cleartext; char *cleartext_end; - int header_size; + unsigned int header_size; unsigned short eexec_key; cairo_bool_t hex_encode; int hex_column; + + struct { + double stack[TYPE1_STACKSIZE]; + int sp; + } build_stack; + + struct { + int stack[TYPE1_STACKSIZE]; + int sp; + } ps_stack; + + } cairo_type1_font_subset_t; static cairo_status_t _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, - cairo_unscaled_font_t *unscaled_font, + cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t hex_encode) { - cairo_ft_unscaled_font_t *ft_unscaled_font; - cairo_status_t status; - FT_Face face; - PS_FontInfoRec font_info; - int i, j; - - ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font; - - face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); - if (unlikely (face == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (FT_Get_PS_Font_Info(face, &font_info) != 0) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto fail1; - } - - /* OpenType/CFF fonts also have a PS_FontInfoRec */ -#if HAVE_FT_LOAD_SFNT_TABLE - if (FT_IS_SFNT (face)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto fail1; - } -#endif - memset (font, 0, sizeof (*font)); - font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font); - font->base.num_glyphs = face->num_glyphs; - font->base.x_min = face->bbox.xMin / (double)face->units_per_EM; - font->base.y_min = face->bbox.yMin / (double)face->units_per_EM; - font->base.x_max = face->bbox.xMax / (double)face->units_per_EM; - font->base.y_max = face->bbox.yMax / (double)face->units_per_EM; - font->base.ascent = face->ascender / (double)face->units_per_EM; - font->base.descent = face->descender / (double)face->units_per_EM; + font->scaled_font_subset = scaled_font_subset; - if (face->family_name) { - font->base.base_font = strdup (face->family_name); - if (unlikely (font->base.base_font == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } - for (i = 0, j = 0; font->base.base_font[j]; j++) { - if (font->base.base_font[j] == ' ') - continue; - font->base.base_font[i++] = font->base.base_font[j]; - } - font->base.base_font[i] = '\0'; - } - - font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]); - if (unlikely (font->glyphs == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } + _cairo_array_init (&font->glyphs_array, sizeof (glyph_data_t)); + _cairo_array_init (&font->glyph_names_array, sizeof (char *)); + font->subset_index_to_glyphs = NULL; + font->base.num_glyphs = 0; + font->num_subrs = 0; + font->subset_subrs = TRUE; + font->subrs = NULL; font->hex_encode = hex_encode; font->num_glyphs = 0; - for (i = 0; i < face->num_glyphs; i++) - font->glyphs[i].subset_index = -1; _cairo_array_init (&font->contents, sizeof (char)); - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - return CAIRO_STATUS_SUCCESS; - - fail3: - if (font->base.base_font) - free (font->base.base_font); - fail2: - _cairo_unscaled_font_destroy (unscaled_font); - fail1: - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - - return status; } static void @@ -203,7 +179,9 @@ cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph) if (font->glyphs[glyph].subset_index >= 0) return; - font->glyphs[glyph].subset_index = font->num_glyphs++; + font->glyphs[glyph].subset_index = font->num_glyphs; + font->subset_index_to_glyphs[font->num_glyphs] = glyph; + font->num_glyphs++; } static cairo_bool_t @@ -238,25 +216,31 @@ cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) { unsigned char *p; const char *eexec_token; - int size, i; + unsigned int size, i; p = (unsigned char *) font->type1_data; font->type1_end = font->type1_data + font->type1_length; - if (p[0] == 0x80 && p[1] == 0x01) { + if (font->type1_length >= 2 && p[0] == 0x80 && p[1] == 0x01) { + if (font->type1_end < (char *)(p + 6)) + return CAIRO_INT_STATUS_UNSUPPORTED; font->header_segment_size = - p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); + p[2] | (p[3] << 8) | (p[4] << 16) | ((unsigned int) p[5] << 24); font->header_segment = (char *) p + 6; p += 6 + font->header_segment_size; + if (font->type1_end < (char *)(p + 6)) + return CAIRO_INT_STATUS_UNSUPPORTED; font->eexec_segment_size = - p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); + p[2] | (p[3] << 8) | (p[4] << 16) | ((unsigned int) p[5] << 24); font->eexec_segment = (char *) p + 6; font->eexec_segment_is_ascii = (p[1] == 1); p += 6 + font->eexec_segment_size; - while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) { - size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); - p += 6 + size; + while (font->type1_end >= (char *)(p + 6) && p[1] != 0x03) { + size = p[2] | (p[3] << 8) | (p[4] << 16) | ((unsigned int) p[5] << 24); + if (font->type1_end < (char *)(p + 6 + size)) + return CAIRO_INT_STATUS_UNSUPPORTED; + p += 6 + size; } font->type1_end = (char *) p; } else { @@ -319,6 +303,167 @@ cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, } while (start); } +static cairo_status_t +cairo_type1_font_subset_get_matrix (cairo_type1_font_subset_t *font, + const char *name, + double *a, + double *b, + double *c, + double *d) +{ + const char *start, *end, *segment_end; + int ret, s_max, i, j; + char *s; + const char *decimal_point; + int decimal_point_len; + + decimal_point = _cairo_get_locale_decimal_point (); + decimal_point_len = strlen (decimal_point); + + assert (decimal_point_len != 0); + + segment_end = font->header_segment + font->header_segment_size; + start = find_token (font->header_segment, segment_end, name); + if (start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + end = find_token (start, segment_end, "def"); + if (end == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + s_max = end - start + 5*decimal_point_len + 1; + s = _cairo_malloc (s_max); + if (unlikely (s == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + i = 0; + j = 0; + while (i < end - start && j < s_max - decimal_point_len) { + if (start[i] == '.') { + strncpy(s + j, decimal_point, decimal_point_len + 1); + i++; + j += decimal_point_len; + } else { + s[j++] = start[i++]; + } + } + s[j] = 0; + + start = strpbrk (s, "{["); + if (!start) { + free (s); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + start++; + ret = 0; + if (*start) + ret = sscanf(start, "%lf %lf %lf %lf", a, b, c, d); + + free (s); + + if (ret != 4) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_subset_get_bbox (cairo_type1_font_subset_t *font) +{ + cairo_status_t status; + double x_min, y_min, x_max, y_max; + double xx, yx, xy, yy; + + status = cairo_type1_font_subset_get_matrix (font, "/FontBBox", + &x_min, + &y_min, + &x_max, + &y_max); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_get_matrix (font, "/FontMatrix", + &xx, &yx, &xy, &yy); + if (unlikely (status)) + return status; + + if (yy == 0.0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Freetype uses 1/yy to get units per EM */ + font->base.units_per_em = 1.0/yy; + + /* If the FontMatrix is not a uniform scale the metrics we extract + * from the font won't match what FreeType returns */ + if (xx != yy || yx != 0.0 || xy != 0.0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->base.x_min = x_min / font->base.units_per_em; + font->base.y_min = y_min / font->base.units_per_em; + font->base.x_max = x_max / font->base.units_per_em; + font->base.y_max = y_max / font->base.units_per_em; + font->base.ascent = font->base.y_max; + font->base.descent = font->base.y_min; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font) +{ + const char *start, *end, *segment_end; + char *s; + int i; + cairo_status_t status; + + segment_end = font->header_segment + font->header_segment_size; + start = find_token (font->header_segment, segment_end, "/FontName"); + if (start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + start += strlen ("/FontName"); + + end = find_token (start, segment_end, "def"); + if (end == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + while (end > start && _cairo_isspace(end[-1])) + end--; + + s = _cairo_malloc (end - start + 1); + if (unlikely (s == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + strncpy (s, start, end - start); + s[end - start] = 0; + + start = strchr (s, '/'); + if (!start++ || !start) { + free (s); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* If font name is prefixed with a subset tag, strip it off. */ + if (strlen(start) > 7 && start[6] == '+') { + for (i = 0; i < 6; i++) { + if (start[i] < 'A' || start[i] > 'Z') + break; + } + if (i == 6) + start += 7; + } + + font->base.base_font = strdup (start); + free (s); + if (unlikely (font->base.base_font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_escape_ps_name (&font->base.base_font); + + return status; +} + static cairo_status_t cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, const char *name) @@ -393,13 +538,26 @@ cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, _cairo_output_stream_printf (font->output, "/Encoding 256 array\n" "0 1 255 {1 index exch /.notdef put} for\n"); - for (i = 1; i < font->base.num_glyphs; i++) { - if (font->glyphs[i].subset_index < 0) - continue; - _cairo_output_stream_printf (font->output, - "dup %d /%s put\n", - font->glyphs[i].subset_index, - font->glyphs[i].name); + if (font->scaled_font_subset->is_latin) { + for (i = 1; i < 256; i++) { + int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i]; + + if (subset_glyph > 0) { + _cairo_output_stream_printf (font->output, + "dup %d /%s put\n", + i, + _cairo_winansi_to_glyphname (i)); + } + } + } else { + for (i = 0; i < font->base.num_glyphs; i++) { + if (font->glyphs[i].subset_index <= 0) + continue; + _cairo_output_stream_printf (font->output, + "dup %d /%s put\n", + font->glyphs[i].subset_index, + font->glyph_names[i]); + } } _cairo_output_stream_printf (font->output, "readonly def"); @@ -408,6 +566,10 @@ cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, return CAIRO_INT_STATUS_UNSUPPORTED; end += 3; + /* There are some buggy fonts that contain more than one /Encoding */ + if (find_token (end, segment_end, "/Encoding")) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_output_stream_write (font->output, end, segment_end - end); return font->output->status; @@ -473,7 +635,7 @@ cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) in = (unsigned char *) font->eexec_segment; end = (unsigned char *) in + font->eexec_segment_size; - font->cleartext = malloc (font->eexec_segment_size); + font->cleartext = _cairo_malloc (font->eexec_segment_size + 1); if (unlikely (font->cleartext == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -511,6 +673,9 @@ cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) for (i = 0; i < 4 && i < font->eexec_segment_size; i++) font->cleartext[i] = ' '; + /* Ensure strtol() can not scan past the end of the cleartext */ + font->cleartext[font->eexec_segment_size] = 0; + return CAIRO_STATUS_SUCCESS; } @@ -529,64 +694,6 @@ skip_token (const char *p, const char *end) return p; } -static int -cairo_type1_font_subset_lookup_glyph (cairo_type1_font_subset_t *font, - const char *glyph_name, int length) -{ - unsigned int i; - - for (i = 0; i < font->base.num_glyphs; i++) { - if (font->glyphs[i].name && - strncmp (font->glyphs[i].name, glyph_name, length) == 0 && - font->glyphs[i].name[length] == '\0') - return i; - } - - return -1; -} - -static cairo_status_t -cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *font) -{ - unsigned int i; - char buffer[256]; - FT_Error error; - - /* Get glyph names and width using the freetype API */ - for (i = 0; i < font->base.num_glyphs; i++) { - if (font->glyphs[i].name != NULL) - continue; - - error = mozilla_LoadFTGlyph (font->face, i, - FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | - FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); - if (error != FT_Err_Ok) { - /* propagate fatal errors from FreeType */ - if (error == FT_Err_Out_Of_Memory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - font->glyphs[i].width = font->face->glyph->metrics.horiAdvance / (double)font->face->units_per_EM; - - error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer); - if (error != FT_Err_Ok) { - /* propagate fatal errors from FreeType */ - if (error == FT_Err_Out_Of_Memory) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - font->glyphs[i].name = strdup (buffer); - if (unlikely (font->glyphs[i].name == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - return CAIRO_STATUS_SUCCESS; -} - static void cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out) { @@ -613,238 +720,81 @@ cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer) *integer = -(p[0] - 251) * 256 - p[1] - 108; p += 2; } else { - *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; + *integer = ((uint32_t)p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; p += 5; } return p; } -#if 0 -/* - * The two tables that follow are generated using this perl code: - */ - -@encoding = ( - /* 0 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 16 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 32 */ - "space", "exclam", "quotedbl", "numbersign", - "dollar", "percent", "ampersand", "quoteright", - "parenleft", "parenright", "asterisk", "plus", - "comma", "hyphen", "period", "slash", - /* 48 */ - "zero", "one", "two", "three", - "four", "five", "six", "seven", - "eight", "nine", "colon", "semicolon", - "less", "equal", "greater", "question", - /* 64 */ - "at", "A", "B", "C", - "D", "E", "F", "G", - "H", "I", "J", "K", - "L", "M", "N", "O", - /* 80 */ - "P", "Q", "R", "S", - "T", "U", "V", "W", - "X", "Y", "Z", "bracketleft", - "backslash", "bracketright", "asciicircum", "underscore", - /* 96 */ - "quoteleft", "a", "b", "c", - "d", "e", "f", "g", - "h", "i", "j", "k", - "l", "m", "n", "o", - /* 112 */ - "p", "q", "r", "s", - "t", "u", "v", "w", - "x", "y", "z", "braceleft", - "bar", "braceright", "asciitilde", NULL, - /* 128 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 144 */ - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 160 */ - NULL, "exclamdown", "cent", "sterling", - "fraction", "yen", "florin", "section", - "currency", "quotesingle", "quotedblleft", "guillemotleft", - "guilsinglleft","guilsinglright","fi", "fl", - /* 176 */ - NULL, "endash", "dagger", "daggerdbl", - "periodcentered",NULL, "paragraph", "bullet", - "quotesinglbase","quotedblbase","quotedblright","guillemotright", - "ellipsis", "perthousand", NULL, "questiondown", - /* 192 */ - NULL, "grave", "acute", "circumflex", - "tilde", "macron", "breve", "dotaccent", - "dieresis", NULL, "ring", "cedilla", - NULL, "hungarumlaut", "ogonek", "caron", - /* 208 */ - "emdash", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - /* 224 */ - NULL, "AE", NULL, "ordfeminine", - NULL, NULL, NULL, NULL, - "Lslash", "Oslash", "OE", "ordmasculine", - NULL, NULL, NULL, NULL, - /* 240 */ - NULL, "ae", NULL, NULL, - NULL, "dotlessi", NULL, NULL, - "lslash", "oslash", "oe", "germandbls", - NULL, NULL, NULL, NULL - ); - -print "static const char ps_standard_encoding_symbol[] = {\n"; -$s = qq( "\\0"); -for $sym (@encoding) { - if (! ($sym eq NULL)) { - $ss = qq( "$sym\\0"); - if (length($s) + length($ss) > 78) { - print qq( $s\n); - $s = ""; - } - $s .= $ss; - } -} -print qq( $s\n); -print "};\n\n"; -print "static const int16_t ps_standard_encoding_offset[256] = {\n"; -$offset = 1; -$s = qq(); -for $sym (@encoding) { - if (! ($sym eq NULL)) { - $ss = qq( $offset/*$sym*/,); - $offset += length($sym) + 1; - } else { - $ss = qq( 0,); - } - if (length($s) + length($ss) > 78) { - print qq( $s\n); - $s = ""; - } - $s .= $ss; -} -print qq( $s\n); -print "};\n"; -exit; -#endif - -static const char ps_standard_encoding_symbol[] = { - "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0" - "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0" - "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0" - "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0" - "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0" - "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0" - "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0" - "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0" - "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0" - "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0" - "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0" - "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0" - "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0" - "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0" - "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0" - "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0" - "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0" - "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0" - "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0" - "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0" - "oslash\0" "oe\0" "germandbls\0" -}; - -static const int16_t ps_standard_encoding_offset[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/, - 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/, - 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/, - 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/, - 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/, - 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/, - 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/, - 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/, - 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/, - 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/, - 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/, - 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/, - 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/, - 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/, - 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/, - 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/, - 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/, - 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/, - 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/, - 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/, - 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/, - 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/, - 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0, - 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/, - 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/, - 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/, - 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/, - 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0, - 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/, - 900/*germandbls*/, 0, 0, 0, 0, -}; - -#define ps_standard_encoding(index) ((index) ? ps_standard_encoding_symbol+ps_standard_encoding_offset[(index)] : NULL) - static cairo_status_t use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index) { const char *glyph_name; + unsigned int i; if (index < 0 || index > 255) return CAIRO_STATUS_SUCCESS; - glyph_name = ps_standard_encoding(index); + glyph_name = _cairo_ps_standard_encoding_to_glyphname (index); if (glyph_name == NULL) return CAIRO_STATUS_SUCCESS; - index = cairo_type1_font_subset_lookup_glyph (font, - glyph_name, - strlen(glyph_name)); - if (index < 0) - return CAIRO_INT_STATUS_UNSUPPORTED; + for (i = 0; i < font->base.num_glyphs; i++) { + if (font->glyph_names[i] && strcmp (font->glyph_names[i], glyph_name) == 0) { + cairo_type1_font_subset_use_glyph (font, i); - cairo_type1_font_subset_use_glyph (font, index); + return CAIRO_STATUS_SUCCESS; + } + } - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } -#define TYPE1_CHARSTRING_COMMAND_ESCAPE (12) -#define TYPE1_CHARSTRING_COMMAND_SEAC (32 + 6) +#define TYPE1_CHARSTRING_COMMAND_HSTEM 0x01 +#define TYPE1_CHARSTRING_COMMAND_VSTEM 0x03 +#define TYPE1_CHARSTRING_COMMAND_VMOVETO 0x04 +#define TYPE1_CHARSTRING_COMMAND_RLINETO 0x05 +#define TYPE1_CHARSTRING_COMMAND_HLINETO 0x06 +#define TYPE1_CHARSTRING_COMMAND_VLINETO 0x07 +#define TYPE1_CHARSTRING_COMMAND_RRCURVETO 0x08 +#define TYPE1_CHARSTRING_COMMAND_CLOSEPATH 0x09 +#define TYPE1_CHARSTRING_COMMAND_CALLSUBR 0x0a +#define TYPE1_CHARSTRING_COMMAND_RETURN 0x0b +#define TYPE1_CHARSTRING_COMMAND_ESCAPE 0x0c +#define TYPE1_CHARSTRING_COMMAND_HSBW 0x0d +#define TYPE1_CHARSTRING_COMMAND_ENDCHAR 0x0e +#define TYPE1_CHARSTRING_COMMAND_RMOVETO 0x15 +#define TYPE1_CHARSTRING_COMMAND_HMOVETO 0x16 +#define TYPE1_CHARSTRING_COMMAND_VHCURVETO 0x1e +#define TYPE1_CHARSTRING_COMMAND_HVCURVETO 0x1f +#define TYPE1_CHARSTRING_COMMAND_DOTSECTION 0x0c00 +#define TYPE1_CHARSTRING_COMMAND_VSTEM3 0x0c01 +#define TYPE1_CHARSTRING_COMMAND_HSTEM3 0x0c02 +#define TYPE1_CHARSTRING_COMMAND_SEAC 0x0c06 +#define TYPE1_CHARSTRING_COMMAND_SBW 0x0c07 +#define TYPE1_CHARSTRING_COMMAND_DIV 0x0c0c +#define TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR 0x0c10 +#define TYPE1_CHARSTRING_COMMAND_POP 0x0c11 +#define TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT 0x0c21 + +/* Parse the charstring, including recursing into subroutines. Find + * the glyph width, subroutines called, and glyphs required by the + * SEAC operator. */ static cairo_status_t -cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font, - const char *name, int name_length, - const char *encrypted_charstring, int encrypted_charstring_length) +cairo_type1_font_subset_parse_charstring (cairo_type1_font_subset_t *font, + int glyph, + const char *encrypted_charstring, + int encrypted_charstring_length) { cairo_status_t status; unsigned char *charstring; const unsigned char *end; const unsigned char *p; - int stack[5], sp, value; int command; - charstring = malloc (encrypted_charstring_length); + charstring = _cairo_malloc (encrypted_charstring_length); if (unlikely (charstring == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -853,64 +803,367 @@ cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font, encrypted_charstring_length, charstring); end = charstring + encrypted_charstring_length; - - p = charstring + 4; - sp = 0; - + p = charstring + font->lenIV; + status = CAIRO_STATUS_SUCCESS; while (p < end) { if (*p < 32) { command = *p++; - - if (command == TYPE1_CHARSTRING_COMMAND_ESCAPE) - command = 32 + *p++; - switch (command) { - case TYPE1_CHARSTRING_COMMAND_SEAC: - /* The seac command takes five integer arguments. The - * last two are glyph indices into the PS standard - * encoding give the names of the glyphs that this - * glyph is composed from. All we need to do is to - * make sure those glyphs are present in the subset - * under their standard names. */ - if (unlikely (sp < 5)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = use_standard_encoding_glyph (font, stack[3]); - if (unlikely (status)) - return status; - - status = use_standard_encoding_glyph (font, stack[4]); - if (unlikely (status)) - return status; - - sp = 0; + case TYPE1_CHARSTRING_COMMAND_HSTEM: + case TYPE1_CHARSTRING_COMMAND_VSTEM: + case TYPE1_CHARSTRING_COMMAND_VMOVETO: + case TYPE1_CHARSTRING_COMMAND_RLINETO: + case TYPE1_CHARSTRING_COMMAND_HLINETO: + case TYPE1_CHARSTRING_COMMAND_VLINETO: + case TYPE1_CHARSTRING_COMMAND_RRCURVETO: + case TYPE1_CHARSTRING_COMMAND_CLOSEPATH: + case TYPE1_CHARSTRING_COMMAND_RMOVETO: + case TYPE1_CHARSTRING_COMMAND_HMOVETO: + case TYPE1_CHARSTRING_COMMAND_VHCURVETO: + case TYPE1_CHARSTRING_COMMAND_HVCURVETO: + case TYPE1_CHARSTRING_COMMAND_RETURN: + case TYPE1_CHARSTRING_COMMAND_ENDCHAR: + default: + /* stack clearing operator */ + font->build_stack.sp = 0; break; - default: - sp = 0; + case TYPE1_CHARSTRING_COMMAND_CALLSUBR: + if (font->subset_subrs && font->build_stack.sp > 0) { + double int_val; + if (modf(font->build_stack.stack[--font->build_stack.sp], &int_val) == 0.0) { + int subr_num = int_val; + if (subr_num >= 0 && subr_num < font->num_subrs) { + font->subrs[subr_num].used = TRUE; + status = cairo_type1_font_subset_parse_charstring ( + font, + glyph, + font->subrs[subr_num].subr_string, + font->subrs[subr_num].subr_length); + break; + } + } + } + font->subset_subrs = FALSE; + break; + + case TYPE1_CHARSTRING_COMMAND_HSBW: + if (font->build_stack.sp < 2) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + font->glyphs[glyph].width = font->build_stack.stack[1]/font->base.units_per_em; + font->build_stack.sp = 0; + break; + + case TYPE1_CHARSTRING_COMMAND_ESCAPE: + command = command << 8 | *p++; + switch (command) { + case TYPE1_CHARSTRING_COMMAND_DOTSECTION: + case TYPE1_CHARSTRING_COMMAND_VSTEM3: + case TYPE1_CHARSTRING_COMMAND_HSTEM3: + case TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT: + default: + /* stack clearing operator */ + font->build_stack.sp = 0; + break; + + case TYPE1_CHARSTRING_COMMAND_SEAC: + /* The seac command takes five integer arguments. The + * last two are glyph indices into the PS standard + * encoding give the names of the glyphs that this + * glyph is composed from. All we need to do is to + * make sure those glyphs are present in the subset + * under their standard names. */ + if (font->build_stack.sp < 5) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + status = use_standard_encoding_glyph (font, font->build_stack.stack[3]); + if (unlikely (status)) + goto cleanup; + + status = use_standard_encoding_glyph (font, font->build_stack.stack[4]); + if (unlikely (status)) + goto cleanup; + + font->build_stack.sp = 0; + break; + + case TYPE1_CHARSTRING_COMMAND_SBW: + if (font->build_stack.sp < 4) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + font->glyphs[glyph].width = font->build_stack.stack[2]/font->base.units_per_em; + font->build_stack.sp = 0; + break; + + case TYPE1_CHARSTRING_COMMAND_DIV: + if (font->build_stack.sp < 2) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } else { + double num1 = font->build_stack.stack[font->build_stack.sp - 2]; + double num2 = font->build_stack.stack[font->build_stack.sp - 1]; + font->build_stack.sp--; + if (num2 == 0.0) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + font->build_stack.stack[font->build_stack.sp - 1] = num1/num2; + } + break; + + case TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR: + if (font->build_stack.sp < 1) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + font->build_stack.sp--; + font->ps_stack.sp = 0; + while (font->build_stack.sp) + font->ps_stack.stack[font->ps_stack.sp++] = font->build_stack.stack[--font->build_stack.sp]; + + break; + + case TYPE1_CHARSTRING_COMMAND_POP: + if (font->ps_stack.sp < 1) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + + /* T1 spec states that if the interpreter does not + * support executing the callothersub, the results + * must be taken from the callothersub arguments. */ + font->build_stack.stack[font->build_stack.sp++] = font->ps_stack.stack[--font->ps_stack.sp]; + break; + } break; } - } else { + } else { /* integer argument */ - p = cairo_type1_font_subset_decode_integer (p, &value); - if (sp < 5) - stack[sp++] = value; - } + if (font->build_stack.sp < TYPE1_STACKSIZE) { + int val; + p = cairo_type1_font_subset_decode_integer (p, &val); + font->build_stack.stack[font->build_stack.sp++] = val; + } else { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup; + } + } } +cleanup: free (charstring); + return status; +} + +static cairo_status_t +cairo_type1_font_subset_build_subr_list (cairo_type1_font_subset_t *font, + int subr_number, + const char *encrypted_charstring, int encrypted_charstring_length, + const char *np, int np_length) +{ + + font->subrs[subr_number].subr_string = encrypted_charstring; + font->subrs[subr_number].subr_length = encrypted_charstring_length; + font->subrs[subr_number].np = np; + font->subrs[subr_number].np_length = np_length; + return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +write_used_subrs (cairo_type1_font_subset_t *font, + int subr_number, + const char *subr_string, int subr_string_length, + const char *np, int np_length) +{ + cairo_status_t status; + char buffer[256]; + int length; + + if (!font->subrs[subr_number].used) + return CAIRO_STATUS_SUCCESS; + + length = snprintf (buffer, sizeof buffer, + "dup %d %d %s ", + subr_number, subr_string_length, font->rd); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_write_encrypted (font, + subr_string, + subr_string_length); + if (unlikely (status)) + return status; + + if (np) { + status = cairo_type1_font_subset_write_encrypted (font, np, np_length); + } else { + length = snprintf (buffer, sizeof buffer, "%s\n", font->np); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + } + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +typedef cairo_status_t (*subr_func_t) (cairo_type1_font_subset_t *font, + int subr_number, + const char *subr_string, int subr_string_length, + const char *np, int np_length); + +static cairo_status_t +cairo_type1_font_for_each_subr (cairo_type1_font_subset_t *font, + const char *array_start, + const char *cleartext_end, + subr_func_t func, + const char **array_end) +{ + const char *p, *subr_string; + char *end; + int subr_num, subr_length; + const char *np; + int np_length; + cairo_status_t status; + + /* We're looking at "dup" at the start of the first subroutine. The subroutines + * definitions are on the form: + * + * dup 5 23 RD <23 binary bytes> NP + * + * or alternatively using -| and |- instead of RD and ND. + * The first number is the subroutine number. + */ + + p = array_start; + while (p + 3 < cleartext_end && strncmp (p, "dup", 3) == 0) { + p = skip_token (p, cleartext_end); + + /* get subr number */ + subr_num = strtol (p, &end, 10); + if (p == end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (subr_num < 0 || subr_num >= font->num_subrs) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* get subr length */ + p = end; + subr_length = strtol (p, &end, 10); + if (p == end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Skip past -| or RD to binary data. There is exactly one space + * between the -| or RD token and the encrypted data, thus '+ 1'. */ + subr_string = skip_token (end, cleartext_end) + 1; + + np = NULL; + np_length = 0; + + /* Skip binary data and | or NP token. */ + p = skip_token (subr_string + subr_length, cleartext_end); + while (p < cleartext_end && _cairo_isspace(*p)) + p++; + + /* Some fonts have "noaccess put" instead of "NP" */ + if (p + 3 < cleartext_end && strncmp (p, "put", 3) == 0) { + p = skip_token (p, cleartext_end); + while (p < cleartext_end && _cairo_isspace(*p)) + p++; + + np = subr_string + subr_length; + np_length = p - np; + } + + status = func (font, subr_num, + subr_string, subr_length, np, np_length); + if (unlikely (status)) + return status; + + } + + *array_end = (char *) p; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_subset_build_glyph_list (cairo_type1_font_subset_t *font, + int glyph_number, + const char *name, int name_length, + const char *encrypted_charstring, int encrypted_charstring_length) +{ + char *s; + glyph_data_t glyph; + cairo_status_t status; + + s = _cairo_malloc (name_length + 1); + if (unlikely (s == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + strncpy (s, name, name_length); + s[name_length] = 0; + + status = _cairo_array_append (&font->glyph_names_array, &s); + if (unlikely (status)) + return status; + + glyph.subset_index = -1; + glyph.width = 0; + glyph.encrypted_charstring = encrypted_charstring; + glyph.encrypted_charstring_length = encrypted_charstring_length; + status = _cairo_array_append (&font->glyphs_array, &glyph); + + return status; +} + static cairo_status_t write_used_glyphs (cairo_type1_font_subset_t *font, + int glyph_number, const char *name, int name_length, const char *charstring, int charstring_length) { cairo_status_t status; char buffer[256]; int length; + unsigned int subset_id; + int ch; + const char *wa_name; + + if (font->glyphs[glyph_number].subset_index < 0) + return CAIRO_STATUS_SUCCESS; + + if (font->scaled_font_subset->is_latin) { + /* When using the WinAnsi encoding in PDF, the /Encoding array + * is ignored and instead glyphs are keyed by glyph names. To + * ensure correct rendering we replace the glyph name in the + * font with the standard name. + **/ + subset_id = font->glyphs[glyph_number].subset_index; + /* Any additional glyph included for use by the seac operator + * will either have subset_id >= font->scaled_font_subset->num_glyphs + * or will not map to a winansi name (wa_name = NULL). In this + * case the original name is used. + */ + if (subset_id > 0 && subset_id < font->scaled_font_subset->num_glyphs) { + ch = font->scaled_font_subset->to_latin_char[subset_id]; + wa_name = _cairo_winansi_to_glyphname (ch); + if (wa_name) { + name = wa_name; + name_length = strlen(name); + } + } + } length = snprintf (buffer, sizeof buffer, "/%.*s %d %s ", @@ -934,6 +1187,7 @@ write_used_glyphs (cairo_type1_font_subset_t *font, } typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font, + int glyph_number, const char *name, int name_length, const char *charstring, int charstring_length); @@ -944,9 +1198,11 @@ cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, glyph_func_t func, const char **dict_out) { - int charstring_length, name_length, glyph_index; + int charstring_length, name_length; const char *p, *charstring, *name; char *end; + cairo_status_t status; + int glyph_count; /* We're looking at '/' in the name of the first glyph. The glyph * definitions are on the form: @@ -963,7 +1219,7 @@ cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, */ p = dict_start; - + glyph_count = 0; while (*p == '/') { name = p + 1; p = skip_token (p, dict_end); @@ -987,15 +1243,11 @@ cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, if (p == dict_end) return CAIRO_INT_STATUS_UNSUPPORTED; - glyph_index = cairo_type1_font_subset_lookup_glyph (font, - name, name_length); - if (font->glyphs[glyph_index].subset_index >= 0) { - cairo_status_t status = func (font, - name, name_length, - charstring, charstring_length); - if (unlikely (status)) - return status; - } + status = func (font, glyph_count++, + name, name_length, + charstring, charstring_length); + if (unlikely (status)) + return status; } *dict_out = p; @@ -1009,34 +1261,110 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, const char *name) { cairo_status_t status; - const char *p, *charstrings, *dict_start; - const char *closefile_token; - char buffer[32], *glyph_count_end; - int num_charstrings, length; + const char *p, *subrs, *charstrings, *array_start, *array_end, *dict_start, *dict_end; + const char *lenIV_start, *lenIV_end, *closefile_token; + char buffer[32], *lenIV_str, *subr_count_end, *glyph_count_end; + int ret, lenIV, length; + const cairo_scaled_font_backend_t *backend; + unsigned int i; + int glyph, j; /* The private dict holds hint information, common subroutines and * the actual glyph definitions (charstrings). * - * FIXME: update this comment. + * What we do here is scan directly to the /Subrs token, which + * marks the beginning of the subroutines. We read in all the + * subroutines, then move on to the /CharString token, which marks + * the beginning of the glyph definitions, and read in the charstrings. * - * What we do here is scan directly the /CharString token, which - * marks the beginning of the glyph definitions. Then we parse - * through the glyph definitions and weed out the glyphs not in - * our subset. Everything else before and after the glyph - * definitions is copied verbatim to the output. It might be - * worthwile to figure out which of the common subroutines are - * used by the glyphs in the subset and get rid of the rest. */ + * The charstrings are parsed to extract glyph widths, work out + * which subroutines are called, and to see if any extra glyphs + * need to be included due to the use of the seac glyph combining + * operator. + * + * Finally, the private dict is copied to the subset font minus the + * subroutines and charstrings not required. + */ - /* FIXME: The /Subrs array contains binary data and could - * conceivably have "/CharStrings" in it, so we might need to skip - * this more cleverly. */ - charstrings = find_token (font->cleartext, font->cleartext_end, "/CharStrings"); + /* Determine lenIV, the number of random characters at the start of + each encrypted charstring. The default is 4, but this can be + overridden in the private dict. */ + font->lenIV = 4; + if ((lenIV_start = find_token (font->cleartext, font->cleartext_end, "/lenIV")) != NULL) { + lenIV_start += 6; + lenIV_end = find_token (lenIV_start, font->cleartext_end, "def"); + if (lenIV_end == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + lenIV_str = _cairo_malloc (lenIV_end - lenIV_start + 1); + if (unlikely (lenIV_str == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + strncpy (lenIV_str, lenIV_start, lenIV_end - lenIV_start); + lenIV_str[lenIV_end - lenIV_start] = 0; + + ret = sscanf(lenIV_str, "%d", &lenIV); + free(lenIV_str); + + if (unlikely (ret <= 0)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Apparently some fonts signal unencrypted charstrings with a negative lenIV, + though this is not part of the Type 1 Font Format specification. See, e.g. + http://lists.gnu.org/archive/html/freetype-devel/2000-06/msg00064.html. */ + if (unlikely (lenIV < 0)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->lenIV = lenIV; + } + + /* Find start of Subrs */ + subrs = find_token (font->cleartext, font->cleartext_end, "/Subrs"); + if (subrs == NULL) { + font->subset_subrs = FALSE; + p = font->cleartext; + array_start = NULL; + goto skip_subrs; + } + + /* Scan past /Subrs and get the array size. */ + p = subrs + strlen ("/Subrs"); + font->num_subrs = strtol (p, &subr_count_end, 10); + if (subr_count_end == p) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (font->num_subrs <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->subrs = calloc (font->num_subrs, sizeof (font->subrs[0])); + if (unlikely (font->subrs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* look for "dup" which marks the beginning of the first subr */ + array_start = find_token (subr_count_end, font->cleartext_end, "dup"); + if (array_start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Read in the subroutines */ + status = cairo_type1_font_for_each_subr (font, + array_start, + font->cleartext_end, + cairo_type1_font_subset_build_subr_list, + &array_end); + if (unlikely(status)) + return status; + + p = array_end; +skip_subrs: + + /* Find start of CharStrings */ + charstrings = find_token (p, font->cleartext_end, "/CharStrings"); if (charstrings == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* Scan past /CharStrings and the integer following it. */ p = charstrings + strlen ("/CharStrings"); - num_charstrings = strtol (p, &glyph_count_end, 10); + strtol (p, &glyph_count_end, 10); if (p == glyph_count_end) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1049,29 +1377,70 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, return CAIRO_INT_STATUS_UNSUPPORTED; dict_start = p; - status = cairo_type1_font_subset_get_glyph_names_and_widths (font); - if (unlikely (status)) - return status; - /* Now that we have the private dictionary broken down in * sections, do the first pass through the glyph definitions to - * figure out which subrs and othersubrs are use and which extra - * glyphs may be required by the seac operator. */ + * build a list of glyph names and charstrings. */ status = cairo_type1_font_subset_for_each_glyph (font, dict_start, font->cleartext_end, - cairo_type1_font_subset_look_for_seac, - &p); - if (unlikely (status)) + cairo_type1_font_subset_build_glyph_list, + &dict_end); + if (unlikely(status)) return status; - closefile_token = find_token (p, font->cleartext_end, "closefile"); - if (closefile_token == NULL) + font->glyphs = _cairo_array_index (&font->glyphs_array, 0); + font->glyph_names = _cairo_array_index (&font->glyph_names_array, 0); + font->base.num_glyphs = _cairo_array_num_elements (&font->glyphs_array); + font->subset_index_to_glyphs = calloc (font->base.num_glyphs, sizeof font->subset_index_to_glyphs[0]); + if (unlikely (font->subset_index_to_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + backend = font->scaled_font_subset->scaled_font->backend; + if (!backend->index_to_glyph_name) return CAIRO_INT_STATUS_UNSUPPORTED; - status = cairo_type1_font_subset_get_glyph_names_and_widths (font); - if (unlikely (status)) - return status; + /* Find the glyph number corresponding to each glyph in the subset + * and mark it as in use */ + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + unsigned long index; + + status = backend->index_to_glyph_name (font->scaled_font_subset->scaled_font, + font->glyph_names, + font->base.num_glyphs, + font->scaled_font_subset->glyphs[i], + &index); + if (unlikely(status)) + return status; + + cairo_type1_font_subset_use_glyph (font, index); + } + + /* Go through the charstring of each glyph in use, get the glyph + * width and figure out which extra glyphs may be required by the + * seac operator (which may cause font->num_glyphs to increase + * while this loop is executing). Also subset the Subrs. */ + for (j = 0; j < font->num_glyphs; j++) { + glyph = font->subset_index_to_glyphs[j]; + font->build_stack.sp = 0; + font->ps_stack.sp = 0; + status = cairo_type1_font_subset_parse_charstring (font, + glyph, + font->glyphs[glyph].encrypted_charstring, + font->glyphs[glyph].encrypted_charstring_length); + if (unlikely (status)) + return status; + } + + /* Always include the first five subroutines in case the Flex/hint mechanism is + * being used. */ + for (j = 0; j < MIN (font->num_subrs, 5); j++) { + font->subrs[j].used = TRUE; + } + + closefile_token = find_token (dict_end, font->cleartext_end, "closefile"); + if (closefile_token == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; /* We're ready to start outputting. First write the header, * i.e. the public part of the font dict.*/ @@ -1081,11 +1450,32 @@ cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, font->base.header_size = _cairo_output_stream_get_position (font->output); + /* Start outputting the private dict */ + if (font->subset_subrs) { + /* First output everything up to the start of the Subrs array. */ + status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, + array_start - font->cleartext); + if (unlikely (status)) + return status; - /* Start outputting the private dict. First output everything up - * to the /CharStrings token. */ - status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, - charstrings - font->cleartext); + /* Write out the subr definitions for each of the glyphs in + * the subset. */ + status = cairo_type1_font_for_each_subr (font, + array_start, + font->cleartext_end, + write_used_subrs, + &p); + if (unlikely (status)) + return status; + } else { + p = font->cleartext; + } + + /* If subr subsetting, output everything from end of subrs to + * start of /CharStrings token. If not subr subsetting, output + * everything start of private dict to start of /CharStrings + * token. */ + status = cairo_type1_font_subset_write_encrypted (font, p, charstrings - p); if (unlikely (status)) return status; @@ -1146,12 +1536,15 @@ cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) _cairo_output_stream_write (font->output, cleartomark_token, font->type1_end - cleartomark_token); + if (*(font->type1_end - 1) != '\n') + _cairo_output_stream_printf (font->output, "\n"); + } else if (!font->eexec_segment_is_ascii) { /* Fonts embedded in PDF may omit the fixed-content portion * that includes the 'cleartomark' operator. Type 1 in PDF is * always binary. */ - _cairo_output_stream_printf (font->output, "cleartomark"); + _cairo_output_stream_printf (font->output, "cleartomark\n"); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -1188,9 +1581,11 @@ cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) { font->rd = "-|"; font->nd = "|-"; + font->np = "|"; } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) { font->rd = "RD"; font->nd = "ND"; + font->np = "NP"; } else { /* Don't know *what* kind of font this is... */ return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1199,6 +1594,14 @@ cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; font->hex_column = 0; + status = cairo_type1_font_subset_get_bbox (font); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_get_fontname (font); + if (unlikely (status)) + return status; + status = cairo_type1_font_subset_write_private_dict (font, name); if (unlikely (status)) return status; @@ -1217,63 +1620,66 @@ cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, return CAIRO_STATUS_SUCCESS; } +static cairo_bool_t +check_fontdata_is_type1 (const unsigned char *data, long length) +{ + /* Test for Type 1 Binary (PFB) */ + if (length > 2 && data[0] == 0x80 && data[1] == 0x01) + return TRUE; + + /* Test for Type 1 1 ASCII (PFA) */ + if (length > 2 && data[0] == '%' && data[1] == '!') + return TRUE; + + return FALSE; +} + static cairo_status_t cairo_type1_font_subset_generate (void *abstract_font, const char *name) { cairo_type1_font_subset_t *font = abstract_font; - cairo_ft_unscaled_font_t *ft_unscaled_font; - unsigned long ret; + cairo_scaled_font_t *scaled_font; cairo_status_t status; + unsigned long data_length; - ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font; - font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font); - if (unlikely (font->face == NULL)) + scaled_font = font->scaled_font_subset->scaled_font; + if (!scaled_font->backend->load_type1_data) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &data_length); + if (status) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->type1_length = data_length; + font->type1_data = _cairo_malloc (font->type1_length); + if (unlikely (font->type1_data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font->type1_length = font->face->stream->size; - font->type1_data = malloc (font->type1_length); - if (unlikely (font->type1_data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail; - } + status = scaled_font->backend->load_type1_data (scaled_font, 0, + (unsigned char *) font->type1_data, + &data_length); + if (unlikely (status)) + return status; - if (font->face->stream->read != NULL) { - /* Note that read() may be implemented as a macro, thanks POSIX!, so we - * need to wrap the following usage in parentheses in order to - * disambiguate it for the pre-processor - using the verbose function - * pointer dereference for clarity. - */ - ret = (* font->face->stream->read) (font->face->stream, 0, - (unsigned char *) font->type1_data, - font->type1_length); - if (ret != font->type1_length) { - status = _cairo_error (CAIRO_STATUS_READ_ERROR); - goto fail; - } - } else { - memcpy (font->type1_data, - font->face->stream->base, font->type1_length); - } + if (!check_fontdata_is_type1 ((unsigned char *)font->type1_data, data_length)) + return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_array_grow_by (&font->contents, 4096); if (unlikely (status)) - goto fail; + return status; font->output = _cairo_output_stream_create (type1_font_write, NULL, font); if (unlikely ((status = font->output->status))) - goto fail; + return status; status = cairo_type1_font_subset_write (font, name); if (unlikely (status)) - goto fail; + return status; font->base.data = _cairo_array_index (&font->contents, 0); - fail: - _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font); - return status; } @@ -1289,19 +1695,25 @@ _cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) _cairo_array_fini (&font->contents); free (font->type1_data); - if (font->glyphs != NULL) { - for (i = 0; i < font->base.num_glyphs; i++) - free (font->glyphs[i].name); - } + for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) { + char **s; - _cairo_unscaled_font_destroy (font->base.unscaled_font); + s = _cairo_array_index (&font->glyph_names_array, i); + free (*s); + } + _cairo_array_fini (&font->glyph_names_array); + _cairo_array_fini (&font->glyphs_array); + + free (font->subrs); if (font->output != NULL) status = _cairo_output_stream_destroy (font->output); - if (font->base.base_font) - free (font->base.base_font); - free (font->glyphs); + free (font->base.base_font); + + free (font->subset_index_to_glyphs); + + free (font->cleartext); return status; } @@ -1313,30 +1725,26 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, cairo_bool_t hex_encode) { cairo_type1_font_subset_t font; - cairo_status_t status, status_ignored; - unsigned long parent_glyph, length; + cairo_status_t status; + cairo_bool_t is_synthetic; + unsigned long length; unsigned int i; - cairo_unscaled_font_t *unscaled_font; char buf[30]; - /* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */ - if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; + /* We need to use a fallback font if this font differs from the type1 outlines. */ + if (scaled_font_subset->scaled_font->backend->is_synthetic) { + status = scaled_font_subset->scaled_font->backend->is_synthetic (scaled_font_subset->scaled_font, &is_synthetic); + if (unlikely (status)) + return status; - if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (is_synthetic) + return CAIRO_INT_STATUS_UNSUPPORTED; + } - unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font); - - status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode); + status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode); if (unlikely (status)) return status; - for (i = 0; i < scaled_font_subset->num_glyphs; i++) { - parent_glyph = scaled_font_subset->glyphs[i]; - cairo_type1_font_subset_use_glyph (&font, parent_glyph); - } - status = cairo_type1_font_subset_generate (&font, name); if (unlikely (status)) goto fail1; @@ -1361,17 +1769,17 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, font.glyphs[i].width; } - type1_subset->x_min = font.base.x_min/1000.0; - type1_subset->y_min = font.base.y_min/1000.0; - type1_subset->x_max = font.base.x_max/1000.0; - type1_subset->y_max = font.base.y_max/1000.0; - type1_subset->ascent = font.base.ascent/1000.0; - type1_subset->descent = font.base.descent/1000.0; + type1_subset->x_min = font.base.x_min; + type1_subset->y_min = font.base.y_min; + type1_subset->x_max = font.base.x_max; + type1_subset->y_max = font.base.y_max; + type1_subset->ascent = font.base.ascent; + type1_subset->descent = font.base.descent; length = font.base.header_size + font.base.data_size + font.base.trailer_size; - type1_subset->data = malloc (length); + type1_subset->data = _cairo_malloc (length); if (unlikely (type1_subset->data == NULL)) goto fail3; @@ -1389,7 +1797,7 @@ _cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, fail2: free (type1_subset->base_font); fail1: - status_ignored = _cairo_type1_font_subset_fini (&font); + _cairo_type1_font_subset_fini (&font); return status; } @@ -1405,32 +1813,26 @@ _cairo_type1_subset_fini (cairo_type1_subset_t *subset) cairo_bool_t _cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) { - cairo_ft_unscaled_font_t *unscaled; - FT_Face face; - PS_FontInfoRec font_info; - cairo_bool_t is_type1 = FALSE; + cairo_status_t status; + unsigned long length; + unsigned char buf[64]; - if (!_cairo_scaled_font_is_ft (scaled_font)) - return FALSE; - unscaled = (cairo_ft_unscaled_font_t *) _cairo_ft_scaled_font_get_unscaled_font (scaled_font); - face = _cairo_ft_unscaled_font_lock_face (unscaled); - if (!face) - return FALSE; + if (!scaled_font->backend->load_type1_data) + return FALSE; - if (FT_Get_PS_Font_Info(face, &font_info) == 0) - is_type1 = TRUE; + status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length); + if (status) + return FALSE; - /* OpenType/CFF fonts also have a PS_FontInfoRec */ -#if HAVE_FT_LOAD_SFNT_TABLE - if (FT_IS_SFNT (face)) - is_type1 = FALSE; -#endif + /* We only need a few bytes to test for Type 1 */ + if (length > sizeof (buf)) + length = sizeof (buf); - _cairo_ft_unscaled_font_unlock_face (unscaled); + status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length); + if (status) + return FALSE; - return is_type1; + return check_fontdata_is_type1 (buf, length); } -#endif /* CAIRO_HAS_FT_FONT */ - #endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h index b4abcf604306..6f40f1c25b4d 100644 --- a/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h @@ -45,8 +45,9 @@ #include "cairo-surface-clipper-private.h" #include "cairo-pdf-operators-private.h" -typedef cairo_status_t (*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image, - cairo_output_stream_t *stream); +typedef cairo_int_status_t +(*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image, + cairo_output_stream_t *stream); typedef struct cairo_type3_glyph_surface { cairo_surface_t base; @@ -64,7 +65,8 @@ cairo_private cairo_surface_t * _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, - cairo_scaled_font_subsets_t *font_subsets); + cairo_scaled_font_subsets_t *font_subsets, + cairo_bool_t ps_output); cairo_private void _cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c index 74257d4bf854..6b21023190c1 100644 --- a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c @@ -42,7 +42,9 @@ #include "cairo-output-stream-private.h" #include "cairo-recording-surface-private.h" #include "cairo-analysis-surface-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" #include "cairo-surface-clipper-private.h" static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; @@ -72,22 +74,23 @@ cairo_surface_t * _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, - cairo_scaled_font_subsets_t *font_subsets) + cairo_scaled_font_subsets_t *font_subsets, + cairo_bool_t ps) { cairo_type3_glyph_surface_t *surface; - cairo_matrix_t invert_y_axis; if (unlikely (stream != NULL && stream->status)) return _cairo_surface_create_in_error (stream->status); - surface = malloc (sizeof (cairo_type3_glyph_surface_t)); + surface = _cairo_malloc (sizeof (cairo_type3_glyph_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &cairo_type3_glyph_surface_backend, NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ surface->scaled_font = scaled_font; surface->stream = stream; @@ -98,13 +101,12 @@ _cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, * entry in the Type 3 dictionary. In the PDF backend this is an * identity matrix. */ surface->cairo_to_pdf = scaled_font->scale_inverse; - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis); _cairo_pdf_operators_init (&surface->pdf_operators, surface->stream, &surface->cairo_to_pdf, - font_subsets); + font_subsets, + ps); _cairo_surface_clipper_init (&surface->clipper, _cairo_type3_glyph_surface_clipper_intersect_clip_path); @@ -187,7 +189,7 @@ static cairo_int_status_t _cairo_type3_glyph_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; const cairo_surface_pattern_t *pattern; @@ -223,7 +225,7 @@ _cairo_type3_glyph_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { return _cairo_type3_glyph_surface_paint (abstract_surface, op, mask, @@ -234,13 +236,13 @@ static cairo_int_status_t _cairo_type3_glyph_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -260,11 +262,11 @@ static cairo_int_status_t _cairo_type3_glyph_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_int_status_t status; @@ -285,21 +287,18 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + 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, invert_y_axis; + cairo_matrix_t new_ctm; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status; - cairo_matrix_init_scale (&invert_y_axis, 1, -1); - cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm); - cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm); + 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, @@ -321,33 +320,35 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH, - NULL, /* _cairo_type3_glyph_surface_create_similar */ _cairo_type3_glyph_surface_finish, + + _cairo_default_context_create, /* XXX usable through a context? */ + + NULL, /* create similar */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + NULL, /* source */ NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* cairo_type3_glyph_surface_copy_page */ - NULL, /* _cairo_type3_glyph_surface_show_page */ + NULL, /* snapshot */ + + NULL, /* copy page */ + NULL, /* show page */ + NULL, /* _cairo_type3_glyph_surface_get_extents */ - NULL, /* old_show_glyphs */ NULL, /* _cairo_type3_glyph_surface_get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ + _cairo_type3_glyph_surface_paint, _cairo_type3_glyph_surface_mask, _cairo_type3_glyph_surface_stroke, _cairo_type3_glyph_surface_fill, + NULL, /* fill-stroke */ _cairo_type3_glyph_surface_show_glyphs, - NULL, /* snapshot */ }; static void @@ -382,14 +383,10 @@ _cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *sur x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y); - mat.xx = image->width; - mat.xy = 0; - mat.yx = 0; - mat.yy = image->height; - mat.x0 = x; - mat.y0 = y; + cairo_matrix_init(&mat, image->width, 0, + 0, -image->height, + x, y); cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse); - mat.y0 *= -1; return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); } @@ -415,7 +412,7 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_scaled_glyph_t *scaled_glyph; - cairo_status_t status, status2; + cairo_int_status_t status, status2; cairo_output_stream_t *null_stream; if (unlikely (surface->base.status)) @@ -433,11 +430,11 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, &scaled_glyph); - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) goto cleanup; if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = CAIRO_STATUS_SUCCESS; + status = CAIRO_INT_STATUS_SUCCESS; goto cleanup; } @@ -448,13 +445,13 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) - status = CAIRO_STATUS_SUCCESS; + status = CAIRO_INT_STATUS_SUCCESS; cleanup: _cairo_scaled_font_thaw_cache (surface->scaled_font); status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; @@ -469,7 +466,7 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_scaled_glyph_t *scaled_glyph; - cairo_status_t status, status2; + cairo_int_status_t status, status2; double x_advance, y_advance; cairo_matrix_t font_matrix_inverse; @@ -489,10 +486,10 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = CAIRO_INT_STATUS_IMAGE_FALLBACK; } - if (_cairo_status_is_error (status)) { + if (_cairo_int_status_is_error (status)) { _cairo_scaled_font_thaw_cache (surface->scaled_font); return status; } @@ -505,7 +502,7 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, /* The invertability of font_matrix is tested in * pdf_operators_show_glyphs before any glyphs are mapped to the * subset. */ - assert (status2 == CAIRO_STATUS_SUCCESS); + assert (status2 == CAIRO_INT_STATUS_SUCCESS); cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); *width = x_advance; @@ -518,11 +515,11 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, "%f 0 %f %f %f %f d1\n", x_advance, _cairo_fixed_to_double (bbox->p1.x), - - _cairo_fixed_to_double (bbox->p2.y), + _cairo_fixed_to_double (bbox->p1.y), _cairo_fixed_to_double (bbox->p2.x), - - _cairo_fixed_to_double (bbox->p1.y)); + _cairo_fixed_to_double (bbox->p2.y)); - if (status == CAIRO_STATUS_SUCCESS) { + if (status == CAIRO_INT_STATUS_SUCCESS) { cairo_output_stream_t *mem_stream; mem_stream = _cairo_memory_stream_create (); @@ -537,17 +534,17 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, &surface->base); status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_type3_glyph_surface_set_stream (surface, stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) _cairo_memory_stream_copy (mem_stream, stream); status2 = _cairo_output_stream_destroy (mem_stream); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; } diff --git a/gfx/cairo/cairo/src/cairo-types-private.h b/gfx/cairo/cairo/src/cairo-types-private.h index 497fa5e8ebd1..da373030c263 100644 --- a/gfx/cairo/cairo/src/cairo-types-private.h +++ b/gfx/cairo/cairo/src/cairo-types-private.h @@ -44,13 +44,15 @@ #include "cairo-list-private.h" #include "cairo-reference-count-private.h" +CAIRO_BEGIN_DECLS + /** * SECTION:cairo-types * @Title: Types * @Short_Description: Generic data types * * This section lists generic data types used in the cairo API. - */ + **/ typedef struct _cairo_array cairo_array_t; typedef struct _cairo_backend cairo_backend_t; @@ -61,9 +63,15 @@ typedef struct _cairo_clip cairo_clip_t; typedef struct _cairo_clip_path cairo_clip_path_t; typedef struct _cairo_color cairo_color_t; typedef struct _cairo_color_stop cairo_color_stop_t; +typedef struct _cairo_contour cairo_contour_t; +typedef struct _cairo_contour_chain cairo_contour_chain_t; +typedef struct _cairo_contour_iter cairo_contour_iter_t; +typedef struct _cairo_damage cairo_damage_t; typedef struct _cairo_device_backend cairo_device_backend_t; typedef struct _cairo_font_face_backend cairo_font_face_backend_t; typedef struct _cairo_gstate cairo_gstate_t; +typedef struct _cairo_gstate_backend cairo_gstate_backend_t; +typedef struct _cairo_glyph_text_info cairo_glyph_text_info_t; typedef struct _cairo_hash_entry cairo_hash_entry_t; typedef struct _cairo_hash_table cairo_hash_table_t; typedef struct _cairo_image_surface cairo_image_surface_t; @@ -73,25 +81,39 @@ typedef struct _cairo_output_stream cairo_output_stream_t; typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; typedef struct _cairo_path_fixed cairo_path_fixed_t; typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; -typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; typedef struct _cairo_solid_pattern cairo_solid_pattern_t; +typedef struct _cairo_surface_attributes cairo_surface_attributes_t; typedef struct _cairo_surface_backend cairo_surface_backend_t; +typedef struct _cairo_surface_observer cairo_surface_observer_t; typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t; typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t; typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t; +typedef struct _cairo_traps cairo_traps_t; +typedef struct _cairo_tristrip cairo_tristrip_t; typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t; typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t; typedef cairo_array_t cairo_user_data_array_t; +typedef struct _cairo_scaled_font_private cairo_scaled_font_private_t; +typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; +typedef struct _cairo_scaled_glyph cairo_scaled_glyph_t; +typedef struct _cairo_scaled_glyph_private cairo_scaled_glyph_private_t; + +typedef struct cairo_compositor cairo_compositor_t; +typedef struct cairo_fallback_compositor cairo_fallback_compositor_t; +typedef struct cairo_mask_compositor cairo_mask_compositor_t; +typedef struct cairo_traps_compositor cairo_traps_compositor_t; +typedef struct cairo_spans_compositor cairo_spans_compositor_t; + struct _cairo_observer { cairo_list_t link; void (*callback) (cairo_observer_t *self, void *arg); }; /** - * cairo_hash_entry_t: + * _cairo_hash_entry: * * A #cairo_hash_entry_t contains both a key and a value for * #cairo_hash_table_t. User-derived types for #cairo_hash_entry_t must @@ -109,7 +131,7 @@ struct _cairo_observer { * * _cairo_hash_table_insert (hash_table, &my_entry->base); * - * IMPORTANT: The caller is reponsible for initializing + * IMPORTANT: The caller is responsible for initializing * my_entry->base.hash with a hash code derived from the key. The * essential property of the hash code is that keys_equal must never * return %TRUE for two keys that have different hashes. The best hash @@ -132,11 +154,33 @@ struct _cairo_array { unsigned int size; unsigned int num_elements; unsigned int element_size; - char **elements; - - cairo_bool_t is_snapshot; + char *elements; }; +/** + * _cairo_lcd_filter: + * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for + * font backend and target device + * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering + * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter + * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel + * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel + * + * The LCD filter specifies the low-pass filter applied to LCD-optimized + * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. + * + * Note: This API was temporarily made available in the public + * interface during the 1.7.x development series, but was made private + * before 1.8. + **/ +typedef enum _cairo_lcd_filter { + CAIRO_LCD_FILTER_DEFAULT, + CAIRO_LCD_FILTER_NONE, + CAIRO_LCD_FILTER_INTRA_PIXEL, + CAIRO_LCD_FILTER_FIR3, + CAIRO_LCD_FILTER_FIR5 +} cairo_lcd_filter_t; + typedef enum _cairo_round_glyph_positions { CAIRO_ROUND_GLYPH_POS_DEFAULT, CAIRO_ROUND_GLYPH_POS_ON, @@ -150,8 +194,19 @@ struct _cairo_font_options { cairo_hint_style_t hint_style; cairo_hint_metrics_t hint_metrics; cairo_round_glyph_positions_t round_glyph_positions; + char *variations; }; +struct _cairo_glyph_text_info { + const char *utf8; + int utf8_len; + + const cairo_text_cluster_t *clusters; + int num_clusters; + cairo_text_cluster_flags_t cluster_flags; +}; + + /* XXX: Right now, the _cairo_color structure puts unpremultiplied color in the doubles and premultiplied color in the shorts. Yes, this is crazy insane, (but at least we don't export this @@ -187,28 +242,14 @@ struct _cairo_color_stop { typedef enum _cairo_paginated_mode { CAIRO_PAGINATED_MODE_ANALYZE, /* analyze page regions */ CAIRO_PAGINATED_MODE_RENDER, /* render page contents */ - CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */ + CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */ } cairo_paginated_mode_t; -/* Sure wish C had a real enum type so that this would be distinct - * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 - * offset. We want to keep it fit in int8_t as the compiler may choose - * that for #cairo_status_t */ -typedef enum _cairo_int_status { - CAIRO_INT_STATUS_UNSUPPORTED = 100, - CAIRO_INT_STATUS_DEGENERATE, - CAIRO_INT_STATUS_NOTHING_TO_DO, - CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY, - CAIRO_INT_STATUS_IMAGE_FALLBACK, - CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, - - CAIRO_INT_STATUS_LAST_STATUS -} cairo_int_status_t; - typedef enum _cairo_internal_surface_type { CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000, CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS, + CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER, CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, @@ -216,9 +257,11 @@ typedef enum _cairo_internal_surface_type { CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH } cairo_internal_surface_type_t; +typedef enum _cairo_internal_device_type { + CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER = 0x1000, +} cairo_device_surface_type_t; + #define CAIRO_HAS_TEST_PAGINATED_SURFACE 1 -#define CAIRO_HAS_TEST_NULL_SURFACE 1 -#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1 typedef struct _cairo_slope { cairo_fixed_t dx; @@ -230,11 +273,21 @@ typedef struct _cairo_point_double { double y; } cairo_point_double_t; +typedef struct _cairo_circle_double { + cairo_point_double_t center; + double radius; +} cairo_circle_double_t; + typedef struct _cairo_distance_double { double dx; double dy; } cairo_distance_double_t; +typedef struct _cairo_box_double { + cairo_point_double_t p1; + cairo_point_double_t p2; +} cairo_box_double_t; + typedef struct _cairo_line { cairo_point_t p1; cairo_point_t p2; @@ -266,13 +319,6 @@ typedef struct _cairo_edge { typedef struct _cairo_polygon { cairo_status_t status; - cairo_point_t first_point; - cairo_point_t last_point; - cairo_point_t current_point; - cairo_slope_t current_edge; - cairo_bool_t has_current_point; - cairo_bool_t has_current_edge; - cairo_box_t extents; cairo_box_t limit; const cairo_box_t *limits; @@ -286,7 +332,8 @@ typedef struct _cairo_polygon { typedef cairo_warn cairo_status_t (*cairo_spline_add_point_func_t) (void *closure, - const cairo_point_t *point); + const cairo_point_t *point, + const cairo_slope_t *tangent); typedef struct _cairo_spline_knots { cairo_point_t a, b, c, d; @@ -353,6 +400,14 @@ typedef enum _cairo_image_transparency { CAIRO_IMAGE_UNKNOWN } cairo_image_transparency_t; +typedef enum _cairo_image_color { + CAIRO_IMAGE_IS_COLOR, + CAIRO_IMAGE_IS_GRAYSCALE, + CAIRO_IMAGE_IS_MONOCHROME, + CAIRO_IMAGE_UNKNOWN_COLOR +} cairo_image_color_t; + + struct _cairo_mime_data { cairo_reference_count_t ref_count; unsigned char *data; @@ -361,76 +416,6 @@ struct _cairo_mime_data { void *closure; }; -struct _cairo_pattern { - cairo_pattern_type_t type; - cairo_reference_count_t ref_count; - cairo_status_t status; - cairo_user_data_array_t user_data; - - cairo_matrix_t matrix; - cairo_filter_t filter; - cairo_extend_t extend; - - cairo_bool_t has_component_alpha; -}; - -struct _cairo_solid_pattern { - cairo_pattern_t base; - cairo_color_t color; -}; - -typedef struct _cairo_surface_pattern { - cairo_pattern_t base; - - cairo_surface_t *surface; -} cairo_surface_pattern_t; - -typedef struct _cairo_gradient_stop { - double offset; - cairo_color_stop_t color; -} cairo_gradient_stop_t; - -typedef struct _cairo_gradient_pattern { - cairo_pattern_t base; - - unsigned int n_stops; - unsigned int stops_size; - cairo_gradient_stop_t *stops; - cairo_gradient_stop_t stops_embedded[2]; -} cairo_gradient_pattern_t; - -typedef struct _cairo_linear_pattern { - cairo_gradient_pattern_t base; - - cairo_point_t p1; - cairo_point_t p2; -} cairo_linear_pattern_t; - -typedef struct _cairo_radial_pattern { - cairo_gradient_pattern_t base; - - cairo_point_t c1; - cairo_fixed_t r1; - cairo_point_t c2; - cairo_fixed_t r2; -} cairo_radial_pattern_t; - -typedef union { - cairo_gradient_pattern_t base; - - cairo_linear_pattern_t linear; - cairo_radial_pattern_t radial; -} cairo_gradient_pattern_union_t; - -typedef union { - cairo_pattern_type_t type; - cairo_pattern_t base; - - cairo_solid_pattern_t solid; - cairo_surface_pattern_t surface; - cairo_gradient_pattern_union_t gradient; -} cairo_pattern_union_t; - /* * A #cairo_unscaled_font_t is just an opaque handle we use in the * glyph cache. @@ -440,21 +425,6 @@ typedef struct _cairo_unscaled_font { cairo_reference_count_t ref_count; const cairo_unscaled_font_backend_t *backend; } cairo_unscaled_font_t; +CAIRO_END_DECLS -typedef struct _cairo_scaled_glyph { - cairo_hash_entry_t hash_entry; - - cairo_text_extents_t metrics; /* user-space metrics */ - cairo_text_extents_t fs_metrics; /* font-space metrics */ - cairo_box_t bbox; /* device-space bounds */ - int16_t x_advance; /* device-space rounded X advance */ - int16_t y_advance; /* device-space rounded Y advance */ - - unsigned int has_info; - cairo_image_surface_t *surface; /* device-space image */ - cairo_path_fixed_t *path; /* device-space outline */ - cairo_surface_t *recording_surface; /* device-space recording-surface */ - - void *surface_private; /* for the surface backend */ -} cairo_scaled_glyph_t; #endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-unicode.c b/gfx/cairo/cairo/src/cairo-unicode.c index 88de39516b6c..966ae84b59bf 100644 --- a/gfx/cairo/cairo/src/cairo-unicode.c +++ b/gfx/cairo/cairo/src/cairo-unicode.c @@ -342,6 +342,36 @@ _cairo_ucs4_to_utf8 (uint32_t unicode, return bytes; } +/** + * _cairo_ucs4_to_utf16: + * @unicode: a UCS-4 character + * @utf16: buffer to write utf16 string into. Must have at least 2 + * elements. Or %NULL. + * + * This space left intentionally blank. + * + * Return value: Number of elements in the utf16 string or 0 if an + * invalid unicode character + **/ +int +_cairo_ucs4_to_utf16 (uint32_t unicode, + uint16_t *utf16) +{ + if (unicode < 0x10000) { + if (utf16) + utf16[0] = unicode; + return 1; + } else if (unicode < 0x110000) { + if (utf16) { + utf16[0] = (unicode - 0x10000) / 0x400 + 0xd800; + utf16[1] = (unicode - 0x10000) % 0x400 + 0xdc00; + } + return 2; + } else { + return 0; + } +} + #if CAIRO_HAS_UTF8_TO_UTF16 /** * _cairo_utf8_to_utf16: @@ -401,12 +431,7 @@ _cairo_utf8_to_utf16 (const char *str, for (i = 0; i < n16;) { uint32_t wc = _utf8_get_char (in); - if (wc < 0x10000) { - str16[i++] = wc; - } else { - str16[i++] = (wc - 0x10000) / 0x400 + 0xd800; - str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00; - } + i += _cairo_ucs4_to_utf16 (wc, str16 + i); in = UTF8_NEXT_CHAR (in); } diff --git a/gfx/cairo/cairo/src/cairo-uninstalled.pc.in b/gfx/cairo/cairo/src/cairo-uninstalled.pc.in new file mode 100644 index 000000000000..9dc3231ae45f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-uninstalled.pc.in @@ -0,0 +1,8 @@ +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/gfx/cairo/cairo/src/cairo-user-font.c b/gfx/cairo/cairo/src/cairo-user-font.c index a524d588fa94..a26fb57ee109 100644 --- a/gfx/cairo/cairo/src/cairo-user-font.c +++ b/gfx/cairo/cairo/src/cairo-user-font.c @@ -49,7 +49,7 @@ * in a font. This is most useful in implementing fonts in non-standard * formats, like SVG fonts and Flash fonts, but can also be used by games and * other application to draw "funky" fonts. - */ + **/ /** * CAIRO_HAS_USER_FONT: @@ -59,8 +59,8 @@ * The user font backend is always built in versions of cairo that support * this feature (1.8 and later). * - * @Since: 1.8 - */ + * Since: 1.8 + **/ typedef struct _cairo_user_scaled_font_methods { cairo_user_scaled_font_init_func_t init; @@ -158,7 +158,7 @@ _cairo_user_scaled_glyph_init (void *abstract_font, status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, _cairo_scaled_glyph_index(scaled_glyph), cr, &extents); - if (status == CAIRO_STATUS_SUCCESS) + if (status == CAIRO_INT_STATUS_SUCCESS) status = cairo_status (cr); cairo_destroy (cr); @@ -229,8 +229,11 @@ _cairo_user_scaled_glyph_init (void *abstract_font, switch (scaled_font->base.options.antialias) { default: case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GOOD: case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break; case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break; + case CAIRO_ANTIALIAS_BEST: case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break; } surface = cairo_image_surface_create (format, width, height); @@ -328,11 +331,12 @@ _cairo_user_text_to_glyphs (void *abstract_font, glyphs, num_glyphs, clusters, num_clusters, cluster_flags); - if (status != CAIRO_STATUS_SUCCESS && - status != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + if (status != CAIRO_INT_STATUS_SUCCESS && + status != CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED) return status; - if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED || *num_glyphs < 0) { + if (status == CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED || + *num_glyphs < 0) { if (orig_glyphs != *glyphs) { cairo_glyph_free (*glyphs); *glyphs = orig_glyphs; @@ -398,7 +402,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ font_face->immutable = TRUE; - user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t)); + user_scaled_font = _cairo_malloc (sizeof (cairo_user_scaled_font_t)); if (unlikely (user_scaled_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -503,7 +507,7 @@ _cairo_user_font_face_scaled_font_create (void *abstract_ const cairo_font_face_backend_t _cairo_user_font_face_backend = { CAIRO_FONT_TYPE_USER, _cairo_user_font_face_create_for_toy, - NULL, /* destroy */ + _cairo_font_face_destroy, _cairo_user_font_face_scaled_font_create }; @@ -540,7 +544,7 @@ cairo_user_font_face_create (void) { cairo_user_font_face_t *font_face; - font_face = malloc (sizeof (cairo_user_font_face_t)); + font_face = _cairo_malloc (sizeof (cairo_user_font_face_t)); if (!font_face) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_font_face_t *)&_cairo_font_face_nil; diff --git a/gfx/cairo/cairo/src/cairo-version.c b/gfx/cairo/cairo/src/cairo-version.c index b07b48b37ab6..943e1cde0211 100644 --- a/gfx/cairo/cairo/src/cairo-version.c +++ b/gfx/cairo/cairo/src/cairo-version.c @@ -36,14 +36,8 @@ * Carl D. Worth */ -#define CAIRO_VERSION_H 1 - #include "cairoint.h" -/* get the "real" version info instead of dummy cairo-version.h */ -#undef CAIRO_VERSION_H -#include "cairo-features.h" - /** * SECTION:cairo-version * @Title: Version Information @@ -54,35 +48,35 @@ * vs. in-progress development, (such as from git instead of a tar file, * or as a "snapshot" tar file as opposed to a "release" tar file). * - * + * * _____ Major. Always 1, until we invent a new scheme. * / ___ Minor. Even/Odd = Release/Snapshot (tar files) or Branch/Head (git) * | / _ Micro. Even/Odd = Tar-file/git * | | / * 1.0.0 - * + * * * Here are a few examples of versions that one might see. - * + * * Releases * -------- * 1.0.0 - A major release * 1.0.2 - A subsequent maintenance release * 1.2.0 - Another major release - * + *   * Snapshots * --------- * 1.1.2 - A snapshot (working toward the 1.2.0 release) - * + *   * In-progress development (eg. from git) * -------------------------------------- * 1.0.1 - Development on a maintenance branch (toward 1.0.2 release) * 1.1.1 - Development on head (toward 1.1.2 snapshot and 1.2.0 release) - * - * + * + * * * Compatibility - * + * * The API/ABI compatibility guarantees for various versions are as * follows. First, let's assume some cairo-using application code that is * successfully using the API/ABI "from" one version of cairo. Then let's @@ -102,27 +96,27 @@ * with the same in-development version number. This is because these * numbers don't correspond to any fixed state of the software, but * rather the many states between snapshots and releases. - * + * * * * Examining the version - * + * * Cairo provides the ability to examine the version at either * compile-time or run-time and in both a human-readable form as well as * an encoded form suitable for direct comparison. Cairo also provides the * macro CAIRO_VERSION_ENCODE() to perform the encoding. * - * + * * Compile-time * ------------ - * CAIRO_VERSION_STRING Human-readable - * CAIRO_VERSION Encoded, suitable for comparison - * + * #CAIRO_VERSION_STRING Human-readable + * #CAIRO_VERSION Encoded, suitable for comparison + *   * Run-time * -------- * cairo_version_string() Human-readable * cairo_version() Encoded, suitable for comparison - * + * * * For example, checking that the cairo version is greater than or equal * to 1.0.0 could be achieved at compile-time or run-time as follows: @@ -135,55 +129,68 @@ * if (cairo_version() >= CAIRO_VERSION_ENCODE(1, 0, 0)) * printf ("Running with suitable cairo version: %s\n", cairo_version_string ()); * - * + * * - */ + * + **/ /** * CAIRO_VERSION: * * The version of cairo available at compile-time, encoded using * CAIRO_VERSION_ENCODE(). - */ + * + * Since: 1.0 + **/ /** * CAIRO_VERSION_MAJOR: * * The major component of the version of cairo available at compile-time. - */ + * + * Since: 1.0 + **/ /** * CAIRO_VERSION_MINOR: * * The minor component of the version of cairo available at compile-time. - */ + * + * Since: 1.0 + **/ /** * CAIRO_VERSION_MICRO: * * The micro component of the version of cairo available at compile-time. - */ + * + * Since: 1.0 + **/ /** * CAIRO_VERSION_STRING: * * A human-readable string literal containing the version of cairo available * at compile-time, in the form of "X.Y.Z". - */ + * + * Since: 1.8 + **/ /** * CAIRO_VERSION_ENCODE: * @major: the major component of the version number * @minor: the minor component of the version number * @micro: the micro component of the version number - * + * * This macro encodes the given cairo version into an integer. The numbers * returned by %CAIRO_VERSION and cairo_version() are encoded using this macro. * Two encoded version numbers can be compared as integers. The encoding ensures * that later versions compare greater than earlier versions. * - * @Returns: the encoded version. - */ + * Returns: the encoded version. + * + * Since: 1.0 + **/ /** * CAIRO_VERSION_STRINGIZE: @@ -195,10 +202,10 @@ * returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro. * The parameters to this macro must expand to numerical literals. * - * @Returns: a string literal containing the version. + * Returns: a string literal containing the version. * - * @Since: 1.8 - */ + * Since: 1.8 + **/ /** * cairo_version: @@ -218,6 +225,8 @@ * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING. * * Return value: the encoded version. + * + * Since: 1.0 **/ int cairo_version (void) @@ -235,6 +244,8 @@ cairo_version (void) * %CAIRO_VERSION_STRING and %CAIRO_VERSION. * * Return value: a string containing the version. + * + * Since: 1.0 **/ const char* cairo_version_string (void) diff --git a/gfx/cairo/cairo/src/cairo-version.h b/gfx/cairo/cairo/src/cairo-version.h index ace09244c1b0..9fd69fb45e38 100644 --- a/gfx/cairo/cairo/src/cairo-version.h +++ b/gfx/cairo/cairo/src/cairo-version.h @@ -1,16 +1,8 @@ -/* This is a dummy file. - * The actual version info is in toplevel cairo-version.h. - * The purpose of this file is to make most of the source files NOT depend - * on the real cairo-version.h, and as a result, changing library version - * would not cause a complete rebuild of all object files (just a relink). - * This is useful when bisecting. */ #ifndef CAIRO_VERSION_H #define CAIRO_VERSION_H -#if 0 -#define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD -#define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD -#define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD -#endif +#define CAIRO_VERSION_MAJOR 1 +#define CAIRO_VERSION_MINOR 17 +#define CAIRO_VERSION_MICRO 4 #endif diff --git a/gfx/cairo/cairo/src/cairo-vg-surface.c b/gfx/cairo/cairo/src/cairo-vg-surface.c index d3ae8aa527d7..cbff748fe6d1 100644 --- a/gfx/cairo/cairo/src/cairo-vg-surface.c +++ b/gfx/cairo/cairo/src/cairo-vg-surface.c @@ -39,9 +39,11 @@ #include "cairo-vg.h" #include "cairo-cache-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" #include "cairo-path-fixed-private.h" -#include "cairo-recording-surface-private.h" +#include "cairo-recording-surface-inline.h" #include "cairo-surface-clipper-private.h" #include @@ -245,9 +247,9 @@ _vg_format_to_pixman (VGImageFormat format, *needs_premult_fixup = FALSE; switch (format) { /* RGB{A,X} channel ordering */ - case VG_sRGBX_8888: return 0; //PIXMAN_r8g8b8x8; - case VG_sRGBA_8888: return 0; - case VG_sRGBA_8888_PRE: return 0; //PIXMAN_r8b8g8a8; + case VG_sRGBX_8888: return PIXMAN_r8g8b8x8; + case VG_sRGBA_8888: *needs_premult_fixup = TRUE; return PIXMAN_r8g8b8a8; + case VG_sRGBA_8888_PRE: return PIXMAN_r8g8b8a8; case VG_sRGB_565: return PIXMAN_r5g6b5; case VG_sRGBA_5551: return 0; case VG_sRGBA_4444: return 0; @@ -465,7 +467,7 @@ _vg_surface_get_extents (void *abstract_surface, typedef struct _vg_path { VGPath path; - cairo_matrix_t *ctm_inverse; + const cairo_matrix_t *ctm_inverse; VGubyte gseg[MAX_SEG]; VGfloat gdata[MAX_SEG*3*2]; @@ -588,7 +590,6 @@ _vg_path_from_cairo (vg_path_t *vg_path, vg_path->dcount = 0; status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _vg_move_to, _vg_line_to, _vg_curve_to, @@ -658,9 +659,12 @@ _vg_rendering_quality_from_cairo (cairo_antialias_t aa) switch (aa) { case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_BEST: return VG_RENDERING_QUALITY_BETTER; case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_FAST: return VG_RENDERING_QUALITY_FASTER; case CAIRO_ANTIALIAS_NONE: @@ -773,10 +777,10 @@ _vg_setup_linear_source (cairo_vg_context_t *context, { VGfloat linear[4]; - linear[0] = _cairo_fixed_to_double (lpat->p1.x); - linear[1] = _cairo_fixed_to_double (lpat->p1.y); - linear[2] = _cairo_fixed_to_double (lpat->p2.x); - linear[3] = _cairo_fixed_to_double (lpat->p2.y); + linear[0] = lpat->pd1.x; + linear[1] = lpat->pd1.y; + linear[2] = lpat->pd2.x; + linear[3] = lpat->pd2.y; vgSetParameteri (context->paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, @@ -800,11 +804,11 @@ _vg_setup_radial_source (cairo_vg_context_t *context, { VGfloat radial[5]; - radial[0] = _cairo_fixed_to_double (rpat->c1.x); - radial[1] = _cairo_fixed_to_double (rpat->c1.y); - radial[2] = _cairo_fixed_to_double (rpat->c2.x); - radial[3] = _cairo_fixed_to_double (rpat->c2.y); - radial[4] = _cairo_fixed_to_double (rpat->r2); + radial[0] = rpat->cd1.center.x; + radial[1] = rpat->cd1.center.y; + radial[2] = rpat->cd2.center.x; + radial[3] = rpat->cd2.center.y; + radial[4] = rpat->cd2.radius; vgSetParameteri (context->paint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); @@ -982,7 +986,7 @@ _vg_setup_surface_source (cairo_vg_context_t *context, return status; } - cairo_surface_attach_snapshot (spat->surface, &clone->base, + _cairo_surface_attach_snapshot (spat->surface, &clone->base, _vg_surface_remove_from_cache); DONE: @@ -1054,16 +1058,16 @@ setup_source (cairo_vg_context_t *context, } static cairo_int_status_t -_vg_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t*source, - cairo_path_fixed_t *path, - cairo_stroke_style_t *style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) +_vg_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_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context; @@ -1130,14 +1134,14 @@ _vg_surface_stroke (void *abstract_surface, } static cairo_int_status_t -_vg_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) +_vg_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_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context; @@ -1197,10 +1201,10 @@ _vg_surface_fill (void *abstract_surface, } static cairo_int_status_t -_vg_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) +_vg_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) { cairo_vg_surface_t *surface = abstract_surface; cairo_vg_context_t *context; @@ -1265,10 +1269,10 @@ _vg_surface_paint (void *abstract_surface, static cairo_int_status_t _vg_surface_mask (void *abstract_surface, - cairo_operator_t op, + cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_vg_surface_t *surface = abstract_surface; cairo_status_t status; @@ -1301,6 +1305,7 @@ _vg_surface_get_font_options (void *abstract_surface, _cairo_font_options_init_default (options); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } static cairo_int_status_t @@ -1310,8 +1315,7 @@ _vg_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_path_fixed_t path; @@ -1333,7 +1337,7 @@ _vg_surface_show_glyphs (void *abstract_surface, op, source, &path, CAIRO_FILL_RULE_WINDING, CAIRO_GSTATE_TOLERANCE_DEFAULT, - CAIRO_ANTIALIAS_SUBPIXEL, + CAIRO_ANTIALIAS_DEFAULT, clip); BAIL: _cairo_path_fixed_fini (&path); @@ -1369,7 +1373,7 @@ premultiply_argb (uint8_t *data, uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff); uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff); uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff); - row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); + row[i] = ((uint32_t)alpha << 24) | (r << 16) | (g << 8) | (b << 0); } } @@ -1445,79 +1449,6 @@ _vg_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } -static cairo_status_t -_vg_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_vg_surface_t *surface = abstract_surface; - - *image_rect_out = *interest_rect; - *image_extra = NULL; - return _vg_get_image (surface, - interest_rect->x, interest_rect->y, - interest_rect->width, interest_rect->height, - image_out); -} - -static void -unpremultiply_argb (uint8_t *data, - int width, - int height, - int stride) -{ - int i; - - while (height--) { - uint32_t *row = (uint32_t *) data; - - for (i = 0; i < width; i ++) { - uint32_t p = row[i]; - uint8_t alpha; - - alpha = p >> 24; - if (alpha == 0) { - row[i] = 0; - } else if (alpha != 0xff) { - uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha; - uint8_t g = (((p >> 8) & 0xff) * 255 + alpha / 2) / alpha; - uint8_t b = (((p >> 0) & 0xff) * 255 + alpha / 2) / alpha; - row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0); - } - } - - data += stride; - } -} - -static void -_vg_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_vg_surface_t *surface = abstract_surface; - cairo_bool_t needs_unpremultiply; - - _vg_format_to_pixman (surface->format, &needs_unpremultiply); - if (needs_unpremultiply) { - unpremultiply_argb (image->data, - image->width, image->height, - image->stride); - } - - vgImageSubData (surface->image, - image->data, image->stride, - surface->format, - image_rect->x, image_rect->y, - image_rect->width, image_rect->height); - - cairo_surface_destroy (&image->base); -} - static cairo_status_t _vg_surface_finish (void *abstract_surface) { @@ -1547,39 +1478,35 @@ _vg_surface_finish (void *abstract_surface) static const cairo_surface_backend_t cairo_vg_surface_backend = { CAIRO_SURFACE_TYPE_VG, - _vg_surface_create_similar, _vg_surface_finish, + _cairo_default_context_create, /* XXX */ + + _vg_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, _vg_surface_acquire_source_image, _vg_surface_release_source_image, - _vg_surface_acquire_dest_image, - _vg_surface_release_dest_image, - - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ + _vg_surface_get_extents, - NULL, /* old_show_glyphs */ _vg_surface_get_font_options, /* get_font_options */ + NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ + NULL, /* mark dirty */ _vg_surface_paint, _vg_surface_mask, _vg_surface_stroke, _vg_surface_fill, + NULL, /* fill-stroke */ _vg_surface_show_glyphs, - - NULL, /* snapshot */ - NULL, /* is_similar */ }; static cairo_surface_t * @@ -1590,7 +1517,7 @@ _vg_surface_create_internal (cairo_vg_context_t *context, { cairo_vg_surface_t *surface; - surface = malloc (sizeof (cairo_vg_surface_t)); + surface = _cairo_malloc (sizeof (cairo_vg_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -1602,7 +1529,8 @@ _vg_surface_create_internal (cairo_vg_context_t *context, _cairo_surface_init (&surface->base, &cairo_vg_surface_backend, NULL, /* device */ - _vg_format_to_content (format)); + _vg_format_to_content (format), + FALSE); /* is_vector */ surface->width = width; surface->height = height; @@ -1784,7 +1712,7 @@ cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx) cairo_vg_context_t *context; cairo_status_t status; - context = malloc (sizeof (*context)); + context = _cairo_malloc (sizeof (*context)); if (unlikely (context == NULL)) return (cairo_vg_context_t *) &_vg_context_nil; @@ -1889,7 +1817,7 @@ cairo_vg_context_create_for_egl (EGLDisplay egl_display, cairo_vg_context_t *context; cairo_status_t status; - context = malloc (sizeof (*context)); + context = _cairo_malloc (sizeof (*context)); if (unlikely (context == NULL)) return (cairo_vg_context_t *) &_vg_context_nil; diff --git a/gfx/cairo/cairo/src/cairo-vg.h b/gfx/cairo/cairo/src/cairo-vg.h index f9a62e51c698..a2701db3bd46 100644 --- a/gfx/cairo/cairo/src/cairo-vg.h +++ b/gfx/cairo/cairo/src/cairo-vg.h @@ -1,8 +1,8 @@ /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * - * Copyright 2007 * Mozilla Corporation - * Copyright 2009 Chris Wilson + * Copyright © 2007 * Mozilla Corporation + * 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 diff --git a/gfx/cairo/cairo/src/cairo-wgl-context.c b/gfx/cairo/cairo/src/cairo-wgl-context.c new file mode 100644 index 000000000000..487237446482 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-wgl-context.c @@ -0,0 +1,261 @@ +/* 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 + * Chris Wilson + * Zoxc + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include "cairo-error-private.h" + +#define WIN32_LEAN_AND_MEAN +#include + +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/gfx/cairo/cairo/src/cairo-wideint-private.h b/gfx/cairo/cairo/src/cairo-wideint-private.h index b9f8dae64f6b..3f5491bb1c02 100644 --- a/gfx/cairo/cairo/src/cairo-wideint-private.h +++ b/gfx/cairo/cairo/src/cairo-wideint-private.h @@ -54,6 +54,11 @@ cairo_uquorem64_t I _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den); +cairo_uint64_t I _cairo_double_to_uint64 (double i); +double I _cairo_uint64_to_double (cairo_uint64_t i); +cairo_int64_t I _cairo_double_to_int64 (double i); +double I _cairo_int64_to_double (cairo_uint64_t i); + cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i); #define _cairo_uint64_to_uint32(a) ((a).lo) cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b); @@ -103,6 +108,19 @@ _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) return qr; } +/* + * These need to be functions or gcc will complain when used on the + * result of a function: + * + * warning: cast from function call of type ‘#cairo_uint64_t’ to + * non-matching type ‘double’ + */ +static cairo_always_inline cairo_const cairo_uint64_t _cairo_double_to_uint64 (double i) { return i; } +static cairo_always_inline cairo_const double _cairo_uint64_to_double (cairo_uint64_t i) { return i; } + +static cairo_always_inline cairo_int64_t I _cairo_double_to_int64 (double i) { return i; } +static cairo_always_inline double I _cairo_int64_to_double (cairo_int64_t i) { return i; } + #define _cairo_uint32_to_uint64(i) ((uint64_t) (i)) #define _cairo_uint64_to_uint32(i) ((uint32_t) (i)) #define _cairo_uint64_add(a,b) ((a) + (b)) @@ -143,7 +161,7 @@ _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) #endif /* - * 64-bit comparisions derived from lt or eq + * 64-bit comparisons derived from lt or eq */ #define _cairo_uint64_le(a,b) (!_cairo_uint64_gt(a,b)) #define _cairo_uint64_ne(a,b) (!_cairo_uint64_eq(a,b)) diff --git a/gfx/cairo/cairo/src/cairo-wideint-type-private.h b/gfx/cairo/cairo/src/cairo-wideint-type-private.h index 9e49ad947a69..84a3cbab0d20 100644 --- a/gfx/cairo/cairo/src/cairo-wideint-type-private.h +++ b/gfx/cairo/cairo/src/cairo-wideint-type-private.h @@ -58,13 +58,6 @@ typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; -#ifndef _UINTPTR_T_DEFINED -#ifdef _WIN64 - typedef unsigned __int64 uintptr_t; -#else - typedef unsigned int uintptr_t; -#endif -#endif # ifndef HAVE_UINT64_T # define HAVE_UINT64_T 1 # endif @@ -87,6 +80,9 @@ #ifndef INT32_MAX # define INT32_MAX (2147483647) #endif +#ifndef UINT32_MAX +# define UINT32_MAX (4294967295U) +#endif #if HAVE_BYTESWAP_H # include diff --git a/gfx/cairo/cairo/src/cairo-wideint.c b/gfx/cairo/cairo/src/cairo-wideint.c index 78dedcdf0cf0..2e056fa36a2b 100644 --- a/gfx/cairo/cairo/src/cairo-wideint.c +++ b/gfx/cairo/cairo/src/cairo-wideint.c @@ -83,6 +83,38 @@ uint64_shift32 (cairo_uint64_t i) static const cairo_uint64_t uint64_carry32 = { 0, 1 }; +cairo_uint64_t +_cairo_double_to_uint64 (double i) +{ + cairo_uint64_t q; + + q.hi = i * (1. / 4294967296.); + q.lo = i - q.hi * 4294967296.; + return q; +} + +double +_cairo_uint64_to_double (cairo_uint64_t i) +{ + return i.hi * 4294967296. + i.lo; +} + +cairo_int64_t +_cairo_double_to_int64 (double i) +{ + cairo_uint64_t q; + + q.hi = i * (1. / INT32_MAX); + q.lo = i - q.hi * (double)INT32_MAX; + return q; +} + +double +_cairo_int64_to_double (cairo_int64_t i) +{ + return i.hi * INT32_MAX + i.lo; +} + cairo_uint64_t _cairo_uint32_to_uint64 (uint32_t i) { @@ -622,16 +654,16 @@ _cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) return qr; } -cairo_int128_t -_cairo_int128_negate (cairo_int128_t a) +cairo_uint128_t +_cairo_uint128_negate (cairo_uint128_t a) { a.lo = _cairo_uint64_not (a.lo); a.hi = _cairo_uint64_not (a.hi); return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1)); } -cairo_int128_t -_cairo_int128_not (cairo_int128_t a) +cairo_uint128_t +_cairo_uint128_not (cairo_uint128_t a) { a.lo = _cairo_uint64_not (a.lo); a.hi = _cairo_uint64_not (a.hi); @@ -672,7 +704,8 @@ _cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den) * bits then the returned remainder is equal to the divisor, and the * quotient is the largest representable 64 bit integer. It is an * error to call this function with the high 32 bits of @num being - * non-zero. */ + * non-zero. + **/ cairo_uquorem64_t _cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, cairo_uint64_t den) diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c deleted file mode 100644 index e52c2f92ef29..000000000000 --- a/gfx/cairo/cairo/src/cairo-win32-surface.c +++ /dev/null @@ -1,4102 +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 © 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): - * Owen Taylor - * Stuart Parmenter - * Vladimir Vukicevic - */ - -#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" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-paginated-private.h" -#include "cairo-win32-private.h" -#include "cairo-scaled-font-subsets-private.h" -#include "cairo-surface-fallback-private.h" -#include "cairo-surface-clipper-private.h" -#include "cairo-gstate-private.h" -#include "cairo-private.h" -#include -#include -#include - -#if defined(__MINGW32__) && !defined(ETO_PDY) -# define ETO_PDY 0x2000 -#endif - -#undef DEBUG_COMPOSITE - -/* for older SDKs */ -#ifndef SHADEBLENDCAPS -#define SHADEBLENDCAPS 120 -#endif -#ifndef SB_NONE -#define SB_NONE 0x00000000 -#endif - -#define PELS_72DPI ((LONG)(72. / 0.0254)) - -/** - * SECTION:cairo-win32 - * @Title: Win32 Surfaces - * @Short_Description: Microsoft Windows surface support - * @See_Also: #cairo_surface_t - * - * The Microsoft Windows surface is used to render cairo graphics to - * Microsoft Windows windows, bitmaps, and printing device contexts. - * - * The surface returned by cairo_win32_printing_surface_create() is of surface - * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface - * type. - * - * The surface returned by the other win32 constructors is of surface type - * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. - */ - -/** - * CAIRO_HAS_WIN32_SURFACE: - * - * Defined if the Microsoft Windows surface backend is available. - * This macro can be used to conditionally compile backend-specific code. - */ - -static const cairo_surface_backend_t cairo_win32_surface_backend; - -/** - * _cairo_win32_print_gdi_error: - * @context: context string to display along with the error - * - * Helper function to dump out a human readable form of the - * current error code. - * - * Return value: A cairo status code for the error code - **/ -cairo_status_t -_cairo_win32_print_gdi_error (const char *context) -{ - void *lpMsgBuf; - DWORD last_error = GetLastError (); - - if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - last_error, - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR) &lpMsgBuf, - 0, NULL)) { - fprintf (stderr, "%s: Unknown GDI error", context); - } else { - fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf); - - LocalFree (lpMsgBuf); - } - fflush(stderr); - - /* We should switch off of last_status, but we'd either return - * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there - * is no CAIRO_STATUS_UNKNOWN_ERROR. - */ - - return _cairo_error (CAIRO_STATUS_NO_MEMORY); -} - -uint32_t -_cairo_win32_flags_for_dc (HDC dc) -{ - uint32_t flags = 0; - - if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) { - flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY; - - /* These will always be possible, but the actual GetDeviceCaps - * calls will return whether they're accelerated or not. - * We may want to use our own (pixman) routines sometimes - * if they're eventually faster, but for now have GDI do - * everything. - */ - flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; - flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; - } else { - int cap; - - cap = GetDeviceCaps(dc, SHADEBLENDCAPS); - if (cap != SB_NONE) - flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; - - cap = GetDeviceCaps(dc, RASTERCAPS); - if (cap & RC_BITBLT) - flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; - if (cap & RC_STRETCHBLT) - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; - if (cap & RC_STRETCHDIB) - flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; - } - - return flags; -} - -static cairo_status_t -_create_dc_and_bitmap (cairo_win32_surface_t *surface, - HDC original_dc, - cairo_format_t format, - int width, - int height, - unsigned char **bits_out, - int *rowstride_out) -{ - cairo_status_t status; - - BITMAPINFO *bitmap_info = NULL; - struct { - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[2]; - } bmi_stack; - void *bits; - - int num_palette = 0; /* Quiet GCC */ - int i; - - surface->dc = NULL; - surface->bitmap = NULL; - surface->is_dib = FALSE; - - switch (format) { - default: - case CAIRO_FORMAT_INVALID: - return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - num_palette = 0; - break; - - case CAIRO_FORMAT_A8: - num_palette = 256; - break; - - case CAIRO_FORMAT_A1: - num_palette = 2; - break; - } - - if (num_palette > 2) { - bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER)); - if (!bitmap_info) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - bitmap_info = (BITMAPINFO *)&bmi_stack; - } - - bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width; - bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */ - bitmap_info->bmiHeader.biSizeImage = 0; - bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */ - bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */ - bitmap_info->bmiHeader.biPlanes = 1; - - switch (format) { - /* We can't create real RGB24 bitmaps because something seems to - * break if we do, especially if we don't set up an image - * fallback. It could be a bug with using a 24bpp pixman image - * (and creating one with masks). So treat them like 32bpp. - * Note: This causes problems when using BitBlt/AlphaBlend/etc! - * see end of file. - */ - case CAIRO_FORMAT_RGB24: - case CAIRO_FORMAT_ARGB32: - bitmap_info->bmiHeader.biBitCount = 32; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ - bitmap_info->bmiHeader.biClrImportant = 0; - break; - - case CAIRO_FORMAT_A8: - bitmap_info->bmiHeader.biBitCount = 8; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 256; - bitmap_info->bmiHeader.biClrImportant = 0; - - for (i = 0; i < 256; i++) { - bitmap_info->bmiColors[i].rgbBlue = i; - bitmap_info->bmiColors[i].rgbGreen = i; - bitmap_info->bmiColors[i].rgbRed = i; - bitmap_info->bmiColors[i].rgbReserved = 0; - } - - break; - - case CAIRO_FORMAT_A1: - bitmap_info->bmiHeader.biBitCount = 1; - bitmap_info->bmiHeader.biCompression = BI_RGB; - bitmap_info->bmiHeader.biClrUsed = 2; - bitmap_info->bmiHeader.biClrImportant = 0; - - for (i = 0; i < 2; i++) { - bitmap_info->bmiColors[i].rgbBlue = i * 255; - bitmap_info->bmiColors[i].rgbGreen = i * 255; - bitmap_info->bmiColors[i].rgbRed = i * 255; - bitmap_info->bmiColors[i].rgbReserved = 0; - } - - break; - } - - surface->dc = CreateCompatibleDC (original_dc); - if (!surface->dc) - goto FAIL; - - surface->bitmap = CreateDIBSection (surface->dc, - bitmap_info, - DIB_RGB_COLORS, - &bits, - NULL, 0); - if (!surface->bitmap) - goto FAIL; - - surface->is_dib = TRUE; - - GdiFlush(); - - surface->saved_dc_bitmap = SelectObject (surface->dc, - surface->bitmap); - if (!surface->saved_dc_bitmap) - goto FAIL; - - if (bitmap_info && num_palette > 2) - free (bitmap_info); - - if (bits_out) - *bits_out = bits; - - if (rowstride_out) { - /* Windows bitmaps are padded to 32-bit (dword) boundaries */ - switch (format) { - case CAIRO_FORMAT_ARGB32: - case CAIRO_FORMAT_RGB24: - *rowstride_out = 4 * width; - break; - - case CAIRO_FORMAT_A8: - *rowstride_out = (width + 3) & ~3; - break; - - case CAIRO_FORMAT_A1: - *rowstride_out = ((width + 31) & ~31) / 8; - break; - } - } - - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); - - if (bitmap_info && num_palette > 2) - free (bitmap_info); - - if (surface->saved_dc_bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - surface->saved_dc_bitmap = NULL; - } - - if (surface->bitmap) { - DeleteObject (surface->bitmap); - surface->bitmap = NULL; - } - - if (surface->dc) { - DeleteDC (surface->dc); - surface->dc = NULL; - } - - return status; -} - -static cairo_surface_t * -_cairo_win32_surface_create_for_dc (HDC original_dc, - cairo_format_t format, - int width, - int height) -{ - cairo_status_t status; - cairo_win32_surface_t *surface; - unsigned char *bits; - int rowstride; - - if (! CAIRO_FORMAT_VALID (format)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - surface->clip_region = NULL; - - status = _create_dc_and_bitmap (surface, original_dc, format, - width, height, - &bits, &rowstride); - if (status) - goto FAIL; - - surface->image = cairo_image_surface_create_for_data (bits, format, - width, height, rowstride); - status = surface->image->status; - if (status) - goto FAIL; - - surface->format = format; - surface->d3d9surface = NULL; - - surface->clip_rect.x = 0; - surface->clip_rect.y = 0; - surface->clip_rect.width = width; - surface->clip_rect.height = height; - - surface->initial_clip_rgn = NULL; - surface->had_simple_clip = FALSE; - - surface->extents = surface->clip_rect; - surface->font_subsets = NULL; - - _cairo_surface_init (&surface->base, - &cairo_win32_surface_backend, - NULL, /* device */ - _cairo_content_from_format (format)); - - return &surface->base; - - FAIL: - if (surface->bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - DeleteObject (surface->bitmap); - DeleteDC (surface->dc); - } - free (surface); - - return _cairo_surface_create_in_error (status); -} - -static cairo_surface_t * -_cairo_win32_surface_create_similar_internal (void *abstract_src, - cairo_content_t content, - int width, - int height, - cairo_bool_t force_dib) -{ - cairo_win32_surface_t *src = abstract_src; - cairo_format_t format = _cairo_format_from_content (content); - cairo_surface_t *new_surf = NULL; - - /* We force a DIB always if: - * - we need alpha; or - * - the parent is a DIB; or - * - the parent is for printing (because we don't care about the bit depth at that point) - * - * We also might end up with a DIB even if a DDB is requested if DDB creation failed - * due to out of memory. - */ - if (src->is_dib || - (content & CAIRO_CONTENT_ALPHA) || - src->base.backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING) - { - force_dib = TRUE; - } - - if (!force_dib) { - /* try to create a ddb */ - new_surf = cairo_win32_surface_create_with_ddb (src->dc, CAIRO_FORMAT_RGB24, width, height); - - if (new_surf->status != CAIRO_STATUS_SUCCESS) - new_surf = NULL; - } - - if (new_surf == NULL) { - new_surf = _cairo_win32_surface_create_for_dc (src->dc, format, width, height); - } - - return new_surf; -} - -cairo_surface_t * -_cairo_win32_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height) -{ - return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE); -} - -cairo_status_t -_cairo_win32_surface_finish (void *abstract_surface) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) - cairo_surface_destroy (surface->image); - - /* If we created the Bitmap and DC, destroy them */ - if (surface->bitmap) { - SelectObject (surface->dc, surface->saved_dc_bitmap); - DeleteObject (surface->bitmap); - DeleteDC (surface->dc); - } else { - _cairo_win32_restore_initial_clip (surface); - } - - if (surface->d3d9surface) { - IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc); - IDirect3DSurface9_Release (surface->d3d9surface); - } - - if (surface->initial_clip_rgn) - DeleteObject (surface->initial_clip_rgn); - - if (surface->font_subsets != NULL) - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - - return CAIRO_STATUS_SUCCESS; -} - -static void -get_d3d9_dc_and_clear_clip (cairo_win32_surface_t *surface) -{ - IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc); - // The DC that we get back from the surface will not have - // a clip so clear surface->clip_region so that we don't think we have - // one when we don't. - _cairo_win32_surface_set_clip_region (surface, NULL); -} - -static cairo_status_t -_cairo_win32_surface_d3d9_lock_rect (cairo_win32_surface_t *surface, - int x, - int y, - int width, - int height, - cairo_image_surface_t **local_out) -{ - cairo_image_surface_t *local; - cairo_int_status_t status; - - RECT rectin = { x, y, x+width, y+height }; - D3DLOCKED_RECT rectout; - HRESULT hr; - hr = IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc); - hr = IDirect3DSurface9_LockRect (surface->d3d9surface, - &rectout, &rectin, 0); - surface->dc = 0; // Don't use the DC when this is locked! - if (hr) { - get_d3d9_dc_and_clear_clip (surface); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - local = cairo_image_surface_create_for_data (rectout.pBits, - surface->format, - width, height, - rectout.Pitch); - if (local == NULL) { - IDirect3DSurface9_UnlockRect (surface->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - if (local->base.status) { - IDirect3DSurface9_UnlockRect (surface->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - return local->base.status; - } - - *local_out = local; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, - int x, - int y, - int width, - int height, - cairo_win32_surface_t **local_out) -{ - cairo_win32_surface_t *local; - cairo_int_status_t status; - cairo_content_t content = _cairo_content_from_format (surface->format); - - local = - (cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal - (surface, content, width, height, TRUE); - if (local == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (local->base.status) - return local->base.status; - - status = CAIRO_INT_STATUS_UNSUPPORTED; - - /* Only BitBlt if the source surface supports it. */ - if ((surface->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) && - BitBlt (local->dc, - 0, 0, - width, height, - surface->dc, - x, y, - SRCCOPY)) - { - status = CAIRO_STATUS_SUCCESS; - } - - if (status) { - /* If we failed here, most likely the source or dest doesn't - * support BitBlt/AlphaBlend (e.g. a printer). - * You can't reliably get bits from a printer DC, so just fill in - * the surface as white (common case for printing). - */ - - RECT r; - r.left = r.top = 0; - r.right = width; - r.bottom = height; - FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); - } - - *local_out = local; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface) -{ - cairo_win32_surface_t *new_surface; - int width = surface->extents.width + surface->extents.x; - int height = surface->extents.height + surface->extents.y; - - BOOL ok; - HBITMAP oldbitmap; - - new_surface = (cairo_win32_surface_t*) - _cairo_win32_surface_create_for_dc (surface->dc, - surface->format, - width, - height); - - if (new_surface->base.status) - return; - - /* DDB can't be 32bpp, so BitBlt is safe */ - ok = BitBlt (new_surface->dc, - 0, 0, width, height, - surface->dc, - 0, 0, SRCCOPY); - - if (!ok) - goto out; - - /* Now swap around new_surface and surface's internal bitmap - * pointers. */ - DeleteDC (new_surface->dc); - new_surface->dc = NULL; - - oldbitmap = SelectObject (surface->dc, new_surface->bitmap); - DeleteObject (oldbitmap); - - surface->image = new_surface->image; - surface->is_dib = new_surface->is_dib; - surface->bitmap = new_surface->bitmap; - - new_surface->bitmap = NULL; - new_surface->image = NULL; - - /* Finally update flags */ - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - - out: - cairo_surface_destroy ((cairo_surface_t*)new_surface); -} - -static cairo_status_t -_cairo_win32_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (!surface->image && !surface->is_dib && surface->bitmap && - (surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0) - { - /* This is a DDB, and we're being asked to use it as a source for - * something that we couldn't support natively. So turn it into - * a DIB, so that we have an equivalent image surface, as long - * as we're allowed to via flags. - */ - _cairo_win32_convert_ddb_to_dib (surface); - } - - if (surface->image) { - *image_out = (cairo_image_surface_t *)surface->image; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; - } - - if (surface->d3d9surface) { - cairo_image_surface_t *local; - status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, 0, 0, - surface->extents.width, - surface->extents.height, &local); - if (status) - return status; - - *image_out = local; - *image_extra = surface; - } else { - cairo_win32_surface_t *local; - status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, - surface->extents.width, - surface->extents.height, &local); - if (status) - return status; - - *image_out = (cairo_image_surface_t *)local->image; - *image_extra = local; - } - // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points - // to the original surface to get back the d3d9surface and properly unlock. - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = image_extra; - - if (local && local->d3d9surface) { - IDirect3DSurface9_UnlockRect (local->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - cairo_surface_destroy ((cairo_surface_t *)image); - } else { - cairo_surface_destroy ((cairo_surface_t *)local); - } -} - -static cairo_status_t -_cairo_win32_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, - void **image_extra) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->image) { - GdiFlush(); - - *image_out = (cairo_image_surface_t *) surface->image; - *image_extra = NULL; - *image_rect = surface->extents; - return CAIRO_STATUS_SUCCESS; - } - - if (surface->d3d9surface) { - cairo_image_surface_t *local = NULL; - status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, - interest_rect->x, - interest_rect->y, - interest_rect->width, - interest_rect->height, &local); - - if (status) - return status; - - *image_out = local; - *image_extra = surface; - } else { - cairo_win32_surface_t *local = NULL; - status = _cairo_win32_surface_get_subimage (abstract_surface, - interest_rect->x, - interest_rect->y, - interest_rect->width, - interest_rect->height, &local); - - if (status) - return status; - - *image_out = (cairo_image_surface_t *) local->image; - *image_extra = local; - } - // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points - // to the original surface to get back the d3d9surface and properly unlock. - - *image_rect = *interest_rect; - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_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_win32_surface_t *surface = abstract_surface; - cairo_win32_surface_t *local = image_extra; - - if (!local) - return; - - if (local->d3d9surface) { - IDirect3DSurface9_UnlockRect (local->d3d9surface); - get_d3d9_dc_and_clear_clip (surface); - cairo_surface_destroy ((cairo_surface_t *)image); - } else { - - /* clear any clip that's currently set on the surface - so that we can blit uninhibited. */ - _cairo_win32_surface_set_clip_region (surface, NULL); - - if (!BitBlt (surface->dc, - image_rect->x, image_rect->y, - image_rect->width, image_rect->height, - local->dc, - 0, 0, - SRCCOPY)) - _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); - - cairo_surface_destroy ((cairo_surface_t *)local); - } - -} - -cairo_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (surface->clip_region == region) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - /* The semantics we want is that any clip set by cairo combines - * is intersected with the clip on device context that the - * surface was created for. To implement this, we need to - * save the original clip when first setting a clip on surface. - */ - - /* Clear any clip set by cairo, return to the original first */ - status = _cairo_win32_restore_initial_clip (surface); - - /* Then combine any new region with it */ - if (region) { - cairo_rectangle_int_t extents; - int num_rects; - RGNDATA *data; - size_t data_size; - RECT *rects; - int i; - HRGN gdi_region; - - /* Create a GDI region for the cairo region */ - - cairo_region_get_extents (region, &extents); - num_rects = cairo_region_num_rectangles (region); - /* XXX see notes in _cairo_win32_save_initial_clip -- - * this code will interact badly with a HDC which had an initial - * world transform -- we should probably manually transform the - * region rects, because SelectClipRgn takes device units, not - * logical units (unlike IntersectClipRect). - */ - - data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); - data = malloc (data_size); - if (!data) - return _cairo_error(CAIRO_STATUS_NO_MEMORY); - rects = (RECT *)data->Buffer; - - data->rdh.dwSize = sizeof (RGNDATAHEADER); - data->rdh.iType = RDH_RECTANGLES; - data->rdh.nCount = num_rects; - data->rdh.nRgnSize = num_rects * sizeof (RECT); - data->rdh.rcBound.left = extents.x; - data->rdh.rcBound.top = extents.y; - data->rdh.rcBound.right = extents.x + extents.width; - data->rdh.rcBound.bottom = extents.y + extents.height; - - for (i = 0; i < num_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].left = rect.x; - rects[i].top = rect.y; - rects[i].right = rect.x + rect.width; - rects[i].bottom = rect.y + rect.height; - } - - gdi_region = ExtCreateRegion (NULL, data_size, data); - free (data); - - if (!gdi_region) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* AND the new region into our DC */ - if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); - - DeleteObject (gdi_region); - } - - return status; -} - -#if !defined(AC_SRC_OVER) -#define AC_SRC_OVER 0x00 -#pragma pack(1) -typedef struct { - BYTE BlendOp; - BYTE BlendFlags; - BYTE SourceConstantAlpha; - BYTE AlphaFormat; -}BLENDFUNCTION; -#pragma pack() -#endif - -/* for compatibility with VC++ 6 */ -#ifndef AC_SRC_ALPHA -#define AC_SRC_ALPHA 0x01 -#endif - -typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest, - int nXOriginDest, - int nYOriginDest, - int nWidthDest, - int hHeightDest, - HDC hdcSrc, - int nXOriginSrc, - int nYOriginSrc, - int nWidthSrc, - int nHeightSrc, - BLENDFUNCTION blendFunction); - -static cairo_int_status_t -_composite_alpha_blend (cairo_win32_surface_t *dst, - cairo_win32_surface_t *src, - int alpha, - int src_x, - int src_y, - int src_w, - int src_h, - int dst_x, - int dst_y, - int dst_w, - int dst_h) -{ - static unsigned alpha_blend_checked = FALSE; - static cairo_alpha_blend_func_t alpha_blend = NULL; - - BLENDFUNCTION blend_function; - - /* Check for AlphaBlend dynamically to allow compiling on - * MSVC 6 and use on older windows versions - */ - if (!alpha_blend_checked) { - OSVERSIONINFO os; - - os.dwOSVersionInfoSize = sizeof (os); - GetVersionEx (&os); - - /* If running on Win98, disable using AlphaBlend() - * to avoid Win98 AlphaBlend() bug */ - if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId || - os.dwMajorVersion != 4 || os.dwMinorVersion != 10) - { - HMODULE msimg32_dll = LoadLibraryW (L"msimg32"); - - if (msimg32_dll != NULL) - alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll, - "AlphaBlend"); - } - - alpha_blend_checked = TRUE; - } - - if (alpha_blend == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND)) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) - return CAIRO_INT_STATUS_UNSUPPORTED; - - blend_function.BlendOp = AC_SRC_OVER; - blend_function.BlendFlags = 0; - blend_function.SourceConstantAlpha = alpha; - blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; - - if (!alpha_blend (dst->dc, - dst_x, dst_y, - dst_w, dst_h, - src->dc, - src_x, src_y, - src_w, src_h, - blend_function)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)"); - - return CAIRO_STATUS_SUCCESS; -} - -/* makes the alpha channel in a RGB24 surface 0xff */ -static void -make_opaque (cairo_image_surface_t *image, cairo_rectangle_int_t src_r) -{ - int x; int y; - for (y = 0; y < src_r.height; y++) { - for (x = 0; x < src_r.width; x++) { - image->data[(src_r.y + y) * image->stride + (src_r.x + x)*4 + 3] = 0xff; - } - } -} - -static cairo_int_status_t -_cairo_win32_surface_composite_inner (cairo_win32_surface_t *src, - cairo_image_surface_t *src_image, - cairo_win32_surface_t *dst, - cairo_rectangle_int_t src_extents, - cairo_rectangle_int_t src_r, - cairo_rectangle_int_t dst_r, - int alpha, - cairo_bool_t needs_alpha, - cairo_bool_t needs_scale) -{ - /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */ - if (src_image) { - if (needs_alpha || needs_scale) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { - BITMAPINFO bi; - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = src_image->width; - bi.bmiHeader.biHeight = - src_image->height; - bi.bmiHeader.biSizeImage = 0; - bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = BI_RGB; - bi.bmiHeader.biClrUsed = 0; - bi.bmiHeader.biClrImportant = 0; - - /* StretchDIBits is broken with top-down dibs; you need to do some - * special munging to make the coordinate space work (basically, - * need to address everything based on the bottom left, instead of top left, - * and need to tell it to flip the resulting image. - * - * See http://blog.vlad1.com/archives/2006/10/26/134/ and comments. - */ - if (!StretchDIBits (dst->dc, - /* dst x,y,w,h */ - dst_r.x, dst_r.y + dst_r.height - 1, - dst_r.width, - (int) dst_r.height, - /* src x,y,w,h */ - src_r.x, src_extents.height - src_r.y + 1, - src_r.width, - (int) src_r.height, - src_image->data, - &bi, - DIB_RGB_COLORS, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)"); - } - } else if (!needs_alpha) { - if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) { - /* Because we store RGB24 & ARGB32 in the same way GDI has no way - * to ignore the alpha channel from a RGB24 source. Therefore, we set - * the alpha channel in our RGB24 source to opaque so that we can treat - * it like ARGB32. */ - GdiFlush(); - make_opaque(src->image, src_r); - } - /* BitBlt or StretchBlt? */ - if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) { - if (!BitBlt (dst->dc, - dst_r.x, dst_r.y, - dst_r.width, dst_r.height, - src->dc, - src_r.x, src_r.y, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)"); - } else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { - /* StretchBlt? */ - /* XXX check if we want HALFTONE, based on the src filter */ - BOOL success; - int oldmode = SetStretchBltMode(dst->dc, HALFTONE); - success = StretchBlt(dst->dc, - dst_r.x, dst_r.y, - dst_r.width, dst_r.height, - src->dc, - src_r.x, src_r.y, - src_r.width, src_r.height, - SRCCOPY); - SetStretchBltMode(dst->dc, oldmode); - - if (!success) - return _cairo_win32_print_gdi_error ("StretchBlt"); - } - } else if (needs_alpha && !needs_scale) { - RECT r = {0, 0, 5000, 5000}; - //FillRect(dst->dc, &r, GetStockObject(DKGRAY_BRUSH)); - return _composite_alpha_blend (dst, src, alpha, - src_r.x, src_r.y, src_r.width, src_r.height, - dst_r.x, dst_r.y, dst_r.width, dst_r.height); - } - - return CAIRO_STATUS_SUCCESS; -} - -/* from pixman-private.h */ -#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) - -static cairo_int_status_t -_cairo_win32_surface_composite (cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_win32_surface_t *dst = abstract_dst; - cairo_win32_surface_t *src; - cairo_surface_pattern_t *src_surface_pattern; - int alpha; - double scalex, scaley; - cairo_fixed_t x0_fixed, y0_fixed; - cairo_int_status_t status; - - cairo_bool_t needs_alpha, needs_scale, needs_repeat, needs_pad; - cairo_image_surface_t *src_image = NULL; - - cairo_format_t src_format; - cairo_rectangle_int_t src_extents; - - cairo_rectangle_int_t src_r = { src_x, src_y, width, height }; - cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height }; - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "+++ composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n", - op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); -#endif - - /* If the destination can't do any of these, then - * we may as well give up, since this is what we'll - * look to for optimization. - */ - if ((dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT | - CAIRO_WIN32_SURFACE_CAN_ALPHABLEND | - CAIRO_WIN32_SURFACE_CAN_STRETCHBLT | - CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) - == 0) - { - goto UNSUPPORTED; - } - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - goto UNSUPPORTED; - - if (pattern->extend != CAIRO_EXTEND_NONE && - pattern->extend != CAIRO_EXTEND_REPEAT && - pattern->extend != CAIRO_EXTEND_PAD) - goto UNSUPPORTED; - - if (mask_pattern) { - /* FIXME: When we fully support RENDER style 4-channel - * masks we need to check r/g/b != 1.0. - */ - if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) - return CAIRO_INT_STATUS_UNSUPPORTED; - - alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; - } else { - alpha = 255; - } - - src_surface_pattern = (cairo_surface_pattern_t *)pattern; - src = (cairo_win32_surface_t *)src_surface_pattern->surface; - - if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE && - dst->flags & (CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) - { - /* In some very limited cases, we can use StretchDIBits to draw - * an image surface directly: - * - source is CAIRO_FORMAT_ARGB32 - * - dest is CAIRO_FORMAT_ARGB32 - * - alpha is 255 - * - operator is SOURCE or OVER - * - image stride is 4*width - */ - src_image = (cairo_image_surface_t*) src; - - if (src_image->format != CAIRO_FORMAT_RGB24 || - dst->format != CAIRO_FORMAT_RGB24 || - alpha != 255 || - (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || - src_image->stride != (src_image->width * 4)) - { - goto UNSUPPORTED; - } - - src_format = src_image->format; - src_extents.x = 0; - src_extents.y = 0; - src_extents.width = src_image->width; - src_extents.height = src_image->height; - } else if (src->base.backend != dst->base.backend) { - goto UNSUPPORTED; - } else { - src_format = src->format; - src_extents = src->extents; - } - - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "Before check: src size: (%d %d) xy [%d %d] -> dst [%d %d %d %d] {srcmat %f %f %f %f}\n", - src_extents.width, src_extents.height, - src_x, src_y, - dst_x, dst_y, width, height, - pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy); -#endif - - /* We can only use GDI functions if the source and destination rectangles - * are on integer pixel boundaries. Figure that out here. - */ - x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx); - y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy); - - if (pattern->matrix.yx != 0.0 || - pattern->matrix.xy != 0.0 || - !_cairo_fixed_is_integer(x0_fixed) || - !_cairo_fixed_is_integer(y0_fixed)) - { - goto UNSUPPORTED; - } - - scalex = pattern->matrix.xx; - scaley = pattern->matrix.yy; - - src_r.x += _cairo_fixed_integer_part(x0_fixed); - src_r.y += _cairo_fixed_integer_part(y0_fixed); - - /* Success, right? */ - if (scalex == 0.0 || scaley == 0.0) - return CAIRO_STATUS_SUCCESS; - - if (scalex != 1.0 || scaley != 1.0) - goto UNSUPPORTED; - - /* If the src coordinates are outside of the source surface bounds, - * we have to fix them up, because this is an error for the GDI - * functions. - */ - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "before: [%d %d %d %d] -> [%d %d %d %d]\n", - src_r.x, src_r.y, src_r.width, src_r.height, - dst_r.x, dst_r.y, dst_r.width, dst_r.height); - fflush (stderr); -#endif - - /* If the src rectangle doesn't wholly lie within the src extents, - * fudge things. We really need to do fixup on the unpainted - * region -- e.g. the SOURCE operator is broken for areas outside - * of the extents, because it won't clear that area to transparent - * black. - */ - - needs_pad = FALSE; - if (pattern->extend != CAIRO_EXTEND_REPEAT) { - needs_repeat = FALSE; - - /* If the src rect and the extents of the source image don't overlap at all, - * we can't do anything useful here. - */ - if (src_r.x > src_extents.width || src_r.y > src_extents.height || - (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0) - { - if (op == CAIRO_OPERATOR_OVER) - return CAIRO_STATUS_SUCCESS; - goto UNSUPPORTED; - } - - if (src_r.x < 0) { - src_r.width += src_r.x; - - dst_r.width += src_r.x; - dst_r.x -= src_r.x; - - src_r.x = 0; - needs_pad = TRUE; - } - - if (src_r.y < 0) { - src_r.height += src_r.y; - - dst_r.height += src_r.y; - dst_r.y -= src_r.y; - - src_r.y = 0; - needs_pad = TRUE; - } - - if (src_r.x + src_r.width > src_extents.width) { - src_r.width = src_extents.width - src_r.x; - dst_r.width = src_r.width; - needs_pad = TRUE; - } - - if (src_r.y + src_r.height > src_extents.height) { - src_r.height = src_extents.height - src_r.y; - dst_r.height = src_r.height; - needs_pad = TRUE; - } - } else { - needs_repeat = TRUE; - } - - if (pattern->extend == CAIRO_EXTEND_PAD && needs_pad) { - goto UNSUPPORTED; - } - - /* - * Operations that we can do: - * - * AlphaBlend uses the following formula for alpha when not use the per-pixel alpha (AlphaFormat = 0) - * Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0)) - * This turns into Dst.Alpha = Src.Alpha when SCA = 255. - * (http://msdn.microsoft.com/en-us/library/aa921335.aspx) - * - * RGB OVER RGB -> BitBlt (same as SOURCE) - * RGB OVER ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB - * by setting the alpha values of the source to 255. - * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA - * ARGB OVER RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte - * - * RGB OVER RGB + mask -> AlphaBlend, no AC_SRC_ALPHA - * RGB OVER ARGB + mask -> Partially supported, We convert this operation into a ARGB OVER ARGB + mask - * by setting the alpha values of the source to 255. - * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA - * ARGB OVER RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte - * - * RGB SOURCE RGB -> BitBlt - * RGB SOURCE ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB - * by setting the alpha values of the source to 255. - * ARGB SOURCE ARGB -> BitBlt - * ARGB SOURCE RGB -> BitBlt - * - * RGB SOURCE RGB + mask -> unsupported - * RGB SOURCE ARGB + mask -> unsupported - * ARGB SOURCE ARGB + mask -> unsupported - * ARGB SOURCE RGB + mask -> unsupported - */ - - /* - * Figure out what action to take. - */ - if (op == CAIRO_OPERATOR_OVER) { - if (alpha == 0) - return CAIRO_STATUS_SUCCESS; - - if (src_format == dst->format) { - if (alpha == 255 && src_format == CAIRO_FORMAT_RGB24) { - needs_alpha = FALSE; - } else { - needs_alpha = TRUE; - } - } else if (src_format == CAIRO_FORMAT_ARGB32 && - dst->format == CAIRO_FORMAT_RGB24) - { - needs_alpha = TRUE; - } else if (src_format == CAIRO_FORMAT_RGB24 && - dst->format == CAIRO_FORMAT_ARGB32 && - src->image) - { - if (alpha == 255) { - needs_alpha = FALSE; - } else { - needs_alpha = TRUE; - } - } else { - goto UNSUPPORTED; - } - } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) { - if ((src_format == dst->format) || - (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24) || - (src_format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32 && src->image)) - { - needs_alpha = FALSE; - } else { - goto UNSUPPORTED; - } - } else { - goto UNSUPPORTED; - } - - if (scalex == 1.0 && scaley == 1.0) { - needs_scale = FALSE; - } else { - /* Should never be reached until we turn StretchBlt back on */ - needs_scale = TRUE; - } - -#ifdef DEBUG_COMPOSITE - fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n", - src_r.x, src_r.y, src_r.width, src_r.height, - dst_r.x, dst_r.y, dst_r.width, dst_r.height); - fflush (stderr); -#endif - - status = _cairo_win32_surface_set_clip_region (dst, clip_region); - if (status) - return status; - - /* If we need to repeat, we turn the repeated blit into - * a bunch of piece-by-piece blits. - */ - if (needs_repeat) { - cairo_rectangle_int_t piece_src_r, piece_dst_r; - uint32_t rendered_width = 0, rendered_height = 0; - uint32_t to_render_height, to_render_width; - int32_t piece_x, piece_y; - int32_t src_start_x = MOD(src_r.x, src_extents.width); - int32_t src_start_y = MOD(src_r.y, src_extents.height); - - if (needs_scale) - goto UNSUPPORTED; - - /* If both the src and dest have an image, we may as well fall - * back, because it will be faster than our separate blits. - * Our blit code will be fastest when the src is a DDB and the - * destination is a DDB. - */ - if ((src_image || src->image) && dst->image) - goto UNSUPPORTED; - - /* If the src is not a bitmap but an on-screen (or unknown) - * DC, chances are that fallback will be faster. - */ - if (src->bitmap == NULL) - goto UNSUPPORTED; - - /* If we can use PatBlt, just do so */ - if (!src_image && !needs_alpha) - { - HBRUSH brush; - HGDIOBJ old_brush; - POINT old_brush_origin; - - /* Set up the brush with our bitmap */ - brush = CreatePatternBrush (src->bitmap); - - /* SetBrushOrgEx sets the coordinates in the destination DC of where the - * pattern should start. - */ - SetBrushOrgEx (dst->dc, dst_r.x - src_start_x, - dst_r.y - src_start_y, &old_brush_origin); - - old_brush = SelectObject (dst->dc, brush); - - PatBlt (dst->dc, dst_r.x, dst_r.y, dst_r.width, dst_r.height, PATCOPY); - - /* Restore the old brush and pen */ - SetBrushOrgEx (dst->dc, old_brush_origin.x, old_brush_origin.y, NULL); - SelectObject (dst->dc, old_brush); - DeleteObject (brush); - - return CAIRO_STATUS_SUCCESS; - } - - /* If we were not able to use PatBlt, then manually expand out the blit */ - - /* Arbitrary factor; we think that going through - * fallback will be faster if we have to do more - * than this amount of blits in either direction. - */ - if (dst_r.width / src_extents.width > 5 || - dst_r.height / src_extents.height > 5) - goto UNSUPPORTED; - - for (rendered_height = 0; - rendered_height < dst_r.height; - rendered_height += to_render_height) - { - piece_y = (src_start_y + rendered_height) % src_extents.height; - to_render_height = src_extents.height - piece_y; - - if (rendered_height + to_render_height > dst_r.height) - to_render_height = dst_r.height - rendered_height; - - for (rendered_width = 0; - rendered_width < dst_r.width; - rendered_width += to_render_width) - { - piece_x = (src_start_x + rendered_width) % src_extents.width; - to_render_width = src_extents.width - piece_x; - - if (rendered_width + to_render_width > dst_r.width) - to_render_width = dst_r.width - rendered_width; - - piece_src_r.x = piece_x; - piece_src_r.y = piece_y; - piece_src_r.width = to_render_width; - piece_src_r.height = to_render_height; - - piece_dst_r.x = dst_r.x + rendered_width; - piece_dst_r.y = dst_r.y + rendered_height; - piece_dst_r.width = to_render_width; - piece_dst_r.height = to_render_height; - - status = _cairo_win32_surface_composite_inner (src, src_image, dst, - src_extents, piece_src_r, piece_dst_r, - alpha, needs_alpha, needs_scale); - if (status != CAIRO_STATUS_SUCCESS) { - /* Uh oh. If something failed, and it's the first - * piece, then we can jump to UNSUPPORTED. - * Otherwise, this is bad times, because part of the - * rendering was already done. */ - if (rendered_width == 0 && - rendered_height == 0) - { - goto UNSUPPORTED; - } - - return status; - } - } - } - } else { - status = _cairo_win32_surface_composite_inner (src, src_image, dst, - src_extents, src_r, dst_r, - alpha, needs_alpha, needs_scale); - } - - if (status == CAIRO_STATUS_SUCCESS) - return status; - -UNSUPPORTED: - /* Fall back to image surface directly, if this is a DIB surface */ - if (dst->image) { - GdiFlush(); - - return dst->image->backend->composite (op, pattern, mask_pattern, - dst->image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - clip_region); - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -/* This big function tells us how to optimize operators for the - * case of solid destination and constant-alpha source - * - * Note: This function needs revisiting if we add support for - * super-luminescent colors (a == 0, r,g,b > 0) - */ -static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED } -categorize_solid_dest_operator (cairo_operator_t op, - unsigned short alpha) -{ - enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source; - - if (alpha >= 0xff00) - source = SOURCE_SOLID; - else if (alpha < 0x100) - source = SOURCE_TRANSPARENT; - else - source = SOURCE_OTHER; - - switch (op) { - case CAIRO_OPERATOR_CLEAR: /* 0 0 */ - case CAIRO_OPERATOR_OUT: /* 1 - Ab 0 */ - return DO_CLEAR; - break; - - case CAIRO_OPERATOR_SOURCE: /* 1 0 */ - case CAIRO_OPERATOR_IN: /* Ab 0 */ - return DO_SOURCE; - break; - - case CAIRO_OPERATOR_OVER: /* 1 1 - Aa */ - case CAIRO_OPERATOR_ATOP: /* Ab 1 - Aa */ - if (source == SOURCE_SOLID) - return DO_SOURCE; - else if (source == SOURCE_TRANSPARENT) - return DO_NOTHING; - else - return DO_UNSUPPORTED; - break; - - case CAIRO_OPERATOR_DEST_OUT: /* 0 1 - Aa */ - case CAIRO_OPERATOR_XOR: /* 1 - Ab 1 - Aa */ - if (source == SOURCE_SOLID) - return DO_CLEAR; - else if (source == SOURCE_TRANSPARENT) - return DO_NOTHING; - else - return DO_UNSUPPORTED; - break; - - case CAIRO_OPERATOR_DEST: /* 0 1 */ - case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab 1 */ - case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa) 1 */ - return DO_NOTHING; - break; - - case CAIRO_OPERATOR_DEST_IN: /* 0 Aa */ - case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab Aa */ - if (source == SOURCE_SOLID) - return DO_NOTHING; - else if (source == SOURCE_TRANSPARENT) - return DO_CLEAR; - else - return DO_UNSUPPORTED; - break; - - case CAIRO_OPERATOR_ADD: /* 1 1 */ - if (source == SOURCE_TRANSPARENT) - return DO_NOTHING; - else - return DO_UNSUPPORTED; - break; - } - - ASSERT_NOT_REACHED; - return DO_UNSUPPORTED; -} - -static cairo_status_t -_cairo_win32_surface_fill_rectangles_stretchdib (HDC dc, - const cairo_color_t *color, - cairo_rectangle_int_t *rect, - int num_rects) -{ - BITMAPINFO bi; - int pixel = ((color->alpha_short >> 8) << 24) | - ((color->red_short >> 8) << 16) | - ((color->green_short >> 8) << 8) | - (color->blue_short >> 8); - int i; - - /* Experiments suggest that it's impossible to use FillRect to set the alpha value - of a Win32 HDC for a transparent window. So instead we use StretchDIBits of a single - pixel, which does work. */ - bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth = 1; - bi.bmiHeader.biHeight = 1; - bi.bmiHeader.biSizeImage = 0; - bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; - bi.bmiHeader.biPlanes = 1; - bi.bmiHeader.biBitCount = 32; - bi.bmiHeader.biCompression = BI_RGB; - bi.bmiHeader.biClrUsed = 0; - bi.bmiHeader.biClrImportant = 0; - - for (i = 0; i < num_rects; i++) { - if (!StretchDIBits (dc, - /* dst x,y,w,h */ - rect[i].x, rect[i].y, rect[i].width, rect[i].height, - /* src x,y,w,h */ - 0, 0, 1, 1, - &pixel, - &bi, - DIB_RGB_COLORS, - SRCCOPY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles_stretchdib(StretchDIBits)"); - } - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_win32_surface_t *surface = abstract_surface; - cairo_status_t status; - COLORREF new_color; - HBRUSH new_brush; - int i; - - status = _cairo_win32_surface_set_clip_region (surface, NULL); - if (status) - return status; - - if (surface->format == CAIRO_FORMAT_ARGB32 && - (surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) && - (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && color->alpha_short >= 0xff00))) { - return _cairo_win32_surface_fill_rectangles_stretchdib (surface->dc, - color, rects, num_rects); - } - if (surface->format != CAIRO_FORMAT_RGB24) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all - * surfaces with alpha.) - */ - switch (categorize_solid_dest_operator (op, color->alpha_short)) { - case DO_CLEAR: - new_color = RGB (0, 0, 0); - break; - case DO_SOURCE: - new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); - break; - case DO_NOTHING: - return CAIRO_STATUS_SUCCESS; - case DO_UNSUPPORTED: - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - new_brush = CreateSolidBrush (new_color); - if (!new_brush) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); - - for (i = 0; i < num_rects; i++) { - RECT rect; - - rect.left = rects[i].x; - rect.top = rects[i].y; - rect.right = rects[i].x + rects[i].width; - rect.bottom = rects[i].y + rects[i].height; - - if (!FillRect (surface->dc, &rect, new_brush)) - goto FAIL; - } - - DeleteObject (new_brush); - - return CAIRO_STATUS_SUCCESS; - - FAIL: - status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); - - DeleteObject (new_brush); - - return status; -} - -cairo_bool_t -_cairo_win32_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_win32_surface_t *surface = abstract_surface; - - *rectangle = surface->extents; - return TRUE; -} - -static cairo_status_t -_cairo_win32_surface_flush (void *abstract_surface) -{ - return _cairo_win32_surface_set_clip_region (abstract_surface, NULL); -} - -#define STACK_GLYPH_SIZE 256 - -cairo_int_status_t -_cairo_win32_surface_show_glyphs_internal (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs, - cairo_bool_t glyph_indexing) -{ -#ifdef CAIRO_HAS_WIN32_FONT - if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { -#ifdef CAIRO_HAS_DWRITE_FONT - return _cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); -#endif - } else { - cairo_win32_surface_t *dst = surface; - - WORD glyph_buf_stack[STACK_GLYPH_SIZE]; - WORD *glyph_buf = glyph_buf_stack; - int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; - int *dxy_buf = dxy_buf_stack; - - BOOL win_result = 0; - int i, j; - - cairo_solid_pattern_t *solid_pattern; - COLORREF color; - - cairo_matrix_t device_to_logical; - - int start_x, start_y; - double user_x, user_y; - int logical_x, logical_y; - unsigned int glyph_index_option; - - /* We can only handle win32 fonts */ - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only handle opaque solid color sources */ - if (!_cairo_pattern_is_opaque_solid(source)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* We can only handle operator SOURCE or OVER with the destination - * having no alpha */ - if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || - (dst->format != CAIRO_FORMAT_RGB24)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* If we have a fallback mask clip set on the dst, we have - * to go through the fallback path, but only if we're not - * doing this for printing */ - if (clip != NULL) { - if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { - cairo_region_t *clip_region; - cairo_status_t status; - - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - - _cairo_win32_surface_set_clip_region (surface, clip_region); - } - } else { - _cairo_win32_surface_set_clip_region (surface, NULL); - } - - solid_pattern = (cairo_solid_pattern_t *)source; - color = RGB(((int)solid_pattern->color.red_short) >> 8, - ((int)solid_pattern->color.green_short) >> 8, - ((int)solid_pattern->color.blue_short) >> 8); - - cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); - - SaveDC(dst->dc); - - cairo_win32_scaled_font_select_font(scaled_font, dst->dc); - SetTextColor(dst->dc, color); - SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); - SetBkMode(dst->dc, TRANSPARENT); - - if (num_glyphs > STACK_GLYPH_SIZE) { - glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); - dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); - } - - /* 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 - * coordinate due to accumulated rounding error. As a result strings could - * be painted shorter or longer than expected. */ - - user_x = glyphs[0].x; - user_y = glyphs[0].y; - - cairo_matrix_transform_point(&device_to_logical, - &user_x, &user_y); - - logical_x = _cairo_lround (user_x); - logical_y = _cairo_lround (user_y); - - start_x = logical_x; - start_y = logical_y; - - for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { - glyph_buf[i] = (WORD) glyphs[i].index; - if (i == num_glyphs - 1) { - dxy_buf[j] = 0; - dxy_buf[j+1] = 0; - } else { - double next_user_x = glyphs[i+1].x; - double next_user_y = glyphs[i+1].y; - int next_logical_x, next_logical_y; - - cairo_matrix_transform_point(&device_to_logical, - &next_user_x, &next_user_y); - - next_logical_x = _cairo_lround (next_user_x); - next_logical_y = _cairo_lround (next_user_y); - - dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); - dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y); - /* note that GDI coordinate system is inverted */ - - logical_x = next_logical_x; - logical_y = next_logical_y; - } - } - - if (glyph_indexing) - glyph_index_option = ETO_GLYPH_INDEX; - else - glyph_index_option = 0; - - win_result = ExtTextOutW(dst->dc, - start_x, - start_y, - glyph_index_option | ETO_PDY, - NULL, - glyph_buf, - num_glyphs, - dxy_buf); - if (!win_result) { - _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); - } - - RestoreDC(dst->dc, -1); - - if (glyph_buf != glyph_buf_stack) { - free(glyph_buf); - free(dxy_buf); - } - return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; - } -#else - return CAIRO_INT_STATUS_UNSUPPORTED; -#endif -} - -#undef STACK_GLYPH_SIZE - -cairo_int_status_t -_cairo_win32_surface_show_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - return _cairo_win32_surface_show_glyphs_internal (surface, - op, - source, - glyphs, - num_glyphs, - scaled_font, - clip, - remaining_glyphs, - TRUE); -} - -static cairo_surface_t * -cairo_win32_surface_create_internal (HDC hdc, cairo_format_t format) -{ - cairo_win32_surface_t *surface; - - RECT rect; - - surface = malloc (sizeof (cairo_win32_surface_t)); - if (surface == NULL) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { - free (surface); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - surface->clip_region = NULL; - surface->image = NULL; - surface->format = format; - - surface->d3d9surface = NULL; - surface->dc = hdc; - surface->bitmap = NULL; - surface->is_dib = FALSE; - surface->saved_dc_bitmap = NULL; - surface->brush = NULL; - surface->old_brush = NULL; - surface->font_subsets = NULL; - - GetClipBox(hdc, &rect); - surface->extents.x = rect.left; - surface->extents.y = rect.top; - surface->extents.width = rect.right - rect.left; - surface->extents.height = rect.bottom - rect.top; - - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - - _cairo_surface_init (&surface->base, - &cairo_win32_surface_backend, - NULL, /* device */ - _cairo_content_from_format (format)); - - return &surface->base; -} - -/** - * cairo_win32_surface_create: - * @hdc: the DC to create a surface for - * - * Creates a cairo surface that targets the given DC. The DC will be - * queried for its initial clip extents, and this will be used as the - * size of the cairo surface. The resulting surface will always be of - * format %CAIRO_FORMAT_RGB24; should you need another surface format, - * you will need to create one through - * cairo_win32_surface_create_with_dib() or call - * cairo_win32_surface_create_with_alpha. - * - * Return value: the newly created surface - **/ -cairo_surface_t * -cairo_win32_surface_create (HDC hdc) -{ - /* Assume everything comes in as RGB24 */ - return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_RGB24); -} - -/** - * cairo_win32_surface_create_with_alpha: - * @hdc: the DC to create a surface for - * - * Creates a cairo surface that targets the given DC. The DC will be - * queried for its initial clip extents, and this will be used as the - * size of the cairo surface. The resulting surface will always be of - * format %CAIRO_FORMAT_ARGB32; this format is used when drawing into - * transparent windows. - * - * Return value: the newly created surface - * - * Since: 1.10 - **/ -cairo_surface_t * -cairo_win32_surface_create_with_alpha (HDC hdc) -{ - return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_ARGB32); -} - -/** - * cairo_win32_surface_create_with_dib: - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a device-independent-bitmap surface not associated with - * any particular existing surface or device context. The created - * bitmap will be uninitialized. - * - * Return value: the newly created surface - * - * Since: 1.2 - **/ -cairo_surface_t * -cairo_win32_surface_create_with_dib (cairo_format_t format, - int width, - int height) -{ - return _cairo_win32_surface_create_for_dc (NULL, format, width, height); -} - -/** - * cairo_win32_surface_create_with_ddb: - * @hdc: the DC to create a surface for - * @format: format of pixels in the surface to create - * @width: width of the surface, in pixels - * @height: height of the surface, in pixels - * - * Creates a device-independent-bitmap surface not associated with - * any particular existing surface or device context. The created - * bitmap will be uninitialized. - * - * Return value: the newly created surface - * - * Since: 1.4 - **/ -cairo_surface_t * -cairo_win32_surface_create_with_ddb (HDC hdc, - cairo_format_t format, - int width, - int height) -{ - cairo_win32_surface_t *new_surf; - HBITMAP ddb; - HDC screen_dc, ddb_dc; - HBITMAP saved_dc_bitmap; - - if (format != CAIRO_FORMAT_RGB24) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); -/* XXX handle these eventually - format != CAIRO_FORMAT_A8 || - format != CAIRO_FORMAT_A1) -*/ - - if (!hdc) { - screen_dc = GetDC (NULL); - hdc = screen_dc; - } else { - screen_dc = NULL; - } - - ddb_dc = CreateCompatibleDC (hdc); - if (ddb_dc == NULL) { - new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto FINISH; - } - - ddb = CreateCompatibleBitmap (hdc, width, height); - if (ddb == NULL) { - DeleteDC (ddb_dc); - - /* Note that if an app actually does hit this out of memory - * condition, it's going to have lots of other issues, as - * video memory is probably exhausted. However, it can often - * continue using DIBs instead of DDBs. - */ - new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - goto FINISH; - } - - saved_dc_bitmap = SelectObject (ddb_dc, ddb); - - new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc); - new_surf->bitmap = ddb; - new_surf->saved_dc_bitmap = saved_dc_bitmap; - new_surf->is_dib = FALSE; - -FINISH: - if (screen_dc) - ReleaseDC (NULL, screen_dc); - - return (cairo_surface_t*) new_surf; -} - -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_d3dsurface9 (IDirect3DSurface9 *surface) -{ - HDC dc; - cairo_surface_t *win_surface; - - IDirect3DSurface9_AddRef (surface); - IDirect3DSurface9_GetDC (surface, &dc); - win_surface = cairo_win32_surface_create_internal(dc, CAIRO_FORMAT_RGB24); - if (likely(win_surface->status == CAIRO_STATUS_SUCCESS)) { - ((cairo_win32_surface_t*)win_surface)->d3d9surface = surface; - } - return win_surface; - -} -/** - * _cairo_surface_is_win32: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a win32 surface. This will - * return False if this is a win32 printing surface; use - * _cairo_surface_is_win32_printing() to check for that. - * - * Return value: True if the surface is an win32 surface - **/ -int -_cairo_surface_is_win32 (cairo_surface_t *surface) -{ - return surface->backend == &cairo_win32_surface_backend; -} - -/** - * cairo_win32_surface_get_dc - * @surface: a #cairo_surface_t - * - * Returns the HDC associated with this surface, or %NULL if none. - * Also returns %NULL if the surface is not a win32 surface. - * - * Return value: HDC or %NULL if no HDC available. - * - * Since: 1.2 - **/ -HDC -cairo_win32_surface_get_dc (cairo_surface_t *surface) -{ - cairo_win32_surface_t *winsurf; - - if (_cairo_surface_is_win32 (surface)){ - winsurf = (cairo_win32_surface_t *) surface; - - return winsurf->dc; - } - - if (_cairo_surface_is_paginated (surface)) { - cairo_surface_t *target; - - target = _cairo_paginated_surface_get_target (surface); - -#ifndef CAIRO_OMIT_WIN32_PRINTING - if (_cairo_surface_is_win32_printing (target)) { - winsurf = (cairo_win32_surface_t *) target; - return winsurf->dc; - } -#endif - } - - return NULL; -} - - -HDC -cairo_win32_get_dc_with_clip (cairo_t *cr) -{ - cairo_surface_t *surface = cr->gstate->target; - cairo_clip_t clip; - _cairo_clip_init_copy(&clip, &cr->gstate->clip); - - if (_cairo_surface_is_win32 (surface)){ - cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) surface; - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (clip.path) { - status = _cairo_clip_get_region (&clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) { - _cairo_clip_fini(&clip); - return NULL; - } - } - _cairo_win32_surface_set_clip_region (winsurf, clip_region); - - _cairo_clip_fini(&clip); - return winsurf->dc; - } - - if (_cairo_surface_is_paginated (surface)) { - cairo_surface_t *target; - - target = _cairo_paginated_surface_get_target (surface); - -#ifndef CAIRO_OMIT_WIN32_PRINTING - if (_cairo_surface_is_win32_printing (target)) { - cairo_status_t status; - cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) target; - - status = _cairo_surface_clipper_set_clip (&winsurf->clipper, &clip); - - _cairo_clip_fini(&clip); - - if (status) - return NULL; - - return winsurf->dc; - } -#endif - } - - _cairo_clip_fini(&clip); - return NULL; -} - - - -/** - * cairo_win32_surface_get_image - * @surface: a #cairo_surface_t - * - * Returns a #cairo_surface_t image surface that refers to the same bits - * as the DIB of the Win32 surface. If the passed-in win32 surface - * is not a DIB surface, %NULL is returned. - * - * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t), - * or %NULL if the win32 surface is not a DIB. - * - * Since: 1.4 - */ -cairo_surface_t * -cairo_win32_surface_get_image (cairo_surface_t *surface) -{ - if (!_cairo_surface_is_win32(surface)) - return NULL; - - return ((cairo_win32_surface_t*)surface)->image; -} - -static cairo_bool_t -_cairo_win32_surface_is_similar (void *surface_a, - void *surface_b) -{ - cairo_win32_surface_t *a = surface_a; - cairo_win32_surface_t *b = surface_b; - - return a->dc == b->dc; -} - -typedef struct _cairo_win32_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_operator_t op; - const cairo_pattern_t *pattern; - cairo_antialias_t antialias; - - uint8_t *mask_data; - uint32_t mask_stride; - - cairo_image_surface_t *mask; - cairo_win32_surface_t *dst; - cairo_region_t *clip_region; - - cairo_composite_rectangles_t composite_rectangles; -} cairo_win32_surface_span_renderer_t; - -static cairo_status_t -_cairo_win32_surface_span_renderer_render_rows ( - void *abstract_renderer, - int y, - int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - while (height--) - _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride); - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_win32_surface_span_renderer_destroy (void *abstract_renderer) -{ - cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - if (!renderer) return; - - if (renderer->mask != NULL) - cairo_surface_destroy (&renderer->mask->base); - - free (renderer); -} - -static cairo_status_t -_cairo_win32_surface_span_renderer_finish (void *abstract_renderer) -{ - cairo_win32_surface_span_renderer_t *renderer = abstract_renderer; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (renderer->pattern == NULL || renderer->mask == NULL) - return CAIRO_STATUS_SUCCESS; - - status = cairo_surface_status (&renderer->mask->base); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_composite_rectangles_t *rects = &renderer->composite_rectangles; - cairo_win32_surface_t *dst = renderer->dst; - cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&renderer->mask->base); - /* composite onto the image surface directly if we can */ - if (dst->image) { - GdiFlush(); /* XXX: I'm not sure if this needed or not */ - - status = dst->image->backend->composite (renderer->op, - renderer->pattern, mask_pattern, dst->image, - rects->bounded.x, rects->bounded.y, - 0, 0, /* mask.x, mask.y */ - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height, - renderer->clip_region); - } else { - /* otherwise go through the fallback_composite path which - * will do the appropriate surface acquisition */ - status = _cairo_surface_fallback_composite ( - renderer->op, - renderer->pattern, mask_pattern, &dst->base, - rects->bounded.x, rects->bounded.y, - 0, 0, /* mask.x, mask.y */ - rects->bounded.x, rects->bounded.y, - rects->bounded.width, rects->bounded.height, - renderer->clip_region); - } - cairo_pattern_destroy (mask_pattern); - - } - if (status != CAIRO_STATUS_SUCCESS) - return _cairo_span_renderer_set_error (abstract_renderer, - status); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_paint (surface->image, op, source, clip); - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_mask (surface->image, op, source, mask, clip); - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_stroke (surface->image, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_win32_surface_t *surface = abstract_surface; - - if (surface->image) { - return _cairo_surface_fill (surface->image, op, source, - path, fill_rule, - tolerance, antialias, - clip); - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -#include "cairoint.h" - -#include "cairo-boxes-private.h" -#include "cairo-clip-private.h" -#include "cairo-composite-rectangles-private.h" -#include "cairo-error-private.h" -#include "cairo-region-private.h" -#include "cairo-spans-private.h" -#include "cairo-surface-fallback-private.h" - -typedef struct { - cairo_surface_t *dst; - cairo_rectangle_int_t extents; - cairo_image_surface_t *image; - cairo_rectangle_int_t image_rect; - void *image_extra; -} fallback_state_t; - -/** - * _fallback_init: - * - * Acquire destination image surface needed for an image-based - * fallback. - * - * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not - * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all - * went well, or some error status otherwise. - **/ -static cairo_int_status_t -_fallback_init (fallback_state_t *state, - cairo_surface_t *dst, - int x, - int y, - int width, - int height) -{ - cairo_status_t status; - - state->extents.x = x; - state->extents.y = y; - state->extents.width = width; - state->extents.height = height; - - state->dst = dst; - - status = _cairo_surface_acquire_dest_image (dst, &state->extents, - &state->image, &state->image_rect, - &state->image_extra); - if (unlikely (status)) - return status; - - - /* XXX: This NULL value tucked away in state->image is a rather - * ugly interface. Cleaner would be to push the - * CAIRO_INT_STATUS_NOTHING_TO_DO value down into - * _cairo_surface_acquire_dest_image and its backend - * counterparts. */ - assert (state->image != NULL); - - return CAIRO_STATUS_SUCCESS; -} - -static void -_fallback_fini (fallback_state_t *state) -{ - _cairo_surface_release_dest_image (state->dst, &state->extents, - state->image, &state->image_rect, - state->image_extra); -} - -typedef cairo_status_t -(*cairo_draw_func_t) (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region); - -static cairo_status_t -_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, - cairo_clip_t *clip, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *mask; - cairo_region_t *clip_region = NULL, *fallback_region = NULL; - cairo_status_t status; - cairo_bool_t clip_surface = FALSE; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with - * a mask (as called via _cairo_surface_mask) triggers assertion failures. - */ - mask = _cairo_surface_create_similar_solid (dst, - CAIRO_CONTENT_ALPHA, - extents->width, - extents->height, - CAIRO_COLOR_TRANSPARENT, - TRUE); - if (unlikely (mask->status)) - return mask->status; - - if (clip_region && (extents->x || extents->y)) { - fallback_region = cairo_region_copy (clip_region); - status = fallback_region->status; - if (unlikely (status)) - goto CLEANUP_SURFACE; - - cairo_region_translate (fallback_region, - -extents->x, - -extents->y); - clip_region = fallback_region; - } - - status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, mask, - extents->x, extents->y, - extents, - clip_region); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - if (clip_surface) - status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); - - _cairo_pattern_init_for_surface (mask_pattern, mask); - - CLEANUP_SURFACE: - if (fallback_region) - cairo_region_destroy (fallback_region); - cairo_surface_destroy (mask); - - return status; -} - -/* Handles compositing with a clip surface when the operator allows - * us to combine the clip with the mask - */ -static cairo_status_t -_clip_and_composite_with_mask (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_status_t status; - - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_surface_composite (op, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - - _cairo_pattern_fini (&mask_pattern.base); - } - - return status; -} - -/* Handles compositing with a clip surface when we have to do the operation - * in two pieces and combine them together. - */ -static cairo_status_t -_clip_and_composite_combine (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_t *intermediate; - cairo_surface_pattern_t pattern; - cairo_surface_pattern_t clip_pattern; - cairo_surface_t *clip_surface; - int clip_x, clip_y; - cairo_status_t status; - - /* We'd be better off here creating a surface identical in format - * to dst, but we have no way of getting that information. Instead - * we ask the backend to create a similar surface of identical content, - * in the belief that the backend will do something useful - like use - * an identical format. For example, the xlib backend will endeavor to - * use a compatible depth to enable core protocol routines. - */ - intermediate = - _cairo_surface_create_similar_scratch (dst, dst->content, - extents->width, - extents->height); - if (intermediate == NULL) { - intermediate = - _cairo_image_surface_create_with_content (dst->content, - extents->width, - extents->width); - } - if (unlikely (intermediate->status)) - return intermediate->status; - - /* Initialize the intermediate surface from the destination surface */ - _cairo_pattern_init_for_surface (&pattern, dst); - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pattern.base, NULL, intermediate, - extents->x, extents->y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - status = (*draw_func) (draw_closure, op, - src, intermediate, - extents->x, extents->y, - extents, - NULL); - if (unlikely (status)) - goto CLEANUP_SURFACE; - - assert (clip->path != NULL); - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - goto CLEANUP_SURFACE; - - _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); - - /* Combine that with the clip */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, - &clip_pattern.base, NULL, intermediate, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - 0, 0, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Punch the clip out of the destination */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &clip_pattern.base, NULL, dst, - extents->x - clip_x, - extents->y - clip_y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - if (unlikely (status)) - goto CLEANUP_CLIP; - - /* Now add the two results together */ - _cairo_pattern_init_for_surface (&pattern, intermediate); - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - &pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - NULL); - _cairo_pattern_fini (&pattern.base); - - CLEANUP_CLIP: - _cairo_pattern_fini (&clip_pattern.base); - CLEANUP_SURFACE: - cairo_surface_destroy (intermediate); - - return status; -} - -/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's - * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) - */ -static cairo_status_t -_clip_and_composite_source (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_surface_pattern_t mask_pattern; - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - } - - /* Create a surface that is mask IN clip */ - status = _create_composite_mask_pattern (&mask_pattern, - clip, - draw_func, draw_closure, - dst, extents); - if (unlikely (status)) - return status; - - /* Compute dest' = dest OUT (mask IN clip) */ - status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, - &mask_pattern.base, NULL, dst, - 0, 0, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - if (unlikely (status)) - goto CLEANUP_MASK_PATTERN; - - /* Now compute (src IN (mask IN clip)) ADD dest' */ - status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, - src, &mask_pattern.base, dst, - extents->x, extents->y, - 0, 0, - extents->x, extents->y, - extents->width, extents->height, - clip_region); - - CLEANUP_MASK_PATTERN: - _cairo_pattern_fini (&mask_pattern.base); - return status; -} - -static int -_cairo_rectangle_empty (const cairo_rectangle_int_t *rect) -{ - return rect->width == 0 || rect->height == 0; -} - -/** - * _clip_and_composite: - * @clip: a #cairo_clip_t - * @op: the operator to draw with - * @src: source pattern - * @draw_func: function that can be called to draw with the mask onto a surface. - * @draw_closure: data to pass to @draw_func. - * @dst: destination surface - * @extents: rectangle holding a bounding box for the operation; this - * rectangle will be used as the size for the temporary - * surface. - * - * When there is a surface clip, we typically need to create an intermediate - * surface. This function handles the logic of creating a temporary surface - * drawing to it, then compositing the result onto the target surface. - * - * @draw_func is to called to draw the mask; it will be called no more - * than once. - * - * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. - **/ -static cairo_status_t -_clip_and_composite (cairo_clip_t *clip, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_draw_func_t draw_func, - void *draw_closure, - cairo_surface_t *dst, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - - if (_cairo_rectangle_empty (extents)) - /* Nothing to do */ - return CAIRO_STATUS_SUCCESS; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (op == CAIRO_OPERATOR_SOURCE) { - status = _clip_and_composite_source (clip, - src, - draw_func, draw_closure, - dst, extents); - } else { - cairo_bool_t clip_surface = FALSE; - cairo_region_t *clip_region = NULL; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status) || - status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - { - return status; - } - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (clip_surface) { - if (_cairo_operator_bounded_by_mask (op)) { - status = _clip_and_composite_with_mask (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } else { - status = _clip_and_composite_combine (clip, op, - src, - draw_func, draw_closure, - dst, extents); - } - } else { - status = draw_func (draw_closure, op, - src, dst, - 0, 0, - extents, - clip_region); - } - } - - return status; -} - -/* Composites a region representing a set of trapezoids. - */ -static cairo_status_t -_composite_trap_region (cairo_clip_t *clip, - const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - const cairo_rectangle_int_t *extents) -{ - cairo_status_t status; - cairo_surface_pattern_t mask_pattern; - cairo_pattern_t *mask = NULL; - int mask_x = 0, mask_y =0; - - if (clip != NULL) { - cairo_surface_t *clip_surface = NULL; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); - if (unlikely (clip_surface->status)) - return clip_surface->status; - - if (op == CAIRO_OPERATOR_CLEAR) { - src = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); - mask_x = extents->x - clip_x; - mask_y = extents->y - clip_y; - mask = &mask_pattern.base; - } - - status = _cairo_surface_composite (op, src, mask, dst, - extents->x, extents->y, - mask_x, mask_y, - extents->x, extents->y, - extents->width, extents->height, - trap_region); - - if (mask != NULL) - _cairo_pattern_fini (mask); - - return status; -} - -typedef struct { - cairo_traps_t *traps; - cairo_antialias_t antialias; -} cairo_composite_traps_info_t; - -static cairo_status_t -_composite_traps_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_traps_info_t *info = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (dst_x != 0 || dst_y != 0) - _cairo_traps_translate (info->traps, - dst_x, - dst_y); - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - status = _cairo_surface_composite_trapezoids (op, - src, dst, info->antialias, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - info->traps->traps, - info->traps->num_traps, - clip_region); - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -enum { - HAS_CLEAR_REGION = 0x1, -}; - -static cairo_status_t -_clip_and_composite_region (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_region_t *trap_region, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_region_t clear_region; - unsigned int has_region = 0; - cairo_status_t status; - - if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { - /* If we optimize drawing with an unbounded operator to - * _cairo_surface_fill_rectangles() or to drawing with a - * clip region, then we have an additional region to clear. - */ - _cairo_region_init_rectangle (&clear_region, extents); - status = cairo_region_subtract (&clear_region, trap_region); - if (unlikely (status)) - return status; - - if (! cairo_region_is_empty (&clear_region)) - has_region |= HAS_CLEAR_REGION; - } - - if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && - clip == NULL) - { - const cairo_color_t *color; - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, trap_region); - } else { - /* For a simple rectangle, we can just use composite(), for more - * rectangles, we have to set a clip region. The cost of rasterizing - * trapezoids is pretty high for most backends currently, so it's - * worthwhile even if a region is needed. - * - * If we have a clip surface, we set it as the mask; this only works - * for bounded operators other than SOURCE; for unbounded operators, - * clip and mask cannot be interchanged. For SOURCE, the operator - * as implemented by the backends is different in its handling - * of the mask then what we want. - * - * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has - * more than rectangle and the destination doesn't support clip - * regions. In that case, we fall through. - */ - status = _composite_trap_region (clip, src, op, dst, - trap_region, extents); - } - - if (has_region & HAS_CLEAR_REGION) { - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_surface_fill_region (dst, - CAIRO_OPERATOR_CLEAR, - CAIRO_COLOR_TRANSPARENT, - &clear_region); - } - _cairo_region_fini (&clear_region); - } - - return status; -} - -/* avoid using region code to re-validate boxes */ -static cairo_status_t -_fill_rectangles (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - const cairo_color_t *color; - cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; - cairo_rectangle_int_t *rects = stack_rects; - cairo_status_t status; - int i; - - if (! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: convert clip region to geometric boxes? */ - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX: fallback for the region_subtract() operation */ - if (! _cairo_operator_bounded_by_mask (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->has_intersections) { - if (traps->is_rectangular) { - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - } else { - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - } - if (unlikely (status)) - return status; - } - - for (i = 0; i < traps->num_traps; i++) { - if (! _cairo_fixed_is_integer (traps->traps[i].top) || - ! _cairo_fixed_is_integer (traps->traps[i].bottom) || - ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - - if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (traps->num_traps, - sizeof (cairo_rectangle_int_t)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < traps->num_traps; i++) { - int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); - int y1 = _cairo_fixed_integer_part (traps->traps[i].top); - int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); - int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); - - rects[i].x = x1; - rects[i].y = y1; - rects[i].width = x2 - x1; - rects[i].height = y2 - y1; - } - - if (op == CAIRO_OPERATOR_CLEAR) - color = CAIRO_COLOR_TRANSPARENT; - else - color = &((cairo_solid_pattern_t *)src)->color; - - status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); - - if (rects != stack_rects) - free (rects); - - return status; -} - -/* fast-path for very common composite of a single rectangle */ -static cairo_status_t -_composite_rectangle (cairo_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_traps_t *traps, - cairo_clip_t *clip) -{ - cairo_rectangle_int_t rect; - - if (clip != NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_fixed_is_integer (traps->traps[0].top) || - ! _cairo_fixed_is_integer (traps->traps[0].bottom) || - ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || - ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) - { - traps->maybe_region = FALSE; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); - rect.y = _cairo_fixed_integer_part (traps->traps[0].top); - rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; - rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; - - return _cairo_surface_composite (op, src, NULL, dst, - rect.x, rect.y, - 0, 0, - rect.x, rect.y, - rect.width, rect.height, - NULL); -} - -/* Warning: This call modifies the coordinates of traps */ -static cairo_status_t -_clip_and_composite_trapezoids (const cairo_pattern_t *src, - cairo_operator_t op, - cairo_surface_t *dst, - cairo_traps_t *traps, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents) -{ - cairo_composite_traps_info_t traps_info; - cairo_region_t *clip_region = NULL; - cairo_bool_t clip_surface = FALSE; - cairo_status_t status; - - if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) - return CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_STATUS_SUCCESS; - - clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* Use a fast path if the trapezoids consist of a simple region, - * but we can only do this if we do not have a clip surface, or can - * substitute the mask with the clip. - */ - if (! clip_surface || - (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) - { - cairo_region_t *trap_region = NULL; - - if (_cairo_operator_bounded_by_source (op)) { - status = _fill_rectangles (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _composite_rectangle (dst, op, src, traps, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_traps_extract_region (traps, &trap_region); - if (unlikely (_cairo_status_is_error (status))) - return status; - - if (trap_region != NULL) { - status = cairo_region_intersect_rectangle (trap_region, extents); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - - if (clip_region != NULL) { - status = cairo_region_intersect (trap_region, clip_region); - if (unlikely (status)) { - cairo_region_destroy (trap_region); - return status; - } - } - - if (_cairo_operator_bounded_by_mask (op)) { - cairo_rectangle_int_t trap_extents; - - cairo_region_get_extents (trap_region, &trap_extents); - if (! _cairo_rectangle_intersect (extents, &trap_extents)) { - cairo_region_destroy (trap_region); - return CAIRO_STATUS_SUCCESS; - } - } - - status = _clip_and_composite_region (src, op, dst, - trap_region, - clip_surface ? clip : NULL, - extents); - cairo_region_destroy (trap_region); - - if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) - return status; - } - } - - /* No fast path, exclude self-intersections and clip trapezoids. */ - if (traps->has_intersections) { - if (traps->is_rectangular) - status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); - else if (traps->is_rectilinear) - status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); - else - status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - return status; - } - - /* Otherwise render the trapezoids to a mask and composite in the usual - * fashion. - */ - traps_info.traps = traps; - traps_info.antialias = antialias; - - return _clip_and_composite (clip, op, src, - _composite_traps_draw_func, - &traps_info, dst, extents); -} - -typedef struct { - cairo_polygon_t *polygon; - cairo_fill_rule_t fill_rule; - cairo_antialias_t antialias; -} cairo_composite_spans_info_t; - -static cairo_status_t -_composite_spans_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_rectangles_t rects; - cairo_composite_spans_info_t *info = closure; - - rects.source = *extents; - rects.mask = *extents; - rects.bounded = *extents; - /* The incoming dst_x/y are where we're pretending the origin of - * the dst surface is -- *not* the offset of a rectangle where - * we'd like to place the result. */ - rects.bounded.x -= dst_x; - rects.bounded.y -= dst_y; - rects.unbounded = rects.bounded; - rects.is_bounded = _cairo_operator_bounded_by_either (op); - - return _cairo_surface_composite_polygon (dst, op, src, - info->fill_rule, - info->antialias, - &rects, - info->polygon, - clip_region); -} - -static cairo_status_t -_cairo_win32_surface_span_renderer_composite - (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_image_surface_t *mask, - cairo_win32_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (src == NULL || mask == NULL) - return CAIRO_STATUS_SUCCESS; - - status = cairo_surface_status (&mask->base); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&mask->base); - /* composite onto the image surface directly if we can */ - if (dst->image) { - GdiFlush(); /* XXX: I'm not sure if this needed or not */ - - status = dst->image->backend->composite (op, - src, mask_pattern, dst->image, - extents->x, extents->y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } else { - /* otherwise go through the fallback_composite path which - * will do the appropriate surface acquisition */ - status = _cairo_surface_fallback_composite ( - op, - src, mask_pattern, &dst->base, - extents->x, extents->y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } - cairo_pattern_destroy (mask_pattern); - - } - return status; -} - -typedef struct _cairo_image_surface_span_renderer { - cairo_span_renderer_t base; - - uint8_t *mask_data; - uint32_t mask_stride; -} cairo_image_surface_span_renderer_t; - -cairo_status_t -_cairo_image_surface_span (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans); - -static cairo_status_t -_composite_spans (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_composite_spans_info_t *info = closure; - cairo_image_surface_span_renderer_t renderer; - cairo_scan_converter_t *converter; - cairo_image_surface_t *mask; - cairo_status_t status; - - converter = _cairo_tor_scan_converter_create (extents->x, extents->y, - extents->x + extents->width, - extents->y + extents->height, - info->fill_rule); - status = converter->add_polygon (converter, info->polygon); - if (unlikely (status)) - goto CLEANUP_CONVERTER; - - /* TODO: support rendering to A1 surfaces (or: go add span - * compositing to pixman.) */ - - { - mask = cairo_image_surface_create (CAIRO_FORMAT_A8, - extents->width, - extents->height); - - if (cairo_surface_status(&mask->base)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_CONVERTER; - } - } - - renderer.base.render_rows = _cairo_image_surface_span; - renderer.mask_stride = cairo_image_surface_get_stride (mask); - renderer.mask_data = cairo_image_surface_get_data (mask); - renderer.mask_data -= extents->y * renderer.mask_stride + extents->x; - - status = converter->generate (converter, &renderer.base); - if (unlikely (status)) - goto CLEANUP_RENDERER; - - _cairo_win32_surface_span_renderer_composite - (closure, - op, - src, - mask, - (cairo_win32_surface_t*)dst, // ewwww - dst_x, - dst_y, - extents, - clip_region); -#if 0 - { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y); - if (unlikely (src == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_RENDERER; - } - - pixman_image_composite32 (_pixman_operator (op), src, mask, dst, - extents->x + src_x, extents->y + src_y, - 0, 0, /* mask.x, mask.y */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height); - pixman_image_unref (src); - } -#endif - CLEANUP_RENDERER: - cairo_surface_destroy (mask); - CLEANUP_CONVERTER: - converter->destroy (converter); - return status; -} - - - -cairo_status_t -_cairo_win32_surface_fallback_paint (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip) -{ - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_clip_path_t *clip_path = clip ? clip->path : NULL; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - cairo_boxes_t boxes; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_status_t status; - cairo_traps_t traps; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - /* If the clip cannot be reduced to a set of boxes, we will need to - * use a clipmask. Paint is special as it is the only operation that - * does not implicitly use a mask, so we may be able to reduce this - * operation to a fill... - */ - if (clip != NULL && clip_path->prev == NULL && - _cairo_operator_bounded_by_mask (op)) - { - return _cairo_surface_fill (surface, op, source, - &clip_path->path, - clip_path->fill_rule, - clip_path->tolerance, - clip_path->antialias, - NULL); - } - - /* meh, surface-fallback is dying anyway... */ - _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); - status = _cairo_traps_init_boxes (&traps, &boxes); - if (unlikely (status)) - goto CLEANUP_BOXES; - - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, CAIRO_ANTIALIAS_DEFAULT, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - _cairo_traps_fini (&traps); - -CLEANUP_BOXES: - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -static cairo_status_t -_cairo_surface_mask_draw_func (void *closure, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_surface_t *dst, - int dst_x, - int dst_y, - const cairo_rectangle_int_t *extents, - cairo_region_t *clip_region) -{ - cairo_pattern_t *mask = closure; - cairo_status_t status; - cairo_region_t *extents_region = NULL; - - if (clip_region == NULL && - !_cairo_operator_bounded_by_source (op)) { - extents_region = cairo_region_create_rectangle (extents); - if (unlikely (extents_region->status)) - return extents_region->status; - cairo_region_translate (extents_region, -dst_x, -dst_y); - clip_region = extents_region; - } - - if (src) { - status = _cairo_surface_composite (op, - src, mask, dst, - extents->x, extents->y, - extents->x, extents->y, - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } else { - status = _cairo_surface_composite (op, - mask, NULL, dst, - extents->x, extents->y, - 0, 0, /* unused */ - extents->x - dst_x, extents->y - dst_y, - extents->width, extents->height, - clip_region); - } - - if (extents_region) - cairo_region_destroy (extents_region); - - return status; -} - -cairo_status_t -_cairo_win32_surface_fallback_mask (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip) -{ - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &rect, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - if (clip != NULL && extents.is_bounded) { - status = _cairo_clip_rectangle (clip, &extents.bounded); - if (unlikely (status)) - return status; - } - - return _clip_and_composite (clip, op, source, - _cairo_surface_mask_draw_func, - (void *) mask, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); -} - -cairo_status_t -_cairo_win32_surface_fallback_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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, - cairo_clip_t *clip) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &rect, - op, source, - path, stroke_style, ctm, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - if (path->is_rectilinear) { - status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, - stroke_style, - ctm, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_stroke_to_polygon (path, - stroke_style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - if (antialias != CAIRO_ANTIALIAS_NONE) { - cairo_composite_spans_info_t info; - - info.polygon = &polygon; - info.fill_rule = CAIRO_FILL_RULE_WINDING; - info.antialias = antialias; - - status = _clip_and_composite (clip, op, source, - _composite_spans, - &info, surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -cairo_status_t -_cairo_win32_surface_fallback_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip) -{ - cairo_polygon_t polygon; - cairo_traps_t traps; - cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; - int num_boxes = ARRAY_LENGTH (boxes_stack); - cairo_bool_t is_rectilinear; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - if (_cairo_clip_contains_extents (clip, &extents)) - clip = NULL; - - status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); - if (unlikely (status)) - return status; - - _cairo_traps_init (&traps); - _cairo_traps_limit (&traps, clip_boxes, num_boxes); - - _cairo_polygon_init (&polygon); - _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); - - if (_cairo_path_fixed_fill_is_empty (path)) - goto DO_TRAPS; - - is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); - if (is_rectilinear) { - status = _cairo_path_fixed_fill_rectilinear_to_traps (path, - fill_rule, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (_cairo_status_is_error (status)) - goto CLEANUP; - } - - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (unlikely (status)) - goto CLEANUP; - - if (polygon.num_edges == 0) - goto DO_TRAPS; - - if (_cairo_operator_bounded_by_mask (op)) { - _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - goto CLEANUP; - } - - if (is_rectilinear) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, - &polygon, - fill_rule); - if (likely (status == CAIRO_STATUS_SUCCESS)) - goto DO_TRAPS; - - if (unlikely (_cairo_status_is_error (status))) - goto CLEANUP; - } - - - if (antialias != CAIRO_ANTIALIAS_NONE) { - cairo_composite_spans_info_t info; - - info.polygon = &polygon; - info.fill_rule = fill_rule; - info.antialias = antialias; - - status = _clip_and_composite (clip, op, source, - _composite_spans, - &info, surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - fill_rule); - if (unlikely (status)) - goto CLEANUP; - - DO_TRAPS: - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - if (clip_boxes != boxes_stack) - free (clip_boxes); - - return status; -} - -static const cairo_surface_backend_t cairo_win32_surface_backend = { - CAIRO_SURFACE_TYPE_WIN32, - _cairo_win32_surface_create_similar, - _cairo_win32_surface_finish, - _cairo_win32_surface_acquire_source_image, - _cairo_win32_surface_release_source_image, - _cairo_win32_surface_acquire_dest_image, - _cairo_win32_surface_release_dest_image, - NULL, /* clone similar */ - _cairo_win32_surface_composite, - _cairo_win32_surface_fill_rectangles, - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_win32_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - _cairo_win32_surface_flush, - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - _cairo_win32_surface_fallback_paint, - _cairo_win32_surface_fallback_mask, - _cairo_win32_surface_fallback_stroke, - _cairo_win32_surface_fallback_fill, - _cairo_win32_surface_show_glyphs, - - NULL, /* snapshot */ - _cairo_win32_surface_is_similar, -}; - -/* Notes: - * - * Win32 alpha-understanding functions - * - * BitBlt - will copy full 32 bits from a 32bpp DIB to result - * (so it's safe to use for ARGB32->ARGB32 SOURCE blits) - * (but not safe going RGB24->ARGB32, if RGB24 is also represented - * as a 32bpp DIB, since the alpha isn't discarded!) - * - * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set, - * it will still copy over the src alpha, because the SCA value (255) will be - * multiplied by all the src components. - */ - - -cairo_int_status_t -_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_surface_t *surface) -{ - RECT rect; - int clipBoxType; - int gm; - XFORM saved_xform; - - /* GetClipBox/GetClipRgn and friends interact badly with a world transform - * set. GetClipBox returns values in logical (transformed) coordinates; - * it's unclear what GetClipRgn returns, because the region is empty in the - * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. - * Similarily, IntersectClipRect works in logical units, whereas SelectClipRgn - * works in device units. - * - * So, avoid the whole mess and get rid of the world transform - * while we store our initial data and when we restore initial coordinates. - * - * XXX we may need to modify x/y by the ViewportOrg or WindowOrg - * here in GM_COMPATIBLE; unclear. - */ - gm = GetGraphicsMode (hdc); - if (gm == GM_ADVANCED) { - GetWorldTransform (hdc, &saved_xform); - ModifyWorldTransform (hdc, NULL, MWT_IDENTITY); - } - - clipBoxType = GetClipBox (hdc, &rect); - if (clipBoxType == ERROR) { - _cairo_win32_print_gdi_error ("cairo_win32_surface_create"); - SetGraphicsMode (hdc, gm); - /* XXX: Can we make a more reasonable guess at the error cause here? */ - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - surface->clip_rect.x = rect.left; - surface->clip_rect.y = rect.top; - surface->clip_rect.width = rect.right - rect.left; - surface->clip_rect.height = rect.bottom - rect.top; - - surface->initial_clip_rgn = NULL; - surface->had_simple_clip = FALSE; - - if (clipBoxType == COMPLEXREGION) { - surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0); - if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) { - DeleteObject(surface->initial_clip_rgn); - surface->initial_clip_rgn = NULL; - } - } else if (clipBoxType == SIMPLEREGION) { - surface->had_simple_clip = TRUE; - } - - if (gm == GM_ADVANCED) - SetWorldTransform (hdc, &saved_xform); - - return CAIRO_STATUS_SUCCESS; -} - -cairo_int_status_t -_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - XFORM saved_xform; - int gm = GetGraphicsMode (surface->dc); - if (gm == GM_ADVANCED) { - GetWorldTransform (surface->dc, &saved_xform); - ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY); - } - - /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ - SelectClipRgn (surface->dc, surface->initial_clip_rgn); - - if (surface->had_simple_clip) { - /* then if we had a simple clip, intersect */ - IntersectClipRect (surface->dc, - surface->clip_rect.x, - surface->clip_rect.y, - surface->clip_rect.x + surface->clip_rect.width, - surface->clip_rect.y + surface->clip_rect.height); - } - - if (gm == GM_ADVANCED) - SetWorldTransform (surface->dc, &saved_xform); - - return status; -} - -void -_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) -{ - RGNDATA *rd; - unsigned int z; - - if (header) - fprintf (stderr, "%s\n", header); - - if (rgn == NULL) { - fprintf (stderr, " NULL\n"); - } - - z = GetRegionData(rgn, 0, NULL); - rd = (RGNDATA*) malloc(z); - z = GetRegionData(rgn, z, rd); - - fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", - rd->rdh.nCount, - rd->rdh.rcBound.left, - rd->rdh.rcBound.top, - rd->rdh.rcBound.right - rd->rdh.rcBound.left, - rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); - - for (z = 0; z < rd->rdh.nCount; z++) { - RECT r = ((RECT*)rd->Buffer)[z]; - fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", - z, r.left, r.top, r.right - r.left, r.bottom - r.top); - } - - free(rd); - fflush (stderr); -} - -/** - * cairo_win32_surface_set_can_convert_to_dib - * @surface: a #cairo_surface_t - * @can_convert: a #cairo_bool_t indicating whether this surface can - * be coverted to a DIB if necessary - * - * A DDB surface with this flag set can be converted to a DIB if it's - * used as a source in a way that GDI can't natively handle; for - * example, drawing a RGB24 DDB onto an ARGB32 DIB. Doing this - * conversion results in a significant speed optimization, because we - * can call on pixman to perform the operation natively, instead of - * reading the data from the DC each time. - * - * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully - * changed, or an error otherwise. - * - */ -cairo_status_t -cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - if (surface->bitmap) { - if (can_convert) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB; - else - surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB; - } - - return CAIRO_STATUS_SUCCESS; -} - -/** - * cairo_win32_surface_get_can_convert_to_dib - * @surface: a #cairo_surface_t - * @can_convert: a #cairo_bool_t* that receives the return value - * - * Returns the value of the flag indicating whether the surface can be - * converted to a DIB if necessary, as set by - * cairo_win32_surface_set_can_convert_to_dib. - * - * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully - * retreived, or an error otherwise. - * - */ -cairo_status_t -cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0); - return CAIRO_STATUS_SUCCESS; -} - -int -cairo_win32_surface_get_width (cairo_surface_t *asurface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - return surface->extents.width; -} - -int -cairo_win32_surface_get_height (cairo_surface_t *asurface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface; - if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32) - return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; - - return surface->extents.height; -} diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h index 02c9ac1025cc..db4cac69f495 100644 --- a/gfx/cairo/cairo/src/cairo-win32.h +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -49,7 +49,8 @@ cairo_public cairo_surface_t * cairo_win32_surface_create (HDC hdc); cairo_public cairo_surface_t * -cairo_win32_surface_create_with_alpha (HDC hdc); +cairo_win32_surface_create_with_format (HDC hdc, + cairo_format_t format); cairo_public cairo_surface_t * cairo_win32_printing_surface_create (HDC hdc); @@ -64,29 +65,13 @@ cairo_public cairo_surface_t * cairo_win32_surface_create_with_dib (cairo_format_t format, int width, int height); + cairo_public HDC cairo_win32_surface_get_dc (cairo_surface_t *surface); -cairo_public HDC -cairo_win32_get_dc_with_clip (cairo_t *cr); - cairo_public cairo_surface_t * cairo_win32_surface_get_image (cairo_surface_t *surface); -cairo_public cairo_status_t -cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert); - -cairo_public cairo_status_t -cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert); - -BYTE cairo_win32_get_system_text_quality (void); - -cairo_public int -cairo_win32_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_win32_surface_get_height (cairo_surface_t *surface); - #if CAIRO_HAS_WIN32_FONT /* @@ -122,35 +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, void *dwrite_font_face); - -void -cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed); - -void -cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force); - -cairo_bool_t -cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font); - -void -cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, int geometry, int mode); - -int -cairo_dwrite_get_cleartype_rendering_mode(); - -#endif /* CAIRO_HAS_DWRITE_FONT */ - -struct IDirect3DSurface9; -cairo_public cairo_surface_t * -cairo_win32_surface_create_with_d3dsurface9 (struct IDirect3DSurface9 *surface); - CAIRO_END_DECLS #else /* CAIRO_HAS_WIN32_SURFACE */ diff --git a/gfx/cairo/cairo/src/cairo-xcb-connection-core.c b/gfx/cairo/cairo/src/cairo-xcb-connection-core.c new file mode 100644 index 000000000000..e01dc1a83619 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-connection-core.c @@ -0,0 +1,300 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include + +xcb_pixmap_t +_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection, + uint8_t depth, + xcb_drawable_t drawable, + uint16_t width, + uint16_t height) +{ + xcb_pixmap_t pixmap = _cairo_xcb_connection_get_xid (connection); + + assert (width > 0); + assert (height > 0); + xcb_create_pixmap (connection->xcb_connection, + depth, pixmap, drawable, + width, height); + return pixmap; +} + +void +_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection, + xcb_pixmap_t pixmap) +{ + xcb_free_pixmap (connection->xcb_connection, pixmap); + _cairo_xcb_connection_put_xid (connection, pixmap); +} + +xcb_gcontext_t +_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection, + xcb_drawable_t drawable, + uint32_t value_mask, + uint32_t *values) +{ + xcb_gcontext_t gc = _cairo_xcb_connection_get_xid (connection); + xcb_create_gc (connection->xcb_connection, gc, drawable, + value_mask, values); + return gc; +} + +void +_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc) +{ + xcb_free_gc (connection->xcb_connection, gc); + _cairo_xcb_connection_put_xid (connection, gc); +} + +void +_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc, + uint32_t value_mask, + uint32_t *values) +{ + xcb_change_gc (connection->xcb_connection, gc, + value_mask, values); +} + +void +_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height) +{ + xcb_copy_area (connection->xcb_connection, src, dst, gc, + src_x, src_y, dst_x, dst_y, width, height); +} + +void +_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint32_t num_rectangles, + xcb_rectangle_t *rectangles) +{ + xcb_poly_fill_rectangle (connection->xcb_connection, dst, gc, + num_rectangles, rectangles); +} + +void +_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t stride, + void *data) +{ + const uint32_t req_size = 18; + uint32_t length = height * stride; + uint32_t len = (req_size + length) >> 2; + + if (len < connection->maximum_request_length) { + xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, + dst, gc, width, height, dst_x, dst_y, 0, depth, + length, data); + } else { + int rows = (connection->maximum_request_length - req_size - 4) / stride; + if (rows > 0) { + do { + if (rows > height) + rows = height; + + length = rows * stride; + + xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, + dst, gc, width, rows, dst_x, dst_y, 0, depth, length, data); + + height -= rows; + dst_y += rows; + data = (char *) data + length; + } while (height); + } else { + ASSERT_NOT_REACHED; + } + } +} + +static void +_cairo_xcb_connection_do_put_subimage (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint16_t cpp, + int stride, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + void *_data) +{ + xcb_protocol_request_t xcb_req = { + 0 /* count */, + 0 /* ext */, + XCB_PUT_IMAGE /* opcode */, + 1 /* isvoid (doesn't cause a reply) */ + }; + xcb_put_image_request_t req; + struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)]; + struct iovec *vec = vec_stack; + uint32_t len = 0; + uint8_t *data = _data; + int n = 3; + /* Two extra entries are needed for xcb, two for us */ + int entries_needed = height + 2 + 2; + + req.format = XCB_IMAGE_FORMAT_Z_PIXMAP; + req.drawable = dst; + req.gc = gc; + req.width = width; + req.height = height; + req.dst_x = dst_x; + req.dst_y = dst_y; + req.left_pad = 0; + req.depth = depth; + req.pad0[0] = 0; + req.pad0[1] = 0; + + if (entries_needed > ARRAY_LENGTH (vec_stack)) { + vec = _cairo_malloc_ab (entries_needed, sizeof (struct iovec)); + if (unlikely (vec == NULL)) { + /* XXX loop over ARRAY_LENGTH (vec_stack) */ + return; + } + } + + data += src_y * stride + src_x * cpp; + /* vec[1] will be used in XCB if it has to use BigRequests or insert a sync, + * vec[0] is used if the internal queue needs to be flushed. */ + vec[2].iov_base = (char *) &req; + vec[2].iov_len = sizeof (req); + + /* Now comes the actual data */ + while (height--) { + vec[n].iov_base = data; + vec[n].iov_len = cpp * width; + len += cpp * width; + data += stride; + n++; + } + + /* And again some padding */ + vec[n].iov_base = 0; + vec[n].iov_len = -len & 3; + n++; + + /* For efficiency reasons, this functions writes the request "directly" to + * the xcb connection to avoid having to copy the data around. */ + assert (n == entries_needed); + xcb_req.count = n - 2; + xcb_send_request (connection->xcb_connection, 0, &vec[2], &xcb_req); + + if (vec != vec_stack) + free (vec); +} + +void +_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint16_t cpp, + int stride, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + void *_data) +{ + const uint32_t req_size = sizeof(xcb_put_image_request_t); + uint32_t length = height * cpp * width; + uint32_t len = (req_size + length) >> 2; + + if (len < connection->maximum_request_length) { + _cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y, + width, height, cpp, stride, dst_x, dst_y, depth, _data); + } else { + int rows = (connection->maximum_request_length - req_size - 4) / (cpp * width); + if (rows > 0) { + do { + if (rows > height) + rows = height; + + _cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y, + width, rows, cpp, stride, dst_x, dst_y, depth, _data); + + height -= rows; + dst_y += rows; + _data = (char *) _data + stride * rows; + } while (height); + } else { + ASSERT_NOT_REACHED; + } + } +} + +xcb_get_image_reply_t * +_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height) +{ + return xcb_get_image_reply (connection->xcb_connection, + xcb_get_image (connection->xcb_connection, + XCB_IMAGE_FORMAT_Z_PIXMAP, + src, + src_x, src_y, + width, height, + (uint32_t) -1), + NULL); +} diff --git a/gfx/cairo/cairo/src/cairo-xcb-connection-render.c b/gfx/cairo/cairo/src/cairo-xcb-connection-render.c new file mode 100644 index 000000000000..61119653e9a4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-connection-render.c @@ -0,0 +1,299 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include + +void +_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_drawable_t drawable, + xcb_render_pictformat_t format, + uint32_t value_mask, + uint32_t *value_list) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_create_picture (connection->xcb_connection, picture, drawable, + format, value_mask, value_list); +} + +void +_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint32_t value_mask, + uint32_t *value_list) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_change_picture (connection->xcb_connection, picture, + value_mask, value_list); +} + +void +_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + int16_t clip_x_origin, + int16_t clip_y_origin, + uint32_t rectangles_len, + xcb_rectangle_t *rectangles) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_set_picture_clip_rectangles (connection->xcb_connection, picture, + clip_x_origin, clip_y_origin, + rectangles_len, rectangles); +} + +void +_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_free_picture (connection->xcb_connection, picture); + _cairo_xcb_connection_put_xid (connection, picture); +} + +void +_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t mask, + xcb_render_picture_t dst, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE); + xcb_render_composite (connection->xcb_connection, op, src, mask, dst, + src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); +} + +void +_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + int16_t src_x, + int16_t src_y, + uint32_t traps_len, + xcb_render_trapezoid_t *traps) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS); + xcb_render_trapezoids (connection->xcb_connection, op, src, dst, + mask_format, src_x, src_y, traps_len, traps); +} + +void +_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t id, + xcb_render_pictformat_t format) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_create_glyph_set (connection->xcb_connection, id, format); +} + +void +_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_free_glyph_set (connection->xcb_connection, glyphset); + _cairo_xcb_connection_put_xid (connection, glyphset); +} + +void +_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + uint32_t *glyphs_id, + xcb_render_glyphinfo_t *glyphs, + uint32_t data_len, + uint8_t *data) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_add_glyphs (connection->xcb_connection, glyphset, num_glyphs, + glyphs_id, glyphs, data_len, data); +} + +void +_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + xcb_render_glyph_t *glyphs) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_free_glyphs (connection->xcb_connection, glyphset, num_glyphs, glyphs); +} + +void +_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_composite_glyphs_8 (connection->xcb_connection, op, src, dst, mask_format, + glyphset, src_x, src_y, glyphcmds_len, glyphcmds); +} + +void +_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_composite_glyphs_16 (connection->xcb_connection, op, src, dst, mask_format, + glyphset, src_x, src_y, glyphcmds_len, glyphcmds); +} + +void +_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds) +{ + assert (connection->flags & CAIRO_XCB_HAS_RENDER); + xcb_render_composite_glyphs_32 (connection->xcb_connection, op, src, dst, mask_format, + glyphset, src_x, src_y, glyphcmds_len, glyphcmds); +} + +void +_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t dst, + xcb_render_color_t color, + uint32_t num_rects, + xcb_rectangle_t *rects) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES); + xcb_render_fill_rectangles (connection->xcb_connection, op, dst, color, + num_rects, rects); +} + +void +_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_transform_t *transform) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM); + xcb_render_set_picture_transform (connection->xcb_connection, picture, *transform); +} + +void +_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint16_t filter_len, + char *filter) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILTERS); + xcb_render_set_picture_filter (connection->xcb_connection, picture, + filter_len, filter, 0, NULL); +} + +void +_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_color_t color) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); + xcb_render_create_solid_fill (connection->xcb_connection, picture, color); +} + +void +_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t p1, + xcb_render_pointfix_t p2, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); + xcb_render_create_linear_gradient (connection->xcb_connection, picture, + p1, p2, num_stops, stops, colors); +} + +void +_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t inner, + xcb_render_pointfix_t outer, + xcb_render_fixed_t inner_radius, + xcb_render_fixed_t outer_radius, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); + xcb_render_create_radial_gradient (connection->xcb_connection, picture, + inner, outer, inner_radius, outer_radius, + num_stops, stops, colors); +} + +void +_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t center, + xcb_render_fixed_t angle, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors) +{ + assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS); + xcb_render_create_conical_gradient (connection->xcb_connection, picture, + center, angle, num_stops, stops, colors); +} diff --git a/gfx/cairo/cairo/src/cairo-xcb-connection-shm.c b/gfx/cairo/cairo/src/cairo-xcb-connection-shm.c new file mode 100644 index 000000000000..7720bbbd25b6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-connection-shm.c @@ -0,0 +1,115 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + +#include "cairo-xcb-private.h" + +#include +#include + +uint32_t +_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection, + uint32_t id, + cairo_bool_t readonly) +{ + uint32_t segment = _cairo_xcb_connection_get_xid (connection); + assert (connection->flags & CAIRO_XCB_HAS_SHM); + xcb_shm_attach (connection->xcb_connection, segment, id, readonly); + return segment; +} + +void +_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t total_width, + uint16_t total_height, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t shm, + uint32_t offset) +{ + assert (connection->flags & CAIRO_XCB_HAS_SHM); + xcb_shm_put_image (connection->xcb_connection, dst, gc, total_width, total_height, + src_x, src_y, width, height, dst_x, dst_y, depth, + XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shm, offset); +} + +cairo_status_t +_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint32_t shmseg, + uint32_t offset) +{ + xcb_shm_get_image_reply_t *reply; + + assert (connection->flags & CAIRO_XCB_HAS_SHM); + reply = xcb_shm_get_image_reply (connection->xcb_connection, + xcb_shm_get_image (connection->xcb_connection, + src, + src_x, src_y, + width, height, + (uint32_t) -1, + XCB_IMAGE_FORMAT_Z_PIXMAP, + shmseg, offset), + NULL); + free (reply); + + if (!reply) { + /* an error here should be impossible */ + return _cairo_error (CAIRO_STATUS_READ_ERROR); + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, + uint32_t segment) +{ + assert (connection->flags & CAIRO_XCB_HAS_SHM); + xcb_shm_detach (connection->xcb_connection, segment); + _cairo_xcb_connection_put_xid (connection, segment); +} + +#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xcb-connection.c b/gfx/cairo/cairo/src/cairo-xcb-connection.c new file mode 100644 index 000000000000..51f5ee323cd5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-connection.c @@ -0,0 +1,1006 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Authors: + * Chris Wilson + */ + + +#include "cairoint.h" + +#include "cairo-xcb-private.h" +#include "cairo-hash-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-inline.h" + +#include +#include +#include + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +#include +#include +#include +#endif + +typedef struct _cairo_xcb_xrender_format { + cairo_hash_entry_t key; + xcb_render_pictformat_t xrender_format; +} cairo_xcb_xrender_format_t; + +typedef struct _cairo_xcb_xid { + cairo_list_t link; + uint32_t xid; +} cairo_xcb_xid_t; + +#define XCB_RENDER_AT_LEAST(V, major, minor) \ + (((V)->major_version > major) || \ + (((V)->major_version == major) && ((V)->minor_version >= minor))) + +#define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) +#define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) +#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0) + +#define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1) + +#define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2) +#define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2) + +#define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) +#define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4) + +#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) +#define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) +#define XCB_RENDER_HAS_FILTER_GOOD(surface) FALSE +#define XCB_RENDER_HAS_FILTER_BEST(surface) FALSE +#define XCB_RENDER_HAS_SUBPIXEL_ORDER(surface) XCB_RENDER_AT_LEAST((surface), 0, 6) + +#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10) +#define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10) + +#define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11) + +static cairo_list_t connections; + +static cairo_status_t +_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection, + const xcb_render_query_pict_formats_reply_t *formats) +{ + xcb_render_pictscreen_iterator_t screens; + xcb_render_pictdepth_iterator_t depths; + xcb_render_pictvisual_iterator_t visuals; + + for (screens = xcb_render_query_pict_formats_screens_iterator (formats); + screens.rem; + xcb_render_pictscreen_next (&screens)) + { + for (depths = xcb_render_pictscreen_depths_iterator (screens.data); + depths.rem; + xcb_render_pictdepth_next (&depths)) + { + for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data); + visuals.rem; + xcb_render_pictvisual_next (&visuals)) + { + cairo_xcb_xrender_format_t *f; + cairo_status_t status; + + f = _cairo_malloc (sizeof (cairo_xcb_xrender_format_t)); + if (unlikely (f == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->key.hash = visuals.data->visual; + f->xrender_format = visuals.data->format; + status = _cairo_hash_table_insert (connection->visual_to_xrender_format, + &f->key); + if (unlikely (status)) + return status; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 +static xcb_format_t * +find_format_for_depth (const xcb_setup_t *setup, uint8_t depth) +{ + xcb_format_t *fmt = xcb_setup_pixmap_formats (setup); + xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup); + + for (; fmt != fmtend; ++fmt) + if (fmt->depth == depth) + return fmt; + + return 0; +} +#endif + +static cairo_status_t +_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection, + const xcb_render_query_pict_formats_reply_t *formats) +{ + xcb_render_pictforminfo_iterator_t i; + cairo_status_t status; + + for (i = xcb_render_query_pict_formats_formats_iterator (formats); + i.rem; + xcb_render_pictforminfo_next (&i)) + { + cairo_format_masks_t masks; + pixman_format_code_t pixman_format; + + if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT) + continue; + + masks.alpha_mask = + (unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift; + masks.red_mask = + (unsigned long) i.data->direct.red_mask << i.data->direct.red_shift; + masks.green_mask = + (unsigned long) i.data->direct.green_mask << i.data->direct.green_shift; + masks.blue_mask = + (unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift; + masks.bpp = i.data->depth; + + if (_pixman_format_from_masks (&masks, &pixman_format)) { + cairo_hash_entry_t key; + + key.hash = pixman_format; + if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) { + cairo_xcb_xrender_format_t *f; + + f = _cairo_malloc (sizeof (cairo_xcb_xrender_format_t)); + if (unlikely (f == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->key.hash = pixman_format; + f->xrender_format = i.data->id; + status = _cairo_hash_table_insert (connection->xrender_formats, + &f->key); + if (unlikely (status)) + return status; + +#if 0 + printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n", + i.data->id, + masks.alpha_mask, + masks.red_mask, + masks.green_mask, + masks.blue_mask, + masks.bpp, + pixman_format, + PIXMAN_FORMAT_DEPTH(pixman_format), + PIXMAN_FORMAT_BPP(pixman_format)); +#endif + } + } + } + + status = _cairo_xcb_connection_find_visual_formats (connection, formats); + if (unlikely (status)) + return status; + + connection->standard_formats[CAIRO_FORMAT_A1] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1); + + connection->standard_formats[CAIRO_FORMAT_A8] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8); + + connection->standard_formats[CAIRO_FORMAT_RGB24] = + _cairo_xcb_connection_get_xrender_format (connection, + PIXMAN_FORMAT (24, + PIXMAN_TYPE_ARGB, + 0, 8, 8, 8)); + if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) { + connection->standard_formats[CAIRO_FORMAT_RGB24] = + _cairo_xcb_connection_get_xrender_format (connection, + PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR, + 0, 8, 8, 8)); + } + + connection->standard_formats[CAIRO_FORMAT_ARGB32] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8); + if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) { + connection->standard_formats[CAIRO_FORMAT_ARGB32] = + _cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* + * We require support for depth 1, 8, 24 and 32 pixmaps + */ +#define DEPTH_MASK(d) (1 << ((d) - 1)) +#define REQUIRED_DEPTHS (DEPTH_MASK(1) | \ + DEPTH_MASK(8) | \ + DEPTH_MASK(24) | \ + DEPTH_MASK(32)) +static cairo_bool_t +pixmap_depths_usable (cairo_xcb_connection_t *connection, + uint32_t missing, + xcb_drawable_t root) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_void_cookie_t create_cookie[32]; + xcb_pixmap_t pixmap; + cairo_bool_t success = TRUE; + int depth, i, j; + + pixmap = _cairo_xcb_connection_get_xid (connection); + + for (depth = 1, i = 0; depth <= 32; depth++) { + if (missing & DEPTH_MASK(depth)) { + create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1); + xcb_free_pixmap (c, pixmap); + if (!create_cookie[i].sequence) { + success = FALSE; + break; + } + i++; + } + } + + for (j = 0; j < i; j++) { + xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]); + success &= create_error == NULL; + free (create_error); + } + + _cairo_xcb_connection_put_xid (connection, pixmap); + + return success; +} + +static cairo_bool_t +has_required_depths (cairo_xcb_connection_t *connection) +{ + xcb_screen_iterator_t screens; + + for (screens = xcb_setup_roots_iterator (connection->root); + screens.rem; + xcb_screen_next (&screens)) + { + xcb_depth_iterator_t depths; + uint32_t missing = REQUIRED_DEPTHS; + + for (depths = xcb_screen_allowed_depths_iterator (screens.data); + depths.rem; + xcb_depth_next (&depths)) + { + missing &= ~DEPTH_MASK (depths.data->depth); + } + if (missing == 0) + continue; + + /* + * Ok, this is ugly. It should be sufficient at this + * point to just return false, but Xinerama is broken at + * this point and only advertises depths which have an + * associated visual. Of course, the other depths still + * work, but the only way to find out is to try them. + */ + if (! pixmap_depths_usable (connection, missing, screens.data->root)) + return FALSE; + } + + return TRUE; +} + +static xcb_render_query_version_reply_t * +_render_restrict_env(xcb_render_query_version_reply_t *version) +{ + const char *env; + + if (version == NULL) + return NULL; + + env = getenv ("CAIRO_DEBUG"); + if (env != NULL) + env = strstr (env, "xrender-version="); + if (env != NULL) { + int max_render_major, max_render_minor; + + env += sizeof ("xrender-version=") - 1; + if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2) + max_render_major = max_render_minor = -1; + + if (max_render_major < 0 || max_render_minor < 0) { + free (version); + return NULL; + } + + if (max_render_major < (int) version->major_version || + (max_render_major == (int) version->major_version && + max_render_minor < (int) version->minor_version)) + { + version->major_version = max_render_major; + version->minor_version = max_render_minor; + } + } + + return version; +} + +static cairo_status_t +_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_render_query_version_cookie_t version_cookie; + xcb_render_query_pict_formats_cookie_t formats_cookie; + xcb_render_query_version_reply_t *version; + xcb_render_query_pict_formats_reply_t *formats; + cairo_status_t status; + cairo_bool_t present; + + version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION); + formats_cookie = xcb_render_query_pict_formats (c); + + present = has_required_depths (connection); + version = xcb_render_query_version_reply (c, version_cookie, 0); + formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0); + + version = _render_restrict_env (version); + + if (! present || version == NULL || formats == NULL) { + free (version); + free (formats); + return CAIRO_STATUS_SUCCESS; + } + + /* always true if the extension is present (i.e. >= 0.0) */ + connection->flags |= CAIRO_XCB_HAS_RENDER; + connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE; + connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS; + + if (XCB_RENDER_HAS_FILL_RECTANGLES (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES; + + if (XCB_RENDER_HAS_TRAPEZOIDS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS; + + if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM; + + if (XCB_RENDER_HAS_FILTERS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS; + + if (XCB_RENDER_HAS_FILTER_GOOD (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_FILTER_GOOD; + + if (XCB_RENDER_HAS_FILTER_BEST (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_FILTER_BEST; + + if (XCB_RENDER_HAS_PDF_OPERATORS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; + + if (XCB_RENDER_HAS_EXTENDED_REPEAT (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT; + + if (XCB_RENDER_HAS_GRADIENTS (version)) + connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS; + + if (XCB_RENDER_HAS_SUBPIXEL_ORDER (version)) { + uint32_t screen; + uint32_t *subpixel = xcb_render_query_pict_formats_subpixels(formats); + + /* The spec explicitly allows to have too few entries in the reply... */ + for (screen = 0; screen < formats->num_subpixel && screen < connection->root->roots_len; screen++) + connection->subpixel_orders[screen] = subpixel[screen]; + } + + free (version); + + status = _cairo_xcb_connection_parse_xrender_formats (connection, formats); + free (formats); + + return status; +} + +#if 0 +static void +_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_cairo_query_version_reply_t *version; + + version = xcb_cairo_query_version_reply (c, + xcb_cairo_query_version (c, 0, 0), + 0); + + free (version); +} +#endif + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +static cairo_bool_t +can_use_shm (cairo_xcb_connection_t *connection) +{ + cairo_bool_t success = TRUE; + xcb_connection_t *c = connection->xcb_connection; + xcb_void_cookie_t cookie[2]; + xcb_generic_error_t *error; + int shmid; + uint32_t shmseg; + void *ptr; + + shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); + if (shmid == -1) + return FALSE; + + ptr = shmat (shmid, NULL, 0); + if (ptr == (char *) -1) { + shmctl (shmid, IPC_RMID, NULL); + return FALSE; + } + + shmseg = _cairo_xcb_connection_get_xid (connection); + cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE); + cookie[1] = xcb_shm_detach_checked (c, shmseg); + _cairo_xcb_connection_put_xid (connection, shmseg); + + error = xcb_request_check (c, cookie[0]); + if (error != NULL) + success = FALSE; + + error = xcb_request_check (c, cookie[1]); + if (error != NULL) + success = FALSE; + + shmctl (shmid, IPC_RMID, NULL); + shmdt (ptr); + + return success; +} + +static void +_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection) +{ + xcb_connection_t *c = connection->xcb_connection; + xcb_shm_query_version_reply_t *version; + + version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0); + if (version == NULL) + return; + + free (version); + + if (can_use_shm (connection)) + connection->flags |= CAIRO_XCB_HAS_SHM; +} +#endif + +static cairo_status_t +_device_flush (void *device) +{ + cairo_xcb_connection_t *connection = device; + cairo_status_t status; + + status = cairo_device_acquire (&connection->device); + if (unlikely (status)) + return status; + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + _cairo_xcb_connection_shm_mem_pools_flush (connection); +#endif + + xcb_flush (connection->xcb_connection); + + cairo_device_release (&connection->device); + return CAIRO_STATUS_SUCCESS; +} + +static void +_pluck_xrender_format (void *entry, + void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + +static void +_device_finish (void *device) +{ + cairo_xcb_connection_t *connection = device; + cairo_bool_t was_cached = FALSE; + + if (! cairo_list_is_empty (&connection->link)) { + CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex); + cairo_list_del (&connection->link); + CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex); + was_cached = TRUE; + } + + while (! cairo_list_is_empty (&connection->fonts)) { + cairo_xcb_font_t *font; + + font = cairo_list_first_entry (&connection->fonts, + cairo_xcb_font_t, + link); + _cairo_xcb_font_close (font); + } + + while (! cairo_list_is_empty (&connection->screens)) { + cairo_xcb_screen_t *screen; + + screen = cairo_list_first_entry (&connection->screens, + cairo_xcb_screen_t, + link); + _cairo_xcb_screen_finish (screen); + } + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + /* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had + * a fallback image, we might have done a SHM PutImage. */ + _cairo_xcb_connection_shm_mem_pools_flush (connection); +#endif + + if (was_cached) + cairo_device_destroy (device); +} + +static void +_device_destroy (void *device) +{ + cairo_xcb_connection_t *connection = device; + + _cairo_hash_table_foreach (connection->xrender_formats, + _pluck_xrender_format, connection->xrender_formats); + _cairo_hash_table_destroy (connection->xrender_formats); + + _cairo_hash_table_foreach (connection->visual_to_xrender_format, + _pluck_xrender_format, + connection->visual_to_xrender_format); + _cairo_hash_table_destroy (connection->visual_to_xrender_format); + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + _cairo_xcb_connection_shm_mem_pools_fini (connection); +#endif + _cairo_freepool_fini (&connection->shm_info_freelist); + + _cairo_freepool_fini (&connection->xid_pool); + + CAIRO_MUTEX_FINI (connection->shm_mutex); + CAIRO_MUTEX_FINI (connection->screens_mutex); + + free (connection->subpixel_orders); + free (connection); +} + +static const cairo_device_backend_t _cairo_xcb_device_backend = { + CAIRO_DEVICE_TYPE_XCB, + + NULL, NULL, /* lock, unlock */ + + _device_flush, + _device_finish, + _device_destroy, +}; + +cairo_xcb_connection_t * +_cairo_xcb_connection_get (xcb_connection_t *xcb_connection) +{ + cairo_xcb_connection_t *connection; + const xcb_query_extension_reply_t *ext; + cairo_status_t status; + + CAIRO_MUTEX_INITIALIZE (); + + CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex); + if (connections.next == NULL) { + /* XXX _cairo_init () */ + cairo_list_init (&connections); + } + + cairo_list_foreach_entry (connection, + cairo_xcb_connection_t, + &connections, + link) + { + if (connection->xcb_connection == xcb_connection) { + /* Maintain MRU order. */ + if (connections.next != &connection->link) + cairo_list_move (&connection->link, &connections); + + goto unlock; + } + } + + connection = _cairo_malloc (sizeof (cairo_xcb_connection_t)); + if (unlikely (connection == NULL)) + goto unlock; + + _cairo_device_init (&connection->device, &_cairo_xcb_device_backend); + + connection->xcb_connection = xcb_connection; + + cairo_list_init (&connection->fonts); + cairo_list_init (&connection->screens); + cairo_list_init (&connection->link); + connection->xrender_formats = _cairo_hash_table_create (NULL); + if (connection->xrender_formats == NULL) { + CAIRO_MUTEX_FINI (connection->device.mutex); + free (connection); + connection = NULL; + goto unlock; + } + + connection->visual_to_xrender_format = _cairo_hash_table_create (NULL); + if (connection->visual_to_xrender_format == NULL) { + _cairo_hash_table_destroy (connection->xrender_formats); + CAIRO_MUTEX_FINI (connection->device.mutex); + free (connection); + connection = NULL; + goto unlock; + } + + cairo_list_init (&connection->free_xids); + _cairo_freepool_init (&connection->xid_pool, + sizeof (cairo_xcb_xid_t)); + + cairo_list_init (&connection->shm_pools); + cairo_list_init (&connection->shm_pending); + _cairo_freepool_init (&connection->shm_info_freelist, + sizeof (cairo_xcb_shm_info_t)); + + connection->maximum_request_length = + xcb_get_maximum_request_length (xcb_connection); + + CAIRO_MUTEX_INIT (connection->shm_mutex); + CAIRO_MUTEX_INIT (connection->screens_mutex); + + CAIRO_MUTEX_LOCK (connection->device.mutex); + + connection->flags = 0; + connection->force_precision = -1; + + xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id); + xcb_prefetch_extension_data (xcb_connection, &xcb_render_id); +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id); +#endif +#if 0 + xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id); +#endif + + xcb_prefetch_maximum_request_length (xcb_connection); + + connection->root = xcb_get_setup (xcb_connection); + connection->render = NULL; + connection->subpixel_orders = calloc (connection->root->roots_len, sizeof(*connection->subpixel_orders)); + if (unlikely (connection->subpixel_orders == NULL)) { + CAIRO_MUTEX_UNLOCK (connection->device.mutex); + _cairo_xcb_connection_destroy (connection); + connection = NULL; + goto unlock; + } + + ext = xcb_get_extension_data (xcb_connection, &xcb_render_id); + if (ext != NULL && ext->present) { + status = _cairo_xcb_connection_query_render (connection); + if (unlikely (status)) { + CAIRO_MUTEX_UNLOCK (connection->device.mutex); + _cairo_xcb_connection_destroy (connection); + connection = NULL; + goto unlock; + } + + connection->render = ext; + } + +#if 0 + ext = xcb_get_extension_data (connection, &xcb_cairo_id); + if (ext != NULL && ext->present) + _cairo_xcb_connection_query_cairo (connection); +#endif + + connection->shm = NULL; +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id); + if (ext != NULL && ext->present) { + _cairo_xcb_connection_query_shm (connection); + connection->shm = ext; + } +#endif + + connection->original_flags = connection->flags; + + CAIRO_MUTEX_UNLOCK (connection->device.mutex); + + cairo_list_add (&connection->link, &connections); +unlock: + CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex); + + return connection; +} + +xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format) +{ + cairo_hash_entry_t key; + cairo_xcb_xrender_format_t *format; + + key.hash = pixman_format; + format = _cairo_hash_table_lookup (connection->xrender_formats, &key); + return format ? format->xrender_format : XCB_NONE; +} + +xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection, + const xcb_visualid_t visual) +{ + cairo_hash_entry_t key; + cairo_xcb_xrender_format_t *format; + + key.hash = visual; + format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key); + return format ? format->xrender_format : XCB_NONE; +} + +void +_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection, + uint32_t xid) +{ + cairo_xcb_xid_t *cache; + + assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex)); + cache = _cairo_freepool_alloc (&connection->xid_pool); + if (likely (cache != NULL)) { + cache->xid = xid; + cairo_list_add (&cache->link, &connection->free_xids); + } +} + +uint32_t +_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection) +{ + uint32_t xid; + + assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex)); + if (! cairo_list_is_empty (&connection->free_xids)) { + cairo_xcb_xid_t *cache; + + cache = cairo_list_first_entry (&connection->free_xids, + cairo_xcb_xid_t, + link); + xid = cache->xid; + + cairo_list_del (&cache->link); + _cairo_freepool_free (&connection->xid_pool, cache); + } else { + xid = xcb_generate_id (connection->xcb_connection); + } + + return xid; +} + +/** + * cairo_xcb_device_get_connection: + * @device: a #cairo_device_t for the XCB backend + * + * Get the connection for the XCB device. + * + * Returns: the #xcb_connection_t for the connection + * + * Since: 1.12 + **/ +xcb_connection_t * +cairo_xcb_device_get_connection (cairo_device_t *device) +{ + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) + return NULL; + + return ((cairo_xcb_connection_t *)device)->xcb_connection; +} + +/* public (debug) interface */ + +/** + * cairo_xcb_device_debug_cap_xshm_version: + * @device: a #cairo_device_t for the XCB backend + * @major_version: major version to restrict to + * @minor_version: minor version to restrict to + * + * Restricts all future XCB surfaces for this devices to the specified version + * of the SHM extension. This function exists solely for debugging purpose. + * It let's you find out how cairo would behave with an older version of + * the SHM extension. + * + * Use the special values -1 and -1 for disabling the SHM extension. + * + * Since: 1.12 + **/ +void +cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, + int major_version, + int minor_version) +{ + cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return; + } + + /* First reset all the SHM flags to their original value. This works + * because we only ever clear bits after the connection was created. + */ + connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK); + + /* clear any flags that are inappropriate for the desired version */ + if (major_version < 0 && minor_version < 0) { + connection->flags &= ~(CAIRO_XCB_HAS_SHM); + } +} + +/** + * cairo_xcb_device_debug_cap_xrender_version: + * @device: a #cairo_device_t for the XCB backend + * @major_version: major version to restrict to + * @minor_version: minor version to restrict to + * + * Restricts all future XCB surfaces for this devices to the specified version + * of the RENDER extension. This function exists solely for debugging purpose. + * It let's you find out how cairo would behave with an older version of + * the RENDER extension. + * + * Use the special values -1 and -1 for disabling the RENDER extension. + * + * Since: 1.12 + **/ +void +cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, + int major_version, + int minor_version) +{ + cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return; + } + + /* First reset all the RENDER flags to their original value. This works + * because we only ever clear bits after the connection was created. + */ + connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK); + + /* clear any flags that are inappropriate for the desired version */ + if (major_version < 0 && minor_version < 0) { + connection->flags &= ~(CAIRO_XCB_HAS_RENDER | + CAIRO_XCB_RENDER_HAS_COMPOSITE | + CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | + CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES | + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM | + CAIRO_XCB_RENDER_HAS_FILTERS | + CAIRO_XCB_RENDER_HAS_FILTER_GOOD | + CAIRO_XCB_RENDER_HAS_FILTER_BEST | + CAIRO_XCB_RENDER_HAS_PDF_OPERATORS | + CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT | + CAIRO_XCB_RENDER_HAS_GRADIENTS); + } else { + xcb_render_query_version_reply_t version; + + version.major_version = major_version; + version.minor_version = minor_version; + + if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES; + + if (! XCB_RENDER_HAS_TRAPEZOIDS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS; + + if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM; + + if (! XCB_RENDER_HAS_FILTERS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS; + + if (! XCB_RENDER_HAS_PDF_OPERATORS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; + + if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT; + + if (! XCB_RENDER_HAS_GRADIENTS (&version)) + connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS; + } +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_device_debug_cap_xrender_version); +#endif + +/** + * cairo_xcb_device_debug_set_precision: + * @device: a #cairo_device_t for the XCB backend + * @precision: the precision to use + * + * Render supports two modes of precision when rendering trapezoids. Set + * the precision to the desired mode. + * + * Since: 1.12 + **/ +void +cairo_xcb_device_debug_set_precision (cairo_device_t *device, + int precision) +{ + if (device == NULL || device->status) + return; + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return; + } + + ((cairo_xcb_connection_t *) device)->force_precision = precision; +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_device_debug_set_precision); +#endif + +/** + * cairo_xcb_device_debug_get_precision: + * @device: a #cairo_device_t for the XCB backend + * + * Get the Xrender precision mode. + * + * Returns: the render precision mode + * + * Since: 1.12 + **/ +int +cairo_xcb_device_debug_get_precision (cairo_device_t *device) +{ + if (device == NULL || device->status) + return -1; + if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return -1; + } + + return ((cairo_xcb_connection_t *) device)->force_precision; +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_device_debug_get_precision); +#endif diff --git a/gfx/cairo/cairo/src/cairo-xcb-private.h b/gfx/cairo/cairo/src/cairo-xcb-private.h new file mode 100644 index 000000000000..f5d5a4c81c78 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-private.h @@ -0,0 +1,801 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * Copyright © 2009 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. + * + * Contributors(s): + * Chris Wilson + */ + +#ifndef CAIRO_XCB_PRIVATE_H +#define CAIRO_XCB_PRIVATE_H + +#include "cairoint.h" + +#include "cairo-xcb.h" + +#include "cairo-cache-private.h" +#include "cairo-compiler-private.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" +#include "cairo-mutex-private.h" +#include "cairo-pattern-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-spans-private.h" +#include "cairo-surface-private.h" + +#include +#include +#include +#include + +#define XLIB_COORD_MAX 32767 + +/* maximum number of cached GC's */ +#define GC_CACHE_SIZE 4 + +#define CAIRO_XCB_RENDER_AT_LEAST(major, minor) \ + ((XCB_RENDER_MAJOR_VERSION > major) || \ + ((XCB_RENDER_MAJOR_VERSION == major) && (XCB_RENDER_MINOR_VERSION >= minor))) + +typedef struct _cairo_xcb_connection cairo_xcb_connection_t; +typedef struct _cairo_xcb_font cairo_xcb_font_t; +typedef struct _cairo_xcb_screen cairo_xcb_screen_t; +typedef struct _cairo_xcb_surface cairo_xcb_surface_t; +typedef struct _cairo_xcb_picture cairo_xcb_picture_t; +typedef struct _cairo_xcb_shm_mem_pool cairo_xcb_shm_mem_pool_t; +typedef struct _cairo_xcb_shm_info cairo_xcb_shm_info_t; +typedef struct _cairo_xcb_resources cairo_xcb_resources_t; + +struct _cairo_xcb_shm_info { + cairo_xcb_connection_t *connection; + uint32_t shm; + uint32_t offset; + size_t size; + void *mem; + cairo_xcb_shm_mem_pool_t *pool; + xcb_get_input_focus_cookie_t sync; + cairo_list_t pending; +}; + +struct _cairo_xcb_surface { + cairo_surface_t base; + cairo_image_surface_t *fallback; + cairo_boxes_t fallback_damage; + + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + + xcb_drawable_t drawable; + cairo_bool_t owns_pixmap; + + cairo_bool_t deferred_clear; + cairo_color_t deferred_clear_color; + + int width; + int height; + int depth; + + xcb_render_picture_t picture; + xcb_render_pictformat_t xrender_format; + pixman_format_code_t pixman_format; + uint32_t precision; + + cairo_list_t link; +}; + +struct _cairo_xcb_picture { + cairo_surface_t base; + + cairo_xcb_screen_t *screen; + xcb_render_picture_t picture; + xcb_render_pictformat_t xrender_format; + pixman_format_code_t pixman_format; + + int width, height; + + cairo_extend_t extend; + cairo_filter_t filter; + cairo_bool_t has_component_alpha; + xcb_render_transform_t transform; + + int x0, y0; + int x, y; + + cairo_list_t link; +}; + +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +typedef struct _cairo_xlib_xcb_surface { + cairo_surface_t base; + + cairo_xcb_surface_t *xcb; + + /* original settings for query */ + void *display; + void *screen; + void *visual; + void *format; +} cairo_xlib_xcb_surface_t; +#endif + + +enum { + GLYPHSET_INDEX_ARGB32, + GLYPHSET_INDEX_A8, + GLYPHSET_INDEX_A1, + NUM_GLYPHSETS +}; + +typedef struct _cairo_xcb_font_glyphset_free_glyphs { + xcb_render_glyphset_t glyphset; + int glyph_count; + xcb_render_glyph_t glyph_indices[128]; +} cairo_xcb_font_glyphset_free_glyphs_t; + +typedef struct _cairo_xcb_font_glyphset_info { + xcb_render_glyphset_t glyphset; + cairo_format_t format; + xcb_render_pictformat_t xrender_format; + cairo_xcb_font_glyphset_free_glyphs_t *pending_free_glyphs; +} cairo_xcb_font_glyphset_info_t; + +struct _cairo_xcb_font { + cairo_scaled_font_private_t base; + cairo_scaled_font_t *scaled_font; + cairo_xcb_connection_t *connection; + cairo_xcb_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; + cairo_list_t link; +}; + +struct _cairo_xcb_screen { + cairo_xcb_connection_t *connection; + + xcb_screen_t *xcb_screen; + xcb_render_sub_pixel_t subpixel_order; + + xcb_gcontext_t gc[GC_CACHE_SIZE]; + uint8_t gc_depths[GC_CACHE_SIZE]; + + cairo_surface_t *stock_colors[CAIRO_STOCK_NUM_COLORS]; + struct { + cairo_surface_t *picture; + cairo_color_t color; + } solid_cache[16]; + int solid_cache_size; + + cairo_cache_t linear_pattern_cache; + cairo_cache_t radial_pattern_cache; + cairo_freelist_t pattern_cache_entry_freelist; + + cairo_list_t link; + cairo_list_t surfaces; + cairo_list_t pictures; + + cairo_bool_t has_font_options; + cairo_font_options_t font_options; +}; + +struct _cairo_xcb_connection { + cairo_device_t device; + + xcb_connection_t *xcb_connection; + + xcb_render_pictformat_t standard_formats[5]; + cairo_hash_table_t *xrender_formats; + cairo_hash_table_t *visual_to_xrender_format; + + unsigned int maximum_request_length; + unsigned int flags; + unsigned int original_flags; + + int force_precision; + + const xcb_setup_t *root; + const xcb_query_extension_reply_t *render; + const xcb_query_extension_reply_t *shm; + xcb_render_sub_pixel_t *subpixel_orders; + + cairo_list_t free_xids; + cairo_freepool_t xid_pool; + + cairo_mutex_t shm_mutex; + cairo_list_t shm_pools; + cairo_list_t shm_pending; + cairo_freepool_t shm_info_freelist; + + cairo_mutex_t screens_mutex; + cairo_list_t screens; + + cairo_list_t fonts; + + cairo_list_t link; +}; + +struct _cairo_xcb_resources { + cairo_bool_t xft_antialias; + int xft_lcdfilter; + cairo_bool_t xft_hinting; + int xft_hintstyle; + int xft_rgba; +}; + +enum { + CAIRO_XCB_HAS_RENDER = 0x0001, + CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES = 0x0002, + CAIRO_XCB_RENDER_HAS_COMPOSITE = 0x0004, + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS = 0x0008, + CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS = 0x0010, + CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM = 0x0020, + CAIRO_XCB_RENDER_HAS_FILTERS = 0x0040, + CAIRO_XCB_RENDER_HAS_PDF_OPERATORS = 0x0080, + CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT = 0x0100, + CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0200, + CAIRO_XCB_RENDER_HAS_FILTER_GOOD = 0x0400, + CAIRO_XCB_RENDER_HAS_FILTER_BEST = 0x0800, + + CAIRO_XCB_HAS_SHM = 0x80000000, + + CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER | + CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES | + CAIRO_XCB_RENDER_HAS_COMPOSITE | + CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | + CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM | + CAIRO_XCB_RENDER_HAS_FILTERS | + CAIRO_XCB_RENDER_HAS_PDF_OPERATORS | + CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT | + CAIRO_XCB_RENDER_HAS_GRADIENTS | + CAIRO_XCB_RENDER_HAS_FILTER_GOOD | + CAIRO_XCB_RENDER_HAS_FILTER_BEST, + CAIRO_XCB_SHM_MASK = CAIRO_XCB_HAS_SHM +}; + +#define CAIRO_XCB_SHM_SMALL_IMAGE 8192 + +cairo_private extern const cairo_surface_backend_t _cairo_xcb_surface_backend; + +/** + * _cairo_surface_is_xcb: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_xcb_surface_t + * + * Return value: %TRUE if the surface is an xcb surface + **/ +static inline cairo_bool_t +_cairo_surface_is_xcb (const cairo_surface_t *surface) +{ + /* _cairo_surface_nil sets a NULL backend so be safe */ + return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_XCB; +} + +cairo_private cairo_xcb_connection_t * +_cairo_xcb_connection_get (xcb_connection_t *connection); + +static inline cairo_xcb_connection_t * +_cairo_xcb_connection_reference (cairo_xcb_connection_t *connection) +{ + return (cairo_xcb_connection_t *) cairo_device_reference (&connection->device); +} + +cairo_private xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format); + +cairo_private xcb_render_pictformat_t +_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection, + const xcb_visualid_t visual); + +static inline cairo_status_t cairo_warn +_cairo_xcb_connection_acquire (cairo_xcb_connection_t *connection) +{ + return cairo_device_acquire (&connection->device); +} + +cairo_private uint32_t +_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection); + +cairo_private void +_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection, + uint32_t xid); + +static inline void +_cairo_xcb_connection_release (cairo_xcb_connection_t *connection) +{ + cairo_device_release (&connection->device); +} + +static inline void +_cairo_xcb_connection_destroy (cairo_xcb_connection_t *connection) +{ + cairo_device_destroy (&connection->device); +} + +cairo_private cairo_int_status_t +_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *display, + size_t size, + cairo_bool_t might_reuse, + cairo_xcb_shm_info_t **shm_info_out); + +cairo_private void +_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info); + +cairo_private void +_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection); + +cairo_private void +_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection); + +cairo_private void +_cairo_xcb_font_close (cairo_xcb_font_t *font); + +cairo_private cairo_xcb_screen_t * +_cairo_xcb_screen_get (xcb_connection_t *connection, + xcb_screen_t *screen); + +cairo_private void +_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen); + +cairo_private xcb_gcontext_t +_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + int depth); + +cairo_private void +_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc); + +cairo_private cairo_font_options_t * +_cairo_xcb_screen_get_font_options (cairo_xcb_screen_t *screen); + +cairo_private cairo_status_t +_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear, + cairo_surface_t *picture); + +cairo_private cairo_surface_t * +_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear); + +cairo_private cairo_status_t +_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial, + cairo_surface_t *picture); + +cairo_private cairo_surface_t * +_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial); + +cairo_private cairo_surface_t * +_cairo_xcb_surface_create_similar_image (void *abstrct_other, + cairo_format_t format, + int width, + int height); + +cairo_private cairo_surface_t * +_cairo_xcb_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height); + +cairo_private cairo_surface_t * +_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + cairo_bool_t owns_pixmap, + pixman_format_code_t pixman_format, + xcb_render_pictformat_t xrender_format, + int width, + int height); + +cairo_private_no_warn cairo_bool_t +_cairo_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents); + +cairo_private cairo_int_status_t +_cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents); + +cairo_private cairo_int_status_t +_cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents); + +cairo_private cairo_int_status_t +_cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + 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_private cairo_int_status_t +_cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias); + +cairo_private cairo_int_status_t +_cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap); +cairo_private void +_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst); + +cairo_private cairo_status_t +_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes); + +cairo_private cairo_status_t +_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes); + +cairo_private xcb_pixmap_t +_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection, + uint8_t depth, + xcb_drawable_t drawable, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection, + xcb_pixmap_t pixmap); + +cairo_private xcb_gcontext_t +_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection, + xcb_drawable_t drawable, + uint32_t value_mask, + uint32_t *values); + +cairo_private void +_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc); + +cairo_private void +_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection, + xcb_gcontext_t gc, + uint32_t value_mask, + uint32_t *values); + +cairo_private void +_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t length, + void *data); + +cairo_private void +_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint16_t cpp, + int stride, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + void *data); + +cairo_private xcb_get_image_reply_t * +_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint32_t num_rectangles, + xcb_rectangle_t *rectangles); + +cairo_private cairo_status_t +_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out); + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +cairo_private uint32_t +_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection, + uint32_t id, + cairo_bool_t readonly); + +cairo_private void +_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t total_width, + uint16_t total_height, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t shm, + uint32_t offset); + +cairo_private cairo_status_t +_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection, + xcb_drawable_t src, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + uint32_t shmseg, + uint32_t offset); + +cairo_private void +_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection, + uint32_t segment); +#else +static inline void +_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection, + xcb_drawable_t dst, + xcb_gcontext_t gc, + uint16_t total_width, + uint16_t total_height, + int16_t src_x, + int16_t src_y, + uint16_t width, + uint16_t height, + int16_t dst_x, + int16_t dst_y, + uint8_t depth, + uint32_t shm, + uint32_t offset) +{ + ASSERT_NOT_REACHED; +} +#endif + +cairo_private void +_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_drawable_t drawable, + xcb_render_pictformat_t format, + uint32_t value_mask, + uint32_t *value_list); + +cairo_private void +_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint32_t value_mask, + uint32_t *value_list); + +cairo_private void +_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + int16_t clip_x_origin, + int16_t clip_y_origin, + uint32_t rectangles_len, + xcb_rectangle_t *rectangles); + +cairo_private void +_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture); + +cairo_private void +_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t mask, + xcb_render_picture_t dst, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dst_x, + int16_t dst_y, + uint16_t width, + uint16_t height); + +cairo_private void +_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + int16_t src_x, + int16_t src_y, + uint32_t traps_len, + xcb_render_trapezoid_t *traps); + +cairo_private void +_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t id, + xcb_render_pictformat_t format); + +cairo_private void +_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset); + +cairo_private void +_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + uint32_t *glyphs_id, + xcb_render_glyphinfo_t *glyphs, + uint32_t data_len, + uint8_t *data); + +cairo_private void +_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection, + xcb_render_glyphset_t glyphset, + uint32_t num_glyphs, + xcb_render_glyph_t *glyphs); + +cairo_private void +_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds); + +cairo_private void +_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds); + +cairo_private void +_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t glyphcmds_len, + uint8_t *glyphcmds); + +cairo_private void +_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t dst, + xcb_render_color_t color, + uint32_t num_rects, + xcb_rectangle_t *rects); + +cairo_private void +_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_transform_t *transform); + +cairo_private void +_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + uint16_t filter_len, + char *filter); + +cairo_private void +_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_color_t color); + +cairo_private void +_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t p1, + xcb_render_pointfix_t p2, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors); + +cairo_private void +_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection, + xcb_render_picture_t picture, + xcb_render_pointfix_t inner, + xcb_render_pointfix_t outer, + xcb_render_fixed_t inner_radius, + xcb_render_fixed_t outer_radius, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors); + +cairo_private void +_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *c, + xcb_render_picture_t picture, + xcb_render_pointfix_t center, + xcb_render_fixed_t angle, + uint32_t num_stops, + xcb_render_fixed_t *stops, + xcb_render_color_t *colors); +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_proto (cairo_xcb_surface_create); +slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); +slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); +slim_hidden_proto (cairo_xcb_surface_set_size); +slim_hidden_proto (cairo_xcb_surface_set_drawable); +slim_hidden_proto (cairo_xcb_device_debug_get_precision); +slim_hidden_proto_no_warn (cairo_xcb_device_debug_set_precision); +slim_hidden_proto_no_warn (cairo_xcb_device_debug_cap_xrender_version); +#endif + +cairo_private void +_cairo_xcb_resources_get (cairo_xcb_screen_t *screen, + cairo_xcb_resources_t *resources); + +#endif /* CAIRO_XCB_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-xcb-resources.c b/gfx/cairo/cairo/src/cairo-xcb-resources.c new file mode 100644 index 000000000000..1877758c25a1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-resources.c @@ -0,0 +1,281 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2014 Lukas Lalinsky + * 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. + * + * Authors: + * Lukas Lalinsky + * + * Partially on code from xftdpy.c + * + * Copyright © 2000 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include "cairo-fontconfig-private.h" + +static void +parse_boolean (const char *v, cairo_bool_t *out) +{ + char c0, c1; + + c0 = *v; + if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1') + *out = TRUE; + if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0') + *out = FALSE; + if (c0 == 'o') { + c1 = v[1]; + if (c1 == 'n' || c1 == 'N') + *out = TRUE; + if (c1 == 'f' || c1 == 'F') + *out = FALSE; + } +} + +static void +parse_integer (const char *v, int *out) +{ + char *e; + int value; + +#if CAIRO_HAS_FC_FONT + if (FcNameConstant ((FcChar8 *) v, out)) + return; +#endif + + value = strtol (v, &e, 0); + if (e != v) + *out = value; +} + +static char * +skip_spaces(char *str) +{ + while (*str == ' ' || *str == '\t' || *str == '\n') + str++; + return str; +} + +struct resource_parser { + int buffer_size; + int bytes_in_buffer; + char* buffer; + cairo_xcb_resources_t *resources; +}; + +static cairo_bool_t +resource_parse_line (char *name, cairo_xcb_resources_t *resources) +{ + char *value; + + value = strchr (name, ':'); + if (value == NULL) + return FALSE; + + *value++ = 0; + + name = skip_spaces (name); + value = skip_spaces (value); + + if (strcmp (name, "Xft.antialias") == 0) + parse_boolean (value, &(resources->xft_antialias)); + else if (strcmp (name, "Xft.lcdfilter") == 0) + parse_integer (value, &(resources->xft_lcdfilter)); + else if (strcmp (name, "Xft.rgba") == 0) + parse_integer (value, &(resources->xft_rgba)); + else if (strcmp (name, "Xft.hinting") == 0) + parse_boolean (value, &(resources->xft_hinting)); + else if (strcmp (name, "Xft.hintstyle") == 0) + parse_integer (value, &(resources->xft_hintstyle)); + + return TRUE; +} + +static int +resource_parse_lines (struct resource_parser *parser) +{ + char *line, *newline; + + line = parser->buffer; + while (1) { + newline = strchr (line, '\n'); + if (newline == NULL) + break; + + *newline++ = 0; + + if (! resource_parse_line (line, parser->resources)) + break; + + line = newline; + } + + return line - parser->buffer; +} + +static void +resource_parser_init (struct resource_parser *parser, cairo_xcb_resources_t *resources) +{ + parser->buffer_size = 0; + parser->bytes_in_buffer = 0; + parser->buffer = NULL; + parser->resources = resources; +} + +static cairo_bool_t +resource_parser_update (struct resource_parser *parser, const char *data, int length) +{ + int bytes_parsed; + + if (parser->bytes_in_buffer + length + 1 > parser->buffer_size) { + parser->buffer_size = parser->bytes_in_buffer + length + 1; + parser->buffer = realloc(parser->buffer, parser->buffer_size); + if (! parser->buffer) { + parser->buffer_size = 0; + parser->bytes_in_buffer = 0; + return FALSE; + } + } + + memmove (parser->buffer + parser->bytes_in_buffer, data, length); + parser->bytes_in_buffer += length; + parser->buffer[parser->bytes_in_buffer] = 0; + + bytes_parsed = resource_parse_lines (parser); + + if (parser->bytes_in_buffer > bytes_parsed) { + memmove (parser->buffer, parser->buffer + bytes_parsed, parser->bytes_in_buffer - bytes_parsed); + parser->bytes_in_buffer -= bytes_parsed; + } else { + parser->bytes_in_buffer = 0; + } + + return TRUE; +} + +static void +resource_parser_done (struct resource_parser *parser) +{ + if (parser->bytes_in_buffer > 0) { + parser->buffer[parser->bytes_in_buffer] = 0; + resource_parse_line (parser->buffer, parser->resources); + } + + free (parser->buffer); +} + +static void +get_resources(xcb_connection_t *connection, xcb_screen_t *screen, cairo_xcb_resources_t *resources) +{ + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *reply; + struct resource_parser parser; + int offset; + cairo_bool_t has_more_data; + + resources->xft_antialias = TRUE; + resources->xft_lcdfilter = -1; + resources->xft_hinting = TRUE; + resources->xft_hintstyle = FC_HINT_FULL; + resources->xft_rgba = FC_RGBA_UNKNOWN; + + resource_parser_init (&parser, resources); + + offset = 0; + has_more_data = FALSE; + do { + cookie = xcb_get_property (connection, 0, screen->root, XCB_ATOM_RESOURCE_MANAGER, XCB_ATOM_STRING, offset, 1024); + reply = xcb_get_property_reply (connection, cookie, NULL); + + if (reply) { + if (reply->format == 8 && reply->type == XCB_ATOM_STRING) { + char *value = (char *) xcb_get_property_value (reply); + int length = xcb_get_property_value_length (reply); + + offset += length / 4; /* X needs the offset in 'long' units */ + has_more_data = reply->bytes_after > 0; + + if (! resource_parser_update (&parser, value, length)) + has_more_data = FALSE; /* early exit on error */ + } + + free (reply); + } + } while (has_more_data); + + resource_parser_done (&parser); +} + +void +_cairo_xcb_resources_get (cairo_xcb_screen_t *screen, cairo_xcb_resources_t *resources) +{ + get_resources (screen->connection->xcb_connection, screen->xcb_screen, resources); + + if (resources->xft_rgba == FC_RGBA_UNKNOWN) { + switch (screen->subpixel_order) { + case XCB_RENDER_SUB_PIXEL_UNKNOWN: + resources->xft_rgba = FC_RGBA_UNKNOWN; + break; + case XCB_RENDER_SUB_PIXEL_HORIZONTAL_RGB: + resources->xft_rgba = FC_RGBA_RGB; + break; + case XCB_RENDER_SUB_PIXEL_HORIZONTAL_BGR: + resources->xft_rgba = FC_RGBA_BGR; + break; + case XCB_RENDER_SUB_PIXEL_VERTICAL_RGB: + resources->xft_rgba = FC_RGBA_VRGB; + break; + case XCB_RENDER_SUB_PIXEL_VERTICAL_BGR: + resources->xft_rgba = FC_RGBA_VBGR; + break; + case XCB_RENDER_SUB_PIXEL_NONE: + resources->xft_rgba = FC_RGBA_NONE; + break; + } + } +} diff --git a/gfx/cairo/cairo/src/cairo-xcb-screen.c b/gfx/cairo/cairo/src/cairo-xcb-screen.c new file mode 100644 index 000000000000..0d23ad3c3d9e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-screen.c @@ -0,0 +1,494 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * Copyright © 2009 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. + * + * Authors: + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" +#include "cairo-list-inline.h" + +#include "cairo-fontconfig-private.h" + +static void +_cairo_xcb_init_screen_font_options (cairo_xcb_screen_t *screen) +{ + cairo_xcb_resources_t res; + cairo_antialias_t antialias; + cairo_subpixel_order_t subpixel_order; + cairo_lcd_filter_t lcd_filter; + cairo_hint_style_t hint_style; + + _cairo_xcb_resources_get (screen, &res); + + /* the rest of the code in this function is copied from + _cairo_xlib_init_screen_font_options in cairo-xlib-screen.c */ + + if (res.xft_hinting) { + switch (res.xft_hintstyle) { + case FC_HINT_NONE: + hint_style = CAIRO_HINT_STYLE_NONE; + break; + case FC_HINT_SLIGHT: + hint_style = CAIRO_HINT_STYLE_SLIGHT; + break; + case FC_HINT_MEDIUM: + hint_style = CAIRO_HINT_STYLE_MEDIUM; + break; + case FC_HINT_FULL: + hint_style = CAIRO_HINT_STYLE_FULL; + break; + default: + hint_style = CAIRO_HINT_STYLE_DEFAULT; + } + } else { + hint_style = CAIRO_HINT_STYLE_NONE; + } + + switch (res.xft_rgba) { + case FC_RGBA_RGB: + subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; + break; + case FC_RGBA_BGR: + subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; + break; + case FC_RGBA_VRGB: + subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; + break; + case FC_RGBA_VBGR: + subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; + break; + case FC_RGBA_UNKNOWN: + case FC_RGBA_NONE: + default: + subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + } + + switch (res.xft_lcdfilter) { + case FC_LCD_NONE: + lcd_filter = CAIRO_LCD_FILTER_NONE; + break; + case FC_LCD_DEFAULT: + lcd_filter = CAIRO_LCD_FILTER_FIR5; + break; + case FC_LCD_LIGHT: + lcd_filter = CAIRO_LCD_FILTER_FIR3; + break; + case FC_LCD_LEGACY: + lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; + break; + default: + lcd_filter = CAIRO_LCD_FILTER_DEFAULT; + break; + } + + if (res.xft_antialias) { + if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT) + antialias = CAIRO_ANTIALIAS_GRAY; + else + antialias = CAIRO_ANTIALIAS_SUBPIXEL; + } else { + antialias = CAIRO_ANTIALIAS_NONE; + } + + cairo_font_options_set_hint_style (&screen->font_options, hint_style); + cairo_font_options_set_antialias (&screen->font_options, antialias); + cairo_font_options_set_subpixel_order (&screen->font_options, subpixel_order); + _cairo_font_options_set_lcd_filter (&screen->font_options, lcd_filter); + cairo_font_options_set_hint_metrics (&screen->font_options, CAIRO_HINT_METRICS_ON); +} + +struct pattern_cache_entry { + cairo_cache_entry_t key; + cairo_xcb_screen_t *screen; + cairo_pattern_union_t pattern; + cairo_surface_t *picture; +}; + +void +_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen) +{ + int i; + + CAIRO_MUTEX_LOCK (screen->connection->screens_mutex); + cairo_list_del (&screen->link); + CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex); + + while (! cairo_list_is_empty (&screen->surfaces)) { + cairo_surface_t *surface; + + surface = &cairo_list_first_entry (&screen->surfaces, + cairo_xcb_surface_t, + link)->base; + + cairo_surface_finish (surface); + } + + while (! cairo_list_is_empty (&screen->pictures)) { + cairo_surface_t *surface; + + surface = &cairo_list_first_entry (&screen->pictures, + cairo_xcb_picture_t, + link)->base; + + cairo_surface_finish (surface); + } + + for (i = 0; i < screen->solid_cache_size; i++) + cairo_surface_destroy (screen->solid_cache[i].picture); + + for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++) + cairo_surface_destroy (screen->stock_colors[i]); + + for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { + if (screen->gc_depths[i] != 0) + _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]); + } + + _cairo_cache_fini (&screen->linear_pattern_cache); + _cairo_cache_fini (&screen->radial_pattern_cache); + _cairo_freelist_fini (&screen->pattern_cache_entry_freelist); + + free (screen); +} + +static cairo_bool_t +_linear_pattern_cache_entry_equal (const void *A, const void *B) +{ + const struct pattern_cache_entry *a = A, *b = B; + + return _cairo_linear_pattern_equal (&a->pattern.gradient.linear, + &b->pattern.gradient.linear); +} + +static cairo_bool_t +_radial_pattern_cache_entry_equal (const void *A, const void *B) +{ + const struct pattern_cache_entry *a = A, *b = B; + + return _cairo_radial_pattern_equal (&a->pattern.gradient.radial, + &b->pattern.gradient.radial); +} + +static void +_pattern_cache_entry_destroy (void *closure) +{ + struct pattern_cache_entry *entry = closure; + + _cairo_pattern_fini (&entry->pattern.base); + cairo_surface_destroy (entry->picture); + _cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry); +} + +static int _get_screen_index(cairo_xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen) +{ + int idx = 0; + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_connection->root); + for (; iter.rem; xcb_screen_next(&iter), idx++) + if (iter.data->root == xcb_screen->root) + return idx; + + ASSERT_NOT_REACHED; +} + +cairo_xcb_screen_t * +_cairo_xcb_screen_get (xcb_connection_t *xcb_connection, + xcb_screen_t *xcb_screen) +{ + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + cairo_status_t status; + int screen_idx; + int i; + + connection = _cairo_xcb_connection_get (xcb_connection); + if (unlikely (connection == NULL)) + return NULL; + + CAIRO_MUTEX_LOCK (connection->screens_mutex); + + cairo_list_foreach_entry (screen, + cairo_xcb_screen_t, + &connection->screens, + link) + { + if (screen->xcb_screen == xcb_screen) { + /* Maintain list in MRU order */ + if (&screen->link != connection->screens.next) + cairo_list_move (&screen->link, &connection->screens); + + goto unlock; + } + } + + screen = _cairo_malloc (sizeof (cairo_xcb_screen_t)); + if (unlikely (screen == NULL)) + goto unlock; + + screen_idx = _get_screen_index(connection, xcb_screen); + + screen->connection = connection; + screen->xcb_screen = xcb_screen; + screen->has_font_options = FALSE; + screen->subpixel_order = connection->subpixel_orders[screen_idx]; + + _cairo_freelist_init (&screen->pattern_cache_entry_freelist, + sizeof (struct pattern_cache_entry)); + cairo_list_init (&screen->link); + cairo_list_init (&screen->surfaces); + cairo_list_init (&screen->pictures); + + memset (screen->gc_depths, 0, sizeof (screen->gc_depths)); + memset (screen->gc, 0, sizeof (screen->gc)); + + screen->solid_cache_size = 0; + for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++) + screen->stock_colors[i] = NULL; + + status = _cairo_cache_init (&screen->linear_pattern_cache, + _linear_pattern_cache_entry_equal, + NULL, + _pattern_cache_entry_destroy, + 16); + if (unlikely (status)) + goto error_screen; + + status = _cairo_cache_init (&screen->radial_pattern_cache, + _radial_pattern_cache_entry_equal, + NULL, + _pattern_cache_entry_destroy, + 4); + if (unlikely (status)) + goto error_linear; + + cairo_list_add (&screen->link, &connection->screens); + +unlock: + CAIRO_MUTEX_UNLOCK (connection->screens_mutex); + + return screen; + +error_linear: + _cairo_cache_fini (&screen->linear_pattern_cache); +error_screen: + CAIRO_MUTEX_UNLOCK (connection->screens_mutex); + free (screen); + + return NULL; +} + +static xcb_gcontext_t +_create_gc (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable) +{ + uint32_t values[] = { 0 }; + + return _cairo_xcb_connection_create_gc (screen->connection, drawable, + XCB_GC_GRAPHICS_EXPOSURES, + values); +} + +xcb_gcontext_t +_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen, + xcb_drawable_t drawable, + int depth) +{ + int i; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); + + for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { + if (screen->gc_depths[i] == depth) { + screen->gc_depths[i] = 0; + return screen->gc[i]; + } + } + + return _create_gc (screen, drawable); +} + +void +_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc) +{ + int i; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); + + for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) { + if (screen->gc_depths[i] == 0) + break; + } + + if (i == ARRAY_LENGTH (screen->gc)) { + /* perform random substitution to ensure fair caching over depths */ + i = rand () % ARRAY_LENGTH (screen->gc); + _cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]); + } + + screen->gc[i] = gc; + screen->gc_depths[i] = depth; +} + +cairo_status_t +_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear, + cairo_surface_t *picture) +{ + struct pattern_cache_entry *entry; + cairo_status_t status; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); + + entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); + if (unlikely (entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear); + entry->key.size = 1; + + status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base); + if (unlikely (status)) { + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + entry->picture = cairo_surface_reference (picture); + entry->screen = screen; + + status = _cairo_cache_insert (&screen->linear_pattern_cache, + &entry->key); + if (unlikely (status)) { + cairo_surface_destroy (picture); + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen, + const cairo_linear_pattern_t *linear) +{ + cairo_surface_t *picture = NULL; + struct pattern_cache_entry tmpl; + struct pattern_cache_entry *entry; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); + + tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear); + _cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base); + + entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key); + if (entry != NULL) + picture = cairo_surface_reference (entry->picture); + + return picture; +} + +cairo_status_t +_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial, + cairo_surface_t *picture) +{ + struct pattern_cache_entry *entry; + cairo_status_t status; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); + + entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist); + if (unlikely (entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial); + entry->key.size = 1; + + status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base); + if (unlikely (status)) { + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + entry->picture = cairo_surface_reference (picture); + entry->screen = screen; + + status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key); + if (unlikely (status)) { + cairo_surface_destroy (picture); + _cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen, + const cairo_radial_pattern_t *radial) +{ + cairo_surface_t *picture = NULL; + struct pattern_cache_entry tmpl; + struct pattern_cache_entry *entry; + + assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex)); + + tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial); + _cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base); + + entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key); + if (entry != NULL) + picture = cairo_surface_reference (entry->picture); + + return picture; +} + +cairo_font_options_t * +_cairo_xcb_screen_get_font_options (cairo_xcb_screen_t *screen) +{ + if (! screen->has_font_options) { + _cairo_font_options_init_default (&screen->font_options); + _cairo_font_options_set_round_glyph_positions (&screen->font_options, CAIRO_ROUND_GLYPH_POS_ON); + + /* XXX: This is disabled because something seems to be merging + font options incorrectly for xcb. This effectively reverts + the changes brought in git e691d242, and restores ~150 tests + to resume passing. See mailing list archives for Sep 17, + 2014 for more discussion. */ + if (0 && ! _cairo_xcb_connection_acquire (screen->connection)) { + _cairo_xcb_init_screen_font_options (screen); + _cairo_xcb_connection_release (screen->connection); + } + + screen->has_font_options = TRUE; + } + + return &screen->font_options; +} diff --git a/gfx/cairo/cairo/src/cairo-xcb-shm.c b/gfx/cairo/cairo/src/cairo-xcb-shm.c new file mode 100644 index 000000000000..763778ab2c45 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-shm.c @@ -0,0 +1,337 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Chris Wilson + * Copyright © 2009 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. + * + * Contributors(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + +#include "cairo-xcb-private.h" +#include "cairo-list-inline.h" +#include "cairo-mempool-private.h" + +#include +#include +#include +#include + +#define CAIRO_MAX_SHM_MEMORY (16*1024*1024) + +/* a simple buddy allocator for memory pools + * XXX fragmentation? use Doug Lea's malloc? + */ + +typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t; + +typedef enum { + PENDING_WAIT, + PENDING_POLL +} shm_wait_type_t; + +struct _cairo_xcb_shm_mem_pool { + int shmid; + uint32_t shmseg; + void *shm; + + cairo_mempool_t mem; + + cairo_list_t link; +}; + +static void +_cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool) +{ + cairo_list_del (&pool->link); + + shmdt (pool->shm); + _cairo_mempool_fini (&pool->mem); + + free (pool); +} + +static void +_cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info) +{ + cairo_xcb_connection_t *connection = shm_info->connection; + + assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex)); + + _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem); + _cairo_freepool_free (&connection->shm_info_freelist, shm_info); + + /* scan for old, unused pools - hold at least one in reserve */ + if (! cairo_list_is_singular (&connection->shm_pools)) + { + cairo_xcb_shm_mem_pool_t *pool, *next; + cairo_list_t head; + + cairo_list_init (&head); + cairo_list_move (connection->shm_pools.next, &head); + + cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, + &connection->shm_pools, link) + { + if (pool->mem.free_bytes == pool->mem.max_bytes) { + _cairo_xcb_connection_shm_detach (connection, pool->shmseg); + _cairo_xcb_shm_mem_pool_destroy (pool); + } + } + + cairo_list_move (head.next, &connection->shm_pools); + } +} + +static void +_cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait) +{ + cairo_xcb_shm_info_t *info, *next; + xcb_get_input_focus_reply_t *reply; + + assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex)); + cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t, + &connection->shm_pending, pending) + { + switch (wait) { + case PENDING_WAIT: + reply = xcb_wait_for_reply (connection->xcb_connection, + info->sync.sequence, NULL); + break; + case PENDING_POLL: + if (! xcb_poll_for_reply (connection->xcb_connection, + info->sync.sequence, + (void **) &reply, NULL)) + /* We cannot be sure the server finished with this image yet, so + * try again later. All other shm info are guaranteed to have a + * larger sequence number and thus don't have to be checked. */ + return; + break; + default: + /* silence Clang static analyzer warning */ + ASSERT_NOT_REACHED; + reply = NULL; + } + + free (reply); + cairo_list_del (&info->pending); + _cairo_xcb_shm_info_finalize (info); + } +} + +cairo_int_status_t +_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection, + size_t size, + cairo_bool_t might_reuse, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_xcb_shm_info_t *shm_info; + cairo_xcb_shm_mem_pool_t *pool, *next; + size_t bytes, maxbits = 16, minbits = 8; + size_t shm_allocated = 0; + void *mem = NULL; + cairo_status_t status; + + assert (connection->flags & CAIRO_XCB_HAS_SHM); + + CAIRO_MUTEX_LOCK (connection->shm_mutex); + _cairo_xcb_shm_process_pending (connection, PENDING_POLL); + + if (might_reuse) { + cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t, + &connection->shm_pending, pending) { + if (shm_info->size >= size) { + cairo_list_del (&shm_info->pending); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + + xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence); + shm_info->sync.sequence = XCB_NONE; + + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; + } + } + } + + cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, + &connection->shm_pools, link) + { + if (pool->mem.free_bytes > size) { + mem = _cairo_mempool_alloc (&pool->mem, size); + if (mem != NULL) { + /* keep the active pools towards the front */ + cairo_list_move (&pool->link, &connection->shm_pools); + goto allocate_shm_info; + } + } + /* scan for old, unused pools */ + if (pool->mem.free_bytes == pool->mem.max_bytes) { + _cairo_xcb_connection_shm_detach (connection, + pool->shmseg); + _cairo_xcb_shm_mem_pool_destroy (pool); + } else { + shm_allocated += pool->mem.max_bytes; + } + } + + if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) { + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pool = _cairo_malloc (sizeof (cairo_xcb_shm_mem_pool_t)); + if (unlikely (pool == NULL)) { + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + bytes = 1 << maxbits; + while (bytes <= size) + bytes <<= 1, maxbits++; + bytes <<= 3; + + do { + pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); + if (pool->shmid != -1) + break; + + /* If the allocation failed because we asked for too much memory, we try + * again with a smaller request, as long as our allocation still fits. */ + bytes >>= 1; + if (errno != EINVAL || bytes < size) + break; + } while (TRUE); + if (pool->shmid == -1) { + int err = errno; + if (! (err == EINVAL || err == ENOMEM)) + connection->flags &= ~CAIRO_XCB_HAS_SHM; + free (pool); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + pool->shm = shmat (pool->shmid, NULL, 0); + if (unlikely (pool->shm == (char *) -1)) { + shmctl (pool->shmid, IPC_RMID, NULL); + free (pool); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_mempool_init (&pool->mem, pool->shm, bytes, + minbits, maxbits - minbits + 1); + if (unlikely (status)) { + shmdt (pool->shm); + free (pool); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return status; + } + + pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE); + shmctl (pool->shmid, IPC_RMID, NULL); + + cairo_list_add (&pool->link, &connection->shm_pools); + mem = _cairo_mempool_alloc (&pool->mem, size); + + allocate_shm_info: + shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist); + if (unlikely (shm_info == NULL)) { + _cairo_mempool_free (&pool->mem, mem); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + shm_info->connection = connection; + shm_info->pool = pool; + shm_info->shm = pool->shmseg; + shm_info->size = size; + shm_info->offset = (char *) mem - (char *) pool->shm; + shm_info->mem = mem; + shm_info->sync.sequence = XCB_NONE; + + /* scan for old, unused pools */ + cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t, + &connection->shm_pools, link) + { + if (pool->mem.free_bytes == pool->mem.max_bytes) { + _cairo_xcb_connection_shm_detach (connection, + pool->shmseg); + _cairo_xcb_shm_mem_pool_destroy (pool); + } + } + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); + + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info) +{ + cairo_xcb_connection_t *connection = shm_info->connection; + + /* We can only return shm_info->mem to the allocator when we can be sure + * that the X server no longer reads from it. Since the X server processes + * requests in order, we send a GetInputFocus here. + * _cairo_xcb_shm_process_pending () will later check if the reply for that + * request was received and then actually mark this memory area as free. */ + + CAIRO_MUTEX_LOCK (connection->shm_mutex); + assert (shm_info->sync.sequence == XCB_NONE); + shm_info->sync = xcb_get_input_focus (connection->xcb_connection); + + cairo_list_init (&shm_info->pending); + cairo_list_add_tail (&shm_info->pending, &connection->shm_pending); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); +} + +void +_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection) +{ + CAIRO_MUTEX_LOCK (connection->shm_mutex); + _cairo_xcb_shm_process_pending (connection, PENDING_WAIT); + CAIRO_MUTEX_UNLOCK (connection->shm_mutex); +} + +void +_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection) +{ + assert (cairo_list_is_empty (&connection->shm_pending)); + while (! cairo_list_is_empty (&connection->shm_pools)) { + _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools, + cairo_xcb_shm_mem_pool_t, + link)); + } +} + +#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface-core.c b/gfx/cairo/cairo/src/cairo-xcb-surface-core.c new file mode 100644 index 000000000000..91c0ff995e4e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-surface-core.c @@ -0,0 +1,641 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-boxes-private.h" +#include "cairo-xcb-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" + +/* XXX dithering */ + +typedef struct _cairo_xcb_pixmap { + cairo_surface_t base; + + cairo_xcb_connection_t *connection; + cairo_xcb_screen_t *screen; + + cairo_surface_t *owner; + xcb_pixmap_t pixmap; + int width; + int height; + int depth; + int x0, y0; + cairo_bool_t repeat; +} cairo_xcb_pixmap_t; + +static cairo_status_t +_cairo_xcb_pixmap_finish (void *abstract_surface) +{ + cairo_xcb_pixmap_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->owner != NULL) { + cairo_surface_destroy (surface->owner); + } else { + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + _cairo_xcb_connection_free_pixmap (surface->connection, + surface->pixmap); + _cairo_xcb_connection_release (surface->connection); + } + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = { + CAIRO_SURFACE_TYPE_XCB, + _cairo_xcb_pixmap_finish, +}; + +static cairo_xcb_pixmap_t * +_cairo_xcb_pixmap_create (cairo_xcb_surface_t *target, + int width, int height) +{ + cairo_xcb_pixmap_t *surface; + + surface = _cairo_malloc (sizeof (cairo_xcb_pixmap_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_pixmap_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_pixmap_backend, + NULL, + target->base.content, + FALSE); /* is_vector */ + + surface->connection = target->connection; + surface->screen = target->screen; + surface->owner = NULL; + surface->width = width; + surface->height = height; + surface->depth = target->depth; + surface->x0 = surface->y0 = 0; + surface->repeat = FALSE; + + surface->pixmap = + _cairo_xcb_connection_create_pixmap (surface->connection, + surface->depth, + target->drawable, + width, height); + + return surface; +} + +static cairo_xcb_pixmap_t * +_cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target) +{ + cairo_xcb_pixmap_t *surface; + + surface = _cairo_malloc (sizeof (cairo_xcb_pixmap_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_pixmap_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_pixmap_backend, + NULL, + target->base.content, + FALSE); /* is_vector */ + + surface->connection = target->connection; + surface->screen = target->screen; + surface->pixmap = target->drawable; + surface->owner = cairo_surface_reference (&target->base); + surface->width = target->width; + surface->height = target->height; + surface->depth = target->depth; + surface->x0 = surface->y0 = 0; + surface->repeat = FALSE; + + return surface; +} + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +static cairo_status_t +_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_surface_t *image = NULL; + cairo_xcb_shm_info_t *shm_info = NULL; + cairo_status_t status; + size_t size, stride; + + if (! (connection->flags & CAIRO_XCB_HAS_SHM)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); + size = stride * height; + if (size <= CAIRO_XCB_SHM_SMALL_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_xcb_connection_allocate_shm_info (connection, size, + FALSE, &shm_info); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, + pixman_format, + width, height, + stride); + status = image->status; + if (unlikely (status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + status = _cairo_user_data_array_set_data (&image->user_data, + (const cairo_user_data_key_t *) connection, + shm_info, + (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); + + if (unlikely (status)) { + cairo_surface_destroy (image); + _cairo_xcb_shm_info_destroy (shm_info); + return status; + } + + *image_out = (cairo_image_surface_t *) image; + *shm_info_out = shm_info; + return CAIRO_STATUS_SUCCESS; +} +#else +static cairo_status_t +_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} +#endif + +cairo_status_t +_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_image_surface_t **image_out, + cairo_xcb_shm_info_t **shm_info_out) +{ + cairo_surface_t *image = NULL; + cairo_xcb_shm_info_t *shm_info = NULL; + cairo_status_t status; + + status = _cairo_xcb_shm_image_create_shm (connection, + pixman_format, + width, + height, + image_out, + shm_info_out); + + if (status != CAIRO_STATUS_SUCCESS) { + image = _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + width, height, + 0); + status = image->status; + if (unlikely (status)) + return status; + + *image_out = (cairo_image_surface_t *) image; + *shm_info_out = shm_info; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_xcb_pixmap_t * +_pixmap_from_image (cairo_xcb_surface_t *target, + xcb_render_pictformat_t format, + cairo_image_surface_t *image, + cairo_xcb_shm_info_t *shm_info) +{ + xcb_gcontext_t gc; + cairo_xcb_pixmap_t *pixmap; + + pixmap = _cairo_xcb_pixmap_create (target, + image->width, + image->height); + if (unlikely (pixmap->base.status)) + return pixmap; + + gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth); + + if (shm_info != NULL) { + _cairo_xcb_connection_shm_put_image (target->connection, + pixmap->pixmap, gc, + image->width, image->height, + 0, 0, + image->width, image->height, + 0, 0, + image->depth, + shm_info->shm, + shm_info->offset); + } else { + int len; + + /* Do we need to trim the image? */ + len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, + PIXMAN_FORMAT_BPP (image->pixman_format)); + if (len == image->stride) { + _cairo_xcb_connection_put_image (target->connection, + pixmap->pixmap, gc, + image->width, image->height, + 0, 0, + image->depth, + image->stride, + image->data); + } else { + _cairo_xcb_connection_put_subimage (target->connection, + pixmap->pixmap, gc, + 0, 0, + image->width, image->height, + PIXMAN_FORMAT_BPP (image->pixman_format) / 8, + image->stride, + 0, 0, + image->depth, + image->data); + + } + } + + _cairo_xcb_screen_put_gc (target->screen, image->depth, gc); + + return pixmap; +} + +static cairo_xcb_pixmap_t * +_render_to_pixmap (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_pattern_union_t copy; + cairo_status_t status; + cairo_xcb_pixmap_t *pixmap; + + status = _cairo_xcb_shm_image_create (target->screen->connection, + target->pixman_format, + extents->width, extents->height, + &image, &shm_info); + if (unlikely (status)) + return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); + + _cairo_pattern_init_static_copy (©.base, pattern); + cairo_matrix_translate (©.base.matrix, -extents->x, -extents->y); + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + ©.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status); + } + + pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info); + cairo_surface_destroy (&image->base); + + if (unlikely (pixmap->base.status)) + return pixmap; + + pixmap->x0 = -extents->x; + pixmap->y0 = -extents->y; + return pixmap; +} + +static cairo_xcb_pixmap_t * +_copy_to_pixmap (cairo_xcb_surface_t *source) +{ + cairo_xcb_pixmap_t *pixmap; + + /* If the source may be a window, we need to copy it and its children + * via a temporary pixmap so that we can IncludeInferiors on the source + * and use ClipByChildren on the destination. + */ + if (source->owns_pixmap) { + pixmap = _cairo_xcb_pixmap_copy (source); + if (unlikely (pixmap->base.status)) + return pixmap; + } else { + uint32_t values[1]; + xcb_gcontext_t gc; + + pixmap = _cairo_xcb_pixmap_create (source, + source->width, + source->height); + if (unlikely (pixmap->base.status)) + return pixmap; + + gc = _cairo_xcb_screen_get_gc (source->screen, + pixmap->pixmap, + pixmap->depth); + + values[0] = TRUE; + _cairo_xcb_connection_change_gc (pixmap->connection, gc, + XCB_GC_SUBWINDOW_MODE, values); + + _cairo_xcb_connection_copy_area (pixmap->connection, + source->drawable, + pixmap->pixmap, gc, + 0, 0, + 0, 0, + source->width, + source->height); + + values[0] = FALSE; + _cairo_xcb_connection_change_gc (pixmap->connection, gc, + XCB_GC_SUBWINDOW_MODE, values); + + _cairo_xcb_screen_put_gc (source->screen, + pixmap->depth, + gc); + } + + return pixmap; +} +static cairo_xcb_pixmap_t * +_cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int tx, int ty) +{ + cairo_surface_t *source; + cairo_xcb_pixmap_t *pixmap; + + source = pattern->surface; + pixmap = (cairo_xcb_pixmap_t *) + _cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend); + if (pixmap != NULL && pixmap->screen == target->screen) + return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base); + + if (_cairo_surface_is_xcb(source) && + ((cairo_xcb_surface_t *) source)->screen == target->screen) + { + cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source; + + if (xcb_source->depth == target->depth) + pixmap = _copy_to_pixmap (xcb_source); + } +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS + else if (source->type == CAIRO_SURFACE_TYPE_XLIB && + ((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen) + { + cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb; + + if (xcb_source->depth == target->depth) + pixmap = _copy_to_pixmap (xcb_source); + } +#endif + + if (pixmap == NULL) { + cairo_rectangle_int_t rect; + + if (! _cairo_surface_get_extents (source, &rect)) { + rect.x = rect.y = 0; + rect.width = target->width; + rect.height = target->height; + } + + pixmap = _render_to_pixmap (target, &pattern->base, &rect); + } + + if (unlikely (pixmap->base.status)) + return pixmap; + + _cairo_surface_attach_snapshot (source, &pixmap->base, NULL); + + if (pattern->base.extend != CAIRO_EXTEND_NONE) { + if (extents->x < 0 || extents->y < 0 || + extents->x + extents->width > pixmap->width || + extents->y + extents->height > pixmap->height) + { + pixmap->repeat = TRUE; + } + } + + pixmap->x0 += tx; + pixmap->y0 += ty; + + return pixmap; +} + +static cairo_xcb_pixmap_t * +_cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + int tx, ty; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: + /* Core can only perform a native, unscaled blit, but can handle tiles */ + if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) { + switch (pattern->extend) { + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + return _cairo_xcb_surface_pixmap (target, + (cairo_surface_pattern_t *) pattern, + extents, tx, ty); + + default: + case CAIRO_EXTEND_PAD: + case CAIRO_EXTEND_REFLECT: + break; + } + } + /* fallthrough */ + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _render_to_pixmap (target, pattern, extents); + + default: + case CAIRO_PATTERN_TYPE_SOLID: + ASSERT_NOT_REACHED; + return NULL; + } +} + +cairo_status_t +_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst, + const cairo_pattern_t *src_pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes) +{ + cairo_xcb_pixmap_t *src; + const struct _cairo_boxes_chunk *chunk; + xcb_gcontext_t gc; + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents); + status = src->base.status; + if (unlikely (status)) + goto CLEANUP_CONNECTION; + + assert (src->depth == dst->depth); + + gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth); + + if (src->repeat) { + uint32_t mask = + XCB_GC_FILL_STYLE | + XCB_GC_TILE | + XCB_GC_TILE_STIPPLE_ORIGIN_X | + XCB_GC_TILE_STIPPLE_ORIGIN_Y; + uint32_t values[] = { + XCB_FILL_STYLE_TILED, + src->pixmap, + - src->x0, - src->y0, + }; + xcb_rectangle_t *xcb_rects; + + _cairo_xcb_connection_change_gc (dst->connection, gc, mask, values); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + int i; + + xcb_rects = (xcb_rectangle_t *) chunk->base; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + xcb_rects[i].x = x1; + xcb_rects[i].y = y1; + xcb_rects[i].width = x2 - x1; + xcb_rects[i].height = y2 - y1; + } + _cairo_xcb_connection_poly_fill_rectangle (dst->connection, + dst->drawable, + gc, chunk->count, xcb_rects); + } + + values[0] = 0; + _cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values); + } else { + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + int i; + + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + _cairo_xcb_connection_copy_area (dst->connection, + src->pixmap, + dst->drawable, gc, + src->x0 + x1, + src->y0 + y1, + x1, y1, + x2 - x1, y2 - y1); + } + } + } + + _cairo_xcb_screen_put_gc (dst->screen, src->depth, gc); + cairo_surface_destroy (&src->base); + + CLEANUP_CONNECTION: + _cairo_xcb_connection_release (dst->connection); + + return status; +} + +cairo_status_t +_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + struct _cairo_boxes_chunk *chunk; + xcb_gcontext_t gc; + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth); + +#if 0 + xcb_pixmap_t source; + + source = _dither_source (dst, color); + XSetTSOrigin (surface->dpy, gc, 0, 0); + XSetTile (surface->dpy, gc, source); +#endif + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + xcb_rectangle_t *xcb_rects; + int i; + + xcb_rects = (xcb_rectangle_t *) chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x); + int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x); + int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y); + int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y); + + xcb_rects[i].x = x1; + xcb_rects[i].y = y1; + xcb_rects[i].width = x2 - x1; + xcb_rects[i].height = y2 - y1; + } + + _cairo_xcb_connection_poly_fill_rectangle (dst->connection, + dst->drawable, gc, + chunk->count, xcb_rects); + } + + _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc); + _cairo_xcb_connection_release (dst->connection); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface-render.c b/gfx/cairo/cairo/src/cairo-xcb-surface-render.c new file mode 100644 index 000000000000..6cb56cb7819b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-surface-render.c @@ -0,0 +1,4879 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-xcb-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-inline.h" +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-region-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-traps-private.h" +#include "cairo-recording-surface-inline.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-inline.h" + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +static cairo_status_t +_clip_and_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_composite_rectangles_t *extents); + +static inline cairo_xcb_connection_t * +_picture_to_connection (cairo_xcb_picture_t *picture) +{ + return (cairo_xcb_connection_t *) picture->base.device; +} + +static void +_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface); + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static cairo_status_t +_cairo_xcb_picture_finish (void *abstract_surface) +{ + cairo_xcb_picture_t *surface = abstract_surface; + cairo_xcb_connection_t *connection = _picture_to_connection (surface); + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (connection); + cairo_list_del (&surface->link); + if (unlikely (status)) + return status; + + _cairo_xcb_connection_render_free_picture (connection, surface->picture); + + _cairo_xcb_connection_release (connection); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t _cairo_xcb_picture_backend = { + CAIRO_SURFACE_TYPE_XCB, + _cairo_xcb_picture_finish, +}; + +static const struct xcb_render_transform_t identity_transform = { + 1 << 16, 0, 0, + 0, 1 << 16, 0, + 0, 0, 1 << 16, +}; + +static cairo_xcb_picture_t * +_cairo_xcb_picture_create (cairo_xcb_screen_t *screen, + pixman_format_code_t pixman_format, + xcb_render_pictformat_t xrender_format, + int width, int height) +{ + cairo_xcb_picture_t *surface; + + surface = _cairo_malloc (sizeof (cairo_xcb_picture_t)); + if (unlikely (surface == NULL)) + return (cairo_xcb_picture_t *) + _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xcb_picture_backend, + &screen->connection->device, + _cairo_content_from_pixman_format (pixman_format), + FALSE); /* is_vector */ + + cairo_list_add (&surface->link, &screen->pictures); + + surface->screen = screen; + surface->picture = _cairo_xcb_connection_get_xid (screen->connection); + surface->pixman_format = pixman_format; + surface->xrender_format = xrender_format; + + surface->x0 = surface->y0 = 0; + surface->x = surface->y = 0; + surface->width = width; + surface->height = height; + + surface->transform = identity_transform; + surface->extend = CAIRO_EXTEND_NONE; + surface->filter = CAIRO_FILTER_NEAREST; + surface->has_component_alpha = FALSE; + + return surface; +} + +static inline cairo_bool_t +_operator_is_supported (uint32_t flags, cairo_operator_t op) +{ + if (op <= CAIRO_OPERATOR_SATURATE) + return TRUE; + + /* Can we use PDF operators? */ +#if CAIRO_XCB_RENDER_AT_LEAST(0, 11) + if (op <= CAIRO_OPERATOR_HSL_LUMINOSITY) + return flags & CAIRO_XCB_RENDER_HAS_PDF_OPERATORS; +#endif + + return FALSE; +} + +static int +_render_operator (cairo_operator_t op) +{ +#define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y + switch (op) { + C(CLEAR, CLEAR); + C(SOURCE, SRC); + + C(OVER, OVER); + C(IN, IN); + C(OUT, OUT); + C(ATOP, ATOP); + + C(DEST, DST); + C(DEST_OVER, OVER_REVERSE); + C(DEST_IN, IN_REVERSE); + C(DEST_OUT, OUT_REVERSE); + C(DEST_ATOP, ATOP_REVERSE); + + C(XOR, XOR); + C(ADD, ADD); + C(SATURATE, SATURATE); + + /* PDF operators were added in RENDER 0.11, check if the xcb headers have + * the defines, else fall through to the default case. */ +#if CAIRO_XCB_RENDER_AT_LEAST(0, 11) +#define BLEND(x,y) C(x,y) +#else +#define BLEND(x,y) case CAIRO_OPERATOR_##x: +#endif + BLEND(MULTIPLY, MULTIPLY); + BLEND(SCREEN, SCREEN); + BLEND(OVERLAY, OVERLAY); + BLEND(DARKEN, DARKEN); + BLEND(LIGHTEN, LIGHTEN); + BLEND(COLOR_DODGE, COLOR_DODGE); + BLEND(COLOR_BURN, COLOR_BURN); + BLEND(HARD_LIGHT, HARD_LIGHT); + BLEND(SOFT_LIGHT, SOFT_LIGHT); + BLEND(DIFFERENCE, DIFFERENCE); + BLEND(EXCLUSION, EXCLUSION); + BLEND(HSL_HUE, HSL_HUE); + BLEND(HSL_SATURATION, HSL_SATURATION); + BLEND(HSL_COLOR, HSL_COLOR); + BLEND(HSL_LUMINOSITY, HSL_LUMINOSITY); + + default: + ASSERT_NOT_REACHED; + return XCB_RENDER_PICT_OP_OVER; + } +} + +static cairo_status_t +_cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface, + cairo_region_t *region) +{ + xcb_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; + xcb_rectangle_t *rects = stack_rects; + int i, num_rects; + + num_rects = cairo_region_num_rectangles (region); + + if (num_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (num_rects, sizeof (xcb_rectangle_t)); + if (unlikely (rects == NULL)) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + + _cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection, + surface->picture, + 0, 0, + num_rects, rects); + + if (rects != stack_rects) + free (rects); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface) +{ + uint32_t values[] = { XCB_NONE }; + _cairo_xcb_connection_render_change_picture (surface->connection, + surface->picture, + XCB_RENDER_CP_CLIP_MASK, + values); +} + +static void +_cairo_xcb_surface_set_precision (cairo_xcb_surface_t *surface, + cairo_antialias_t antialias) +{ + cairo_xcb_connection_t *connection = surface->connection; + uint32_t precision; + + if (connection->force_precision != -1) + precision = connection->force_precision; + else switch (antialias) { + default: + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_NONE: + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GOOD: + precision = XCB_RENDER_POLY_MODE_IMPRECISE; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + precision = XCB_RENDER_POLY_MODE_PRECISE; + break; + } + + if (surface->precision != precision) { + _cairo_xcb_connection_render_change_picture (connection, + surface->picture, + XCB_RENDER_CP_POLY_MODE, + &precision); + surface->precision = precision; + } +} + + +static void +_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface) +{ + assert (surface->fallback == NULL); + if (surface->picture == XCB_NONE) { + uint32_t values[1]; + uint32_t flags = 0; + + if (surface->precision != XCB_RENDER_POLY_MODE_PRECISE) { + flags |= XCB_RENDER_CP_POLY_MODE; + values[0] = surface->precision; + } + + surface->picture = _cairo_xcb_connection_get_xid (surface->connection); + _cairo_xcb_connection_render_create_picture (surface->connection, + surface->picture, + surface->drawable, + surface->xrender_format, + flags, values); + } +} + +static cairo_xcb_picture_t * +_picture_from_image (cairo_xcb_surface_t *target, + xcb_render_pictformat_t format, + cairo_image_surface_t *image, + cairo_xcb_shm_info_t *shm_info) +{ + xcb_pixmap_t pixmap; + xcb_gcontext_t gc; + cairo_xcb_picture_t *picture; + + pixmap = _cairo_xcb_connection_create_pixmap (target->connection, + image->depth, + target->drawable, + image->width, image->height); + + gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth); + + if (shm_info != NULL) { + _cairo_xcb_connection_shm_put_image (target->connection, + pixmap, gc, + image->width, image->height, + 0, 0, + image->width, image->height, + 0, 0, + image->depth, + shm_info->shm, + shm_info->offset); + } else { + int len; + + /* Do we need to trim the image? */ + len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)); + if (len == image->stride) { + _cairo_xcb_connection_put_image (target->connection, + pixmap, gc, + image->width, image->height, + 0, 0, + image->depth, + image->stride, + image->data); + } else { + _cairo_xcb_connection_put_subimage (target->connection, + pixmap, gc, + 0, 0, + image->width, image->height, + PIXMAN_FORMAT_BPP (image->pixman_format) / 8, + image->stride, + 0, 0, + image->depth, + image->data); + + } + } + + _cairo_xcb_screen_put_gc (target->screen, image->depth, gc); + + picture = _cairo_xcb_picture_create (target->screen, + image->pixman_format, format, + image->width, image->height); + if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) { + _cairo_xcb_connection_render_create_picture (target->connection, + picture->picture, pixmap, format, + 0, 0); + } + + _cairo_xcb_connection_free_pixmap (target->connection, pixmap); + + return picture; +} + +static cairo_bool_t +_pattern_is_supported (uint32_t flags, + const cairo_pattern_t *pattern) + +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + switch (pattern->extend) { + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + break; + case CAIRO_EXTEND_PAD: + case CAIRO_EXTEND_REFLECT: + if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0) + return FALSE; + } + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return (flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) || + _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL); + case CAIRO_FILTER_GOOD: + return flags & CAIRO_XCB_RENDER_HAS_FILTER_GOOD; + case CAIRO_FILTER_BEST: + return flags & CAIRO_XCB_RENDER_HAS_FILTER_BEST; + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + default: + return flags & CAIRO_XCB_RENDER_HAS_FILTERS; + } + } else if (pattern->type == CAIRO_PATTERN_TYPE_MESH) { + return FALSE; + } else { /* gradient */ + if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0) + return FALSE; + + /* The RENDER specification says that the inner circle has to be + * completely contained inside the outer one. */ + if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL && + ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern)) + { + return FALSE; + } + return TRUE; + } +} + +static void +_cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture, + const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, double yc) +{ + xcb_render_transform_t transform; + pixman_transform_t *pixman_transform; + cairo_int_status_t ignored; + + /* Casting between pixman_transform_t and xcb_render_transform_t is safe + * because they happen to be the exact same type. + */ + pixman_transform = (pixman_transform_t *) &transform; + + picture->x = picture->x0; + picture->y = picture->y0; + ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, + pixman_transform, + &picture->x, &picture->y); + (void) ignored; + + if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) { + _cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture), + picture->picture, + &transform); + + picture->transform = transform; + } +} + +static void +_cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture, + cairo_filter_t filter) +{ + const char *render_filter; + int len; + + if (picture->filter == filter) + return; + + switch (filter) { + case CAIRO_FILTER_FAST: + render_filter = "fast"; + len = strlen ("fast"); + break; + + case CAIRO_FILTER_GOOD: + render_filter = "good"; + len = strlen ("good"); + break; + + case CAIRO_FILTER_BEST: + render_filter = "best"; + len = strlen ("best"); + break; + + case CAIRO_FILTER_NEAREST: + render_filter = "nearest"; + len = strlen ("nearest"); + break; + + case CAIRO_FILTER_BILINEAR: + render_filter = "bilinear"; + len = strlen ("bilinear"); + break; + + default: + ASSERT_NOT_REACHED; + case CAIRO_FILTER_GAUSSIAN: + render_filter = "best"; + len = strlen ("best"); + break; + } + + _cairo_xcb_connection_render_set_picture_filter (_picture_to_connection (picture), + picture->picture, + len, (char *) render_filter); + picture->filter = filter; +} + +static void +_cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture, + cairo_extend_t extend) +{ + uint32_t pa[1]; + + if (picture->extend == extend) + return; + + switch (extend) { + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_NONE: + pa[0] = XCB_RENDER_REPEAT_NONE; + break; + + case CAIRO_EXTEND_REPEAT: + pa[0] = XCB_RENDER_REPEAT_NORMAL; + break; + + case CAIRO_EXTEND_REFLECT: + pa[0] = XCB_RENDER_REPEAT_REFLECT; + break; + + case CAIRO_EXTEND_PAD: + pa[0] = XCB_RENDER_REPEAT_PAD; + break; + } + + _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture), + picture->picture, + XCB_RENDER_CP_REPEAT, pa); + picture->extend = extend; +} + +static void +_cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture, + cairo_bool_t ca) +{ + uint32_t pa[1]; + + if (picture->has_component_alpha == ca) + return; + + pa[0] = ca; + + _cairo_xcb_connection_render_change_picture (_picture_to_connection (picture), + picture->picture, + XCB_RENDER_CP_COMPONENT_ALPHA, + pa); + picture->has_component_alpha = ca; +} + +static cairo_xcb_picture_t * +_solid_picture (cairo_xcb_surface_t *target, + const cairo_color_t *color) +{ + xcb_render_color_t xcb_color; + xcb_render_pictformat_t xrender_format; + cairo_xcb_picture_t *picture; + + xcb_color.red = color->red_short; + xcb_color.green = color->green_short; + xcb_color.blue = color->blue_short; + xcb_color.alpha = color->alpha_short; + + xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32]; + picture = _cairo_xcb_picture_create (target->screen, + PIXMAN_a8r8g8b8, + xrender_format, + -1, -1); + if (unlikely (picture->base.status)) + return picture; + + if (target->connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) { + _cairo_xcb_connection_render_create_solid_fill (target->connection, + picture->picture, + xcb_color); + } else { + xcb_pixmap_t pixmap; + uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL }; + + pixmap = _cairo_xcb_connection_create_pixmap (target->connection, + 32, target->drawable, 1, 1); + _cairo_xcb_connection_render_create_picture (target->connection, + picture->picture, + pixmap, + xrender_format, + XCB_RENDER_CP_REPEAT, + values); + if (target->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + xcb_rectangle_t rect; + + rect.x = rect.y = 0; + rect.width = rect.height = 1; + + _cairo_xcb_connection_render_fill_rectangles (_picture_to_connection (picture), + XCB_RENDER_PICT_OP_SRC, + picture->picture, + xcb_color, 1, &rect); + } else { + xcb_gcontext_t gc; + uint32_t pixel; + + gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32); + + /* XXX byte ordering? */ + pixel = (((uint32_t)color->alpha_short >> 8) << 24) | + ((color->red_short >> 8) << 16) | + ((color->green_short >> 8) << 8) | + ((color->blue_short >> 8) << 0); + + _cairo_xcb_connection_put_image (target->connection, + pixmap, gc, + 1, 1, 0, 0, + 32, 4, &pixel); + + _cairo_xcb_screen_put_gc (target->screen, 32, gc); + } + + _cairo_xcb_connection_free_pixmap (target->connection, pixmap); + } + + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_transparent_picture (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT]; + if (picture == NULL) { + picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT); + target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base; + } + + return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); +} + +static cairo_xcb_picture_t * +_cairo_xcb_black_picture (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK]; + if (picture == NULL) { + picture = _solid_picture (target, CAIRO_COLOR_BLACK); + target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base; + } + + return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); +} + +static cairo_xcb_picture_t * +_cairo_xcb_white_picture (cairo_xcb_surface_t *target) +{ + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE]; + if (picture == NULL) { + picture = _solid_picture (target, CAIRO_COLOR_WHITE); + target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base; + } + + return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); +} + +static cairo_xcb_picture_t * +_cairo_xcb_solid_picture (cairo_xcb_surface_t *target, + const cairo_solid_pattern_t *pattern) +{ + cairo_xcb_picture_t *picture; + cairo_xcb_screen_t *screen; + int i, n_cached; + + if (pattern->color.alpha_short <= 0x00ff) + return _cairo_xcb_transparent_picture (target); + + if (pattern->color.alpha_short >= 0xff00) { + if (pattern->color.red_short <= 0x00ff && + pattern->color.green_short <= 0x00ff && + pattern->color.blue_short <= 0x00ff) + { + return _cairo_xcb_black_picture (target); + } + + if (pattern->color.red_short >= 0xff00 && + pattern->color.green_short >= 0xff00 && + pattern->color.blue_short >= 0xff00) + { + return _cairo_xcb_white_picture (target); + } + } + + screen = target->screen; + n_cached = screen->solid_cache_size; + for (i = 0; i < n_cached; i++) { + if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) { + return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture); + } + } + + picture = _solid_picture (target, &pattern->color); + if (unlikely (picture->base.status)) + return picture; + + if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) { + i = screen->solid_cache_size++; + } else { + i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache); + cairo_surface_destroy (screen->solid_cache[i].picture); + } + screen->solid_cache[i].picture = cairo_surface_reference (&picture->base); + screen->solid_cache[i].color = pattern->color; + + return picture; +} + +static cairo_xcb_picture_t * +_render_to_picture (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_pattern_union_t copy; + cairo_status_t status; + cairo_xcb_picture_t *picture; + pixman_format_code_t pixman_format; + xcb_render_pictformat_t xrender_format; + + /* XXX handle extend modes via tiling? */ + /* XXX alpha-only masks? */ + + pixman_format = PIXMAN_a8r8g8b8; + xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32]; + + status = _cairo_xcb_shm_image_create (target->screen->connection, + pixman_format, + extents->width, extents->height, + &image, &shm_info); + if (unlikely (status)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + + _cairo_pattern_init_static_copy (©.base, pattern); + cairo_matrix_translate (©.base.matrix, extents->x, extents->y); + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + ©.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + + picture = _picture_from_image (target, xrender_format, image, shm_info); + cairo_surface_destroy (&image->base); + + if (unlikely (picture->base.status)) + return picture; + + _cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha); + picture->x = -extents->x; + picture->y = -extents->y; + + return picture; +} + +static xcb_render_fixed_t * +_gradient_to_xcb (const cairo_gradient_pattern_t *gradient, + unsigned int *n_stops, + char *buf, unsigned int buflen) +{ + xcb_render_fixed_t *stops; + xcb_render_color_t *colors; + unsigned int i; + + assert (gradient->n_stops > 0); + *n_stops = MAX (gradient->n_stops, 2); + + if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen) + { + stops = (xcb_render_fixed_t *) buf; + } + else + { + stops = + _cairo_malloc_ab (*n_stops, + sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)); + if (unlikely (stops == NULL)) + return NULL; + } + + colors = (xcb_render_color_t *) (stops + *n_stops); + for (i = 0; i < gradient->n_stops; i++) { + stops[i] = + _cairo_fixed_16_16_from_double (gradient->stops[i].offset); + + colors[i].red = gradient->stops[i].color.red_short; + colors[i].green = gradient->stops[i].color.green_short; + colors[i].blue = gradient->stops[i].color.blue_short; + colors[i].alpha = gradient->stops[i].color.alpha_short; + } + + /* RENDER does not support gradients with less than 2 stops. If a + * gradient has only a single stop, duplicate it to make RENDER + * happy. */ + if (gradient->n_stops == 1) { + stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset); + + colors[1].red = gradient->stops[0].color.red_short; + colors[1].green = gradient->stops[0].color.green_short; + colors[1].blue = gradient->stops[0].color.blue_short; + colors[1].alpha = gradient->stops[0].color.alpha_short; + } + + return stops; +} + +static cairo_xcb_picture_t * +_cairo_xcb_linear_picture (cairo_xcb_surface_t *target, + const cairo_linear_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + char buf[CAIRO_STACK_BUFFER_SIZE]; + xcb_render_fixed_t *stops; + xcb_render_color_t *colors; + xcb_render_pointfix_t p1, p2; + cairo_matrix_t matrix; + cairo_circle_double_t extremes[2]; + cairo_xcb_picture_t *picture; + cairo_status_t status; + unsigned int n_stops; + + _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes); + + picture = (cairo_xcb_picture_t *) + _cairo_xcb_screen_lookup_linear_picture (target->screen, pattern); + if (picture != NULL) + goto setup_picture; + + stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf)); + if (unlikely (stops == NULL)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + picture = _cairo_xcb_picture_create (target->screen, + target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32], + PIXMAN_a8r8g8b8, + -1, -1); + if (unlikely (picture->base.status)) { + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + return picture; + } + picture->filter = CAIRO_FILTER_DEFAULT; + + colors = (xcb_render_color_t *) (stops + n_stops); + + p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); + p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); + p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); + p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); + + _cairo_xcb_connection_render_create_linear_gradient (target->connection, + picture->picture, + p1, p2, + n_stops, + stops, colors); + + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + + status = _cairo_xcb_screen_store_linear_picture (target->screen, + pattern, + &picture->base); + if (unlikely (status)) { + cairo_surface_destroy (&picture->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + +setup_picture: + _cairo_xcb_picture_set_matrix (picture, &matrix, + pattern->base.base.filter, + extents->x + extents->width/2., + extents->y + extents->height/2.); + _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter); + _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend); + _cairo_xcb_picture_set_component_alpha (picture, + pattern->base.base.has_component_alpha); + + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_radial_picture (cairo_xcb_surface_t *target, + const cairo_radial_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + char buf[CAIRO_STACK_BUFFER_SIZE]; + xcb_render_fixed_t *stops; + xcb_render_color_t *colors; + xcb_render_pointfix_t p1, p2; + xcb_render_fixed_t r1, r2; + cairo_matrix_t matrix; + cairo_circle_double_t extremes[2]; + cairo_xcb_picture_t *picture; + cairo_status_t status; + unsigned int n_stops; + + _cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes); + + picture = (cairo_xcb_picture_t *) + _cairo_xcb_screen_lookup_radial_picture (target->screen, pattern); + if (picture != NULL) + goto setup_picture; + + stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf)); + if (unlikely (stops == NULL)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + picture = _cairo_xcb_picture_create (target->screen, + target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32], + PIXMAN_a8r8g8b8, + -1, -1); + if (unlikely (picture->base.status)) { + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + return picture; + } + picture->filter = CAIRO_FILTER_DEFAULT; + + colors = (xcb_render_color_t *) (stops + n_stops); + + p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); + p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); + p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); + p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); + + r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); + r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); + + _cairo_xcb_connection_render_create_radial_gradient (target->connection, + picture->picture, + p1, p2, r1, r2, + n_stops, + stops, colors); + + if (stops != (xcb_render_fixed_t *) buf) + free (stops); + + status = _cairo_xcb_screen_store_radial_picture (target->screen, + pattern, + &picture->base); + if (unlikely (status)) { + cairo_surface_destroy (&picture->base); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + +setup_picture: + _cairo_xcb_picture_set_matrix (picture, &matrix, + pattern->base.base.filter, + extents->x + extents->width/2., + extents->y + extents->height/2.); + _cairo_xcb_picture_set_filter (picture, pattern->base.base.filter); + _cairo_xcb_picture_set_extend (picture, pattern->base.base.extend); + _cairo_xcb_picture_set_component_alpha (picture, + pattern->base.base.has_component_alpha); + + return picture; +} + +static cairo_xcb_picture_t * +_copy_to_picture (cairo_xcb_surface_t *source) +{ + cairo_xcb_picture_t *picture; + uint32_t values[] = { 0, 1 }; + + if (source->deferred_clear) { + cairo_status_t status = _cairo_xcb_surface_clear (source); + if (unlikely (status)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + + picture = _cairo_xcb_picture_create (source->screen, + source->xrender_format, + source->pixman_format, + source->width, + source->height); + if (unlikely (picture->base.status)) + return picture; + + _cairo_xcb_connection_render_create_picture (source->connection, + picture->picture, + source->drawable, + source->xrender_format, + XCB_RENDER_CP_GRAPHICS_EXPOSURE | + XCB_RENDER_CP_SUBWINDOW_MODE, + values); + + return picture; +} + +static void +_cairo_xcb_surface_setup_surface_picture(cairo_xcb_picture_t *picture, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_filter_t filter; + + filter = pattern->base.filter; + if (filter != CAIRO_FILTER_NEAREST && + _cairo_matrix_is_pixel_exact (&pattern->base.matrix)) + { + filter = CAIRO_FILTER_NEAREST; + } + _cairo_xcb_picture_set_filter (picture, filter); + + _cairo_xcb_picture_set_matrix (picture, + &pattern->base.matrix, filter, + extents->x + extents->width/2., + extents->y + extents->height/2.); + + + _cairo_xcb_picture_set_extend (picture, pattern->base.extend); + _cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha); +} + +static cairo_xcb_picture_t * +record_to_picture (cairo_surface_t *target, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t tmp_pattern; + cairo_xcb_picture_t *picture; + cairo_status_t status; + cairo_matrix_t matrix; + cairo_surface_t *tmp; + cairo_surface_t *source; + cairo_rectangle_int_t limit; + cairo_extend_t extend; + + /* XXX: The following was once more or less copied from cairo-xlibs-ource.c, + * record_source() and recording_pattern_get_surface(), can we share a + * single version? + */ + + /* First get the 'real' recording surface and figure out the size for tmp */ + source = _cairo_pattern_get_source (pattern, &limit); + assert (_cairo_surface_is_recording (source)); + + if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { + double x1, y1, x2, y2; + + matrix = pattern->base.matrix; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + x1 = limit.x; + y1 = limit.y; + x2 = limit.x + limit.width; + y2 = limit.y + limit.height; + + _cairo_matrix_transform_bounding_box (&matrix, + &x1, &y1, &x2, &y2, NULL); + + limit.x = floor (x1); + limit.y = floor (y1); + limit.width = ceil (x2) - limit.x; + limit.height = ceil (y2) - limit.y; + } + extend = pattern->base.extend; + if (_cairo_rectangle_contains_rectangle (&limit, extents)) + extend = CAIRO_EXTEND_NONE; + if (extend == CAIRO_EXTEND_NONE && ! _cairo_rectangle_intersect (&limit, extents)) + return _cairo_xcb_transparent_picture ((cairo_xcb_surface_t *) target); + + /* Now draw the recording surface to an xcb surface */ + tmp = _cairo_surface_create_scratch (target, + source->content, + limit.width, + limit.height, + CAIRO_COLOR_TRANSPARENT); + if (tmp->status != CAIRO_STATUS_SUCCESS) { + return (cairo_xcb_picture_t *) tmp; + } + + cairo_matrix_init_translate (&matrix, limit.x, limit.y); + cairo_matrix_multiply (&matrix, &matrix, &pattern->base.matrix); + + status = _cairo_recording_surface_replay_with_clip (source, + &matrix, tmp, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (tmp); + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + } + + /* Now that we have drawn this to an xcb surface, try again with that */ + _cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base); + tmp_pattern.surface = tmp; + cairo_matrix_init_translate (&tmp_pattern.base.matrix, -limit.x, -limit.y); + + picture = _copy_to_picture ((cairo_xcb_surface_t *) tmp); + if (picture->base.status == CAIRO_STATUS_SUCCESS) + _cairo_xcb_surface_setup_surface_picture (picture, &tmp_pattern, extents); + cairo_surface_destroy (tmp); + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_surface_picture (cairo_xcb_surface_t *target, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *source = pattern->surface; + cairo_xcb_picture_t *picture; + + picture = (cairo_xcb_picture_t *) + _cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend); + if (picture != NULL) { + if (picture->screen == target->screen) { + picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base); + _cairo_xcb_surface_setup_surface_picture (picture, pattern, extents); + return picture; + } + picture = NULL; + } + + if (source->type == CAIRO_SURFACE_TYPE_XCB) + { + if (_cairo_surface_is_xcb(source)) { + cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) source; + if (xcb->screen == target->screen && xcb->fallback == NULL) { + picture = _copy_to_picture ((cairo_xcb_surface_t *) source); + if (unlikely (picture->base.status)) + return picture; + } + } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target; + + /* XXX repeat interval with source clipping? */ + if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) { + xcb_rectangle_t rect; + + picture = _copy_to_picture (xcb); + if (unlikely (picture->base.status)) + return picture; + + rect.x = sub->extents.x; + rect.y = sub->extents.y; + rect.width = sub->extents.width; + rect.height = sub->extents.height; + + _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection, + picture->picture, + 0, 0, + 1, &rect); + picture->x0 = rect.x; + picture->y0 = rect.y; + picture->width = rect.width; + picture->height = rect.height; + } + } else if (_cairo_surface_is_snapshot (source)) { + cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source; + cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target; + + if (xcb->screen == target->screen && xcb->fallback == NULL) { + picture = _copy_to_picture (xcb); + if (unlikely (picture->base.status)) + return picture; + } + } + } +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS + else if (source->type == CAIRO_SURFACE_TYPE_XLIB) + { + if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) { + cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) source)->xcb; + if (xcb->screen == target->screen && xcb->fallback == NULL) { + picture = _copy_to_picture (xcb); + if (unlikely (picture->base.status)) + return picture; + } + } else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; + cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb; + + if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) { + xcb_rectangle_t rect; + + picture = _copy_to_picture (xcb); + if (unlikely (picture->base.status)) + return picture; + + rect.x = sub->extents.x; + rect.y = sub->extents.y; + rect.width = sub->extents.width; + rect.height = sub->extents.height; + + _cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection, + picture->picture, + 0, 0, + 1, &rect); + picture->x0 = rect.x; + picture->y0 = rect.y; + picture->width = rect.width; + picture->height = rect.height; + } + } else if (_cairo_surface_is_snapshot (source)) { + cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source; + cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb; + + if (xcb->screen == target->screen && xcb->fallback == NULL) { + picture = _copy_to_picture (xcb); + if (unlikely (picture->base.status)) + return picture; + } + } + } +#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 + * only drew part of the recording surface. + * TODO: When can we safely attach a snapshot? + */ + return record_to_picture(&target->base, pattern, extents); + } + + if (picture == NULL) { + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); + if (unlikely (status)) + return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status); + + if (image->format != CAIRO_FORMAT_INVALID) { + xcb_render_pictformat_t format; + + format = target->screen->connection->standard_formats[image->format]; + + picture = _picture_from_image (target, format, image, NULL); + _cairo_surface_release_source_image (source, image, image_extra); + } else { + cairo_image_surface_t *conv; + xcb_render_pictformat_t render_format; + + /* XXX XRenderPutImage! */ + + conv = _cairo_image_surface_coerce (image); + _cairo_surface_release_source_image (source, image, image_extra); + if (unlikely (conv->base.status)) + return (cairo_xcb_picture_t *) conv; + + render_format = target->screen->connection->standard_formats[conv->format]; + picture = _picture_from_image (target, render_format, conv, NULL); + cairo_surface_destroy (&conv->base); + } + + if (unlikely (picture->base.status)) + return picture; + } + + /* XXX: This causes too many problems and bugs, let's skip it for now. */ +#if 0 + _cairo_surface_attach_snapshot (source, + &picture->base, + NULL); +#endif + + _cairo_xcb_surface_setup_surface_picture (picture, pattern, extents); + return picture; +} + +static cairo_xcb_picture_t * +_cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + if (pattern == NULL) + return _cairo_xcb_white_picture (target); + + if (! _pattern_is_supported (target->connection->flags, pattern)) + return _render_to_picture (target, pattern, extents); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern); + + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_xcb_linear_picture (target, + (cairo_linear_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_xcb_radial_picture (target, + (cairo_radial_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_xcb_surface_picture (target, + (cairo_surface_pattern_t *) pattern, + extents); + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return _render_to_picture (target, pattern, extents); + } +} + +COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t)); + +static cairo_status_t +_render_fill_boxes (void *abstract_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_xcb_surface_t *dst = abstract_dst; + xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; + xcb_rectangle_t *xrects = stack_xrects; + xcb_render_color_t render_color; + int render_op = _render_operator (op); + struct _cairo_boxes_chunk *chunk; + int max_count; + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + max_count = 0; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + if (chunk->count > max_count) + max_count = chunk->count; + } + if (max_count > ARRAY_LENGTH (stack_xrects)) { + xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t)); + if (unlikely (xrects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + int i, j; + + for (i = j = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.x); + int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y); + int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x); + int y2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.y); + + if (x2 > x1 && y2 > y1) { + xrects[j].x = x1; + xrects[j].y = y1; + xrects[j].width = x2 - x1; + xrects[j].height = y2 - y1; + j++; + } + } + + if (j) { + _cairo_xcb_connection_render_fill_rectangles + (dst->connection, + render_op, dst->picture, + render_color, j, xrects); + } + } + + if (xrects != stack_xrects) + free (xrects); + + return CAIRO_STATUS_SUCCESS; +} + +/* pixel aligned, non-overlapping boxes */ +static cairo_int_status_t +_render_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + const cairo_pattern_t *mask_pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes) +{ + cairo_xcb_picture_t *src, *mask; + const struct _cairo_boxes_chunk *chunk; + xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)]; + xcb_rectangle_t *clip_boxes; + cairo_rectangle_int_t stack_extents; + cairo_status_t status; + int num_boxes; + int render_op; + + render_op = _render_operator (op); + + if (src_pattern == NULL) { + src_pattern = mask_pattern; + mask_pattern = NULL; + } + + /* amalgamate into a single Composite call by setting a clip region */ + clip_boxes = stack_boxes; + if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) { + clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t)); + if (unlikely (clip_boxes == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); + status = src->base.status; + if (unlikely (status)) + goto cleanup_boxes; + + num_boxes = 0; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + int x = _cairo_fixed_integer_round_down (box[i].p1.x); + int y = _cairo_fixed_integer_round_down (box[i].p1.y); + int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x; + int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y; + + if (width && height) { + clip_boxes[num_boxes].x = x; + clip_boxes[num_boxes].y = y; + clip_boxes[num_boxes].width = width; + clip_boxes[num_boxes].height = height; + num_boxes++; + } + } + } + + if (num_boxes) { + if (num_boxes > 1) { + _cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection, + dst->picture, + 0, 0, + num_boxes, + clip_boxes); + } else { + stack_extents.x = clip_boxes[0].x; + stack_extents.y = clip_boxes[0].y; + stack_extents.width = clip_boxes[0].width; + stack_extents.height = clip_boxes[0].height; + extents = &stack_extents; + } + + if (mask_pattern != NULL) { + mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); + status = mask->base.status; + if (unlikely (status)) + goto cleanup_clip; + + _cairo_xcb_connection_render_composite (dst->connection, + render_op, + src->picture, + mask->picture, + dst->picture, + src->x + extents->x, src->y + extents->y, + mask->x + extents->x, mask->y + extents->y, + extents->x, extents->y, + extents->width, extents->height); + + cairo_surface_destroy (&mask->base); + } else { + _cairo_xcb_connection_render_composite (dst->connection, + render_op, + src->picture, + XCB_NONE, + dst->picture, + src->x + extents->x, src->y + extents->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + +cleanup_clip: + + if (num_boxes > 1) + _cairo_xcb_surface_clear_clip_region (dst); + } + + cairo_surface_destroy (&src->base); + +cleanup_boxes: + + if (clip_boxes != stack_boxes) + free (clip_boxes); + + return status; +} + + +#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768) +#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767) + +static cairo_bool_t +_line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x <= CAIRO_FIXED_16_16_MIN || + line->p1.x >= CAIRO_FIXED_16_16_MAX || + + line->p2.x <= CAIRO_FIXED_16_16_MIN || + line->p2.x >= CAIRO_FIXED_16_16_MAX || + + line->p1.y <= CAIRO_FIXED_16_16_MIN || + line->p1.y >= CAIRO_FIXED_16_16_MAX || + + line->p2.y <= CAIRO_FIXED_16_16_MIN || + line->p2.y >= CAIRO_FIXED_16_16_MAX; +} + +static void +_project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + xcb_render_linefix_t *out) +{ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} + +typedef struct { + cairo_traps_t traps; + cairo_antialias_t antialias; +} composite_traps_info_t; + +COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t)); + +static cairo_int_status_t +_composite_traps (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + composite_traps_info_t *info = closure; + const cairo_traps_t *traps = &info->traps; + cairo_xcb_picture_t *src; + cairo_format_t format; + xcb_render_pictformat_t xrender_format; + xcb_render_trapezoid_t *xtraps; + int render_reference_x, render_reference_y; + cairo_status_t status; + int i; + + if (dst->deferred_clear) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) + return status; + } + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + if (info->antialias == CAIRO_ANTIALIAS_NONE) + format = CAIRO_FORMAT_A1; + else + format = CAIRO_FORMAT_A8; + xrender_format = dst->screen->connection->standard_formats[format]; + + xtraps = (xcb_render_trapezoid_t *) traps->traps; + for (i = 0; i < traps->num_traps; i++) { + cairo_trapezoid_t t = traps->traps[i]; + + /* top/bottom will be clamped to surface bounds */ + xtraps[i].top = _cairo_fixed_to_16_16 (t.top); + xtraps[i].top -= dst_y << 16; + xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom); + xtraps[i].bottom -= dst_y << 16; + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (_line_exceeds_16_16 (&t.left))) { + _project_line_x_onto_16_16 (&t.left, + t.top, + t.bottom, + &xtraps[i].left); + xtraps[i].left.p1.y = xtraps[i].top; + xtraps[i].left.p2.y = xtraps[i].bottom; + } else { + xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x); + xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y); + xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x); + xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y); + } + xtraps[i].left.p1.x -= dst_x << 16; + xtraps[i].left.p1.y -= dst_y << 16; + xtraps[i].left.p2.x -= dst_x << 16; + xtraps[i].left.p2.y -= dst_y << 16; + + if (unlikely (_line_exceeds_16_16 (&t.right))) { + _project_line_x_onto_16_16 (&t.right, + t.top, + t.bottom, + &xtraps[i].right); + xtraps[i].right.p1.y = xtraps[i].top; + xtraps[i].right.p2.y = xtraps[i].bottom; + } else { + xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x); + xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y); + xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x); + xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y); + } + xtraps[i].right.p1.x -= dst_x << 16; + xtraps[i].right.p1.y -= dst_y << 16; + xtraps[i].right.p2.x -= dst_x << 16; + xtraps[i].right.p2.y -= dst_y << 16; + } + + if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { + render_reference_x = xtraps[0].left.p1.x >> 16; + render_reference_y = xtraps[0].left.p1.y >> 16; + } else { + render_reference_x = xtraps[0].left.p2.x >> 16; + render_reference_y = xtraps[0].left.p2.y >> 16; + } + render_reference_x += src->x + dst_x; + render_reference_y += src->y + dst_y; + + _cairo_xcb_surface_set_precision (dst, info->antialias); + _cairo_xcb_connection_render_trapezoids (dst->connection, + _render_operator (op), + src->picture, + dst->picture, + xrender_format, + render_reference_x, + render_reference_y, + traps->num_traps, xtraps); + + cairo_surface_destroy (&src->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* low-level composite driver */ + +static cairo_xcb_surface_t * +get_clip_surface (const cairo_clip_t *clip, + cairo_xcb_surface_t *target, + int *tx, int *ty) +{ + cairo_surface_t *surface; + cairo_status_t status; + + surface = _cairo_surface_create_scratch (&target->base, + CAIRO_CONTENT_ALPHA, + clip->extents.width, + clip->extents.height, + CAIRO_COLOR_WHITE); + if (unlikely (surface->status)) + return (cairo_xcb_surface_t *) surface; + + assert (surface->backend == &_cairo_xcb_surface_backend); + status = _cairo_clip_combine_with_surface (clip, surface, + clip->extents.x, clip->extents.y); + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + *tx = clip->extents.x; + *ty = clip->extents.y; + + return (cairo_xcb_surface_t *) surface; +} + +typedef cairo_int_status_t +(*xcb_draw_func_t) (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip); + +static void do_unaligned_row(void (*blt)(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage), + void *closure, + const cairo_box_t *b, + int tx, int y, int h, + uint16_t coverage) +{ + int x1 = _cairo_fixed_integer_part (b->p1.x) - tx; + int x2 = _cairo_fixed_integer_part (b->p2.x) - tx; + if (x2 > x1) { + if (! _cairo_fixed_is_integer (b->p1.x)) { + blt(closure, x1, y, 1, h, + coverage * (256 - _cairo_fixed_fractional_part (b->p1.x))); + x1++; + } + + if (x2 > x1) + blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8)); + + if (! _cairo_fixed_is_integer (b->p2.x)) + blt(closure, x2, y, 1, h, + coverage * _cairo_fixed_fractional_part (b->p2.x)); + } else + blt(closure, x1, y, 1, h, + coverage * (b->p2.x - b->p1.x)); +} + +static void do_unaligned_box(void (*blt)(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage), + void *closure, + const cairo_box_t *b, int tx, int ty) +{ + int y1 = _cairo_fixed_integer_part (b->p1.y) - ty; + int y2 = _cairo_fixed_integer_part (b->p2.y) - ty; + if (y2 > y1) { + if (! _cairo_fixed_is_integer (b->p1.y)) { + do_unaligned_row(blt, closure, b, tx, y1, 1, + 256 - _cairo_fixed_fractional_part (b->p1.y)); + y1++; + } + + if (y2 > y1) + do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256); + + if (! _cairo_fixed_is_integer (b->p2.y)) + do_unaligned_row(blt, closure, b, tx, y2, 1, + _cairo_fixed_fractional_part (b->p2.y)); + } else + do_unaligned_row(blt, closure, b, tx, y1, 1, + b->p2.y - b->p1.y); +} + + +static void blt_in(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + cairo_xcb_surface_t *mask = closure; + xcb_render_color_t color; + xcb_rectangle_t rect; + + if (coverage == 0xffff) + return; + + color.red = color.green = color.blue = 0; + color.alpha = coverage; + + rect.x = x; + rect.y = y; + rect.width = w; + rect.height = h; + + _cairo_xcb_connection_render_fill_rectangles (mask->connection, + XCB_RENDER_PICT_OP_IN, + mask->picture, + color, 1, &rect); +} + +static cairo_xcb_surface_t * +_create_composite_mask (cairo_clip_t *clip, + xcb_draw_func_t draw_func, + xcb_draw_func_t mask_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t*extents) +{ + cairo_xcb_surface_t *surface; + cairo_bool_t need_clip_combine; + cairo_int_status_t status; + + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA, + extents->width, extents->height); + if (unlikely (surface->base.status)) + return surface; + + _cairo_xcb_surface_ensure_picture (surface); + + surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT; + surface->deferred_clear = TRUE; + surface->base.is_clear = TRUE; + + if (mask_func) { + status = mask_func (draw_closure, surface, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, clip); + if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) + return surface; + } + + /* Is it worth setting the clip region here? */ + status = draw_func (draw_closure, surface, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); + } + + if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + int i; + + for (i = 0; i < clip->num_boxes; i++) { + cairo_box_t *b = &clip->boxes[i]; + + if (! _cairo_fixed_is_integer (b->p1.x) || + ! _cairo_fixed_is_integer (b->p1.y) || + ! _cairo_fixed_is_integer (b->p2.x) || + ! _cairo_fixed_is_integer (b->p2.y)) + { + do_unaligned_box(blt_in, surface, b, extents->x, extents->y); + } + } + + need_clip_combine = clip->path != NULL; + } else + need_clip_combine = ! _cairo_clip_is_region (clip); + + if (need_clip_combine) { + status = _cairo_clip_combine_with_surface (clip, &surface->base, + extents->x, extents->y); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status); + } + } + + return surface; +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +_clip_and_composite_with_mask (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *pattern, + xcb_draw_func_t draw_func, + xcb_draw_func_t mask_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t*extents) +{ + cairo_xcb_surface_t *mask; + cairo_xcb_picture_t *src; + + mask = _create_composite_mask (clip, + draw_func, mask_func, draw_closure, + dst, extents); + if (unlikely (mask->base.status)) + return mask->base.status; + + if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) { + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) { + cairo_surface_destroy (&mask->base); + return src->base.status; + } + + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + cairo_surface_destroy (&src->base); + } else { + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + mask->picture, + XCB_NONE, + dst->picture, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + cairo_surface_destroy (&mask->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +_clip_and_composite_combine (cairo_clip_t *clip, + cairo_operator_t op, + const cairo_pattern_t *pattern, + xcb_draw_func_t draw_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t*extents) +{ + cairo_xcb_surface_t *tmp; + cairo_xcb_surface_t *clip_surface; + int clip_x = 0, clip_y = 0; + xcb_render_picture_t clip_picture; + cairo_status_t status; + + tmp = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_similar (dst, dst->base.content, + extents->width, extents->height); + if (unlikely (tmp->base.status)) + return tmp->base.status; + + /* create_similar() could have done a fallback to an image surface */ + assert (tmp->base.backend == &_cairo_xcb_surface_backend); + + _cairo_xcb_surface_ensure_picture (tmp); + + if (pattern == NULL) { + status = (*draw_func) (draw_closure, tmp, + CAIRO_OPERATOR_ADD, NULL, + extents->x, extents->y, + extents, NULL); + } else { + /* Initialize the temporary surface from the destination surface */ + if (! dst->base.is_clear || + (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0) + { + /* XCopyArea may actually be quicker here. + * A good driver should translate if appropriate. + */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_SRC, + dst->picture, + XCB_NONE, + tmp->picture, + extents->x, extents->y, + 0, 0, + 0, 0, + extents->width, extents->height); + } + else + { + xcb_render_color_t clear; + xcb_rectangle_t xrect; + + clear.red = clear.green = clear.blue = clear.alpha = 0; + + xrect.x = xrect.y = 0; + xrect.width = extents->width; + xrect.height = extents->height; + + _cairo_xcb_connection_render_fill_rectangles (dst->connection, + XCB_RENDER_PICT_OP_CLEAR, + dst->picture, + clear, 1, &xrect); + } + + status = (*draw_func) (draw_closure, tmp, op, pattern, + extents->x, extents->y, + extents, NULL); + } + if (unlikely (status)) + goto CLEANUP_SURFACE; + + clip_surface = get_clip_surface (clip, dst, &clip_x, &clip_y); + status = clip_surface->base.status; + if (unlikely (status)) + goto CLEANUP_SURFACE; + + assert (clip_surface->base.backend == &_cairo_xcb_surface_backend); + clip_picture = clip_surface->picture; + assert (clip_picture != XCB_NONE); + + if (dst->base.is_clear) { + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_SRC, + tmp->picture, clip_picture, dst->picture, + 0, 0, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } else { + /* Punch the clip out of the destination */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + clip_picture, XCB_NONE, dst->picture, + extents->x - clip_x, + extents->y - clip_y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now add the two results together */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_ADD, + tmp->picture, clip_picture, dst->picture, + 0, 0, + extents->x - clip_x, + extents->y - clip_y, + extents->x, extents->y, + extents->width, extents->height); + } + cairo_surface_destroy (&clip_surface->base); + + CLEANUP_SURFACE: + cairo_surface_destroy (&tmp->base); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +_clip_and_composite_source (cairo_clip_t *clip, + const cairo_pattern_t *pattern, + xcb_draw_func_t draw_func, + xcb_draw_func_t mask_func, + void *draw_closure, + cairo_xcb_surface_t *dst, + const cairo_rectangle_int_t *extents) +{ + cairo_xcb_surface_t *mask; + cairo_xcb_picture_t *src; + + /* Create a surface that is mask IN clip */ + mask = _create_composite_mask (clip, + draw_func, mask_func, draw_closure, + dst, extents); + if (unlikely (mask->base.status)) + return mask->base.status; + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) { + cairo_surface_destroy (&mask->base); + return src->base.status; + } + + if (dst->base.is_clear) { + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_SRC, + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } else { + /* Compute dest' = dest OUT (mask IN clip) */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, + XCB_NONE, + dst->picture, + 0, 0, 0, 0, + extents->x, extents->y, + extents->width, extents->height); + + /* Now compute (src IN (mask IN clip)) ADD dest' */ + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_ADD, + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x, extents->y, + extents->width, extents->height); + } + + cairo_surface_destroy (&src->base); + cairo_surface_destroy (&mask->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +can_reduce_alpha_op (cairo_operator_t op) +{ + int iop = op; + switch (iop) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_ADD: + return TRUE; + default: + return FALSE; + } +} + +static cairo_bool_t +reduce_alpha_op (cairo_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return dst->is_clear && + dst->content == CAIRO_CONTENT_ALPHA && + _cairo_pattern_is_opaque_solid (pattern) && + can_reduce_alpha_op (op); +} + +static cairo_status_t +_cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst, + const cairo_composite_rectangles_t *rects) +{ + xcb_rectangle_t xrects[4]; + int n; + + if (rects->bounded.width == rects->unbounded.width && + rects->bounded.height == rects->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + + n = 0; + if (rects->bounded.width == 0 || rects->bounded.height == 0) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->unbounded.width; + xrects[n].y = rects->unbounded.y; + xrects[n].height = rects->unbounded.height; + n++; + } else { + /* top */ + if (rects->bounded.y != rects->unbounded.y) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->unbounded.width; + xrects[n].y = rects->unbounded.y; + xrects[n].height = rects->bounded.y - rects->unbounded.y; + n++; + } + /* left */ + if (rects->bounded.x != rects->unbounded.x) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->bounded.x - rects->unbounded.x; + xrects[n].y = rects->bounded.y; + xrects[n].height = rects->bounded.height; + n++; + } + /* right */ + if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { + xrects[n].x = rects->bounded.x + rects->bounded.width; + xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x; + xrects[n].y = rects->bounded.y; + xrects[n].height = rects->bounded.height; + n++; + } + /* bottom */ + if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { + xrects[n].x = rects->unbounded.x; + xrects[n].width = rects->unbounded.width; + xrects[n].y = rects->bounded.y + rects->bounded.height; + xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y; + n++; + } + } + + if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + xcb_render_color_t color; + + color.red = 0; + color.green = 0; + color.blue = 0; + color.alpha = 0; + + _cairo_xcb_connection_render_fill_rectangles (dst->connection, + XCB_RENDER_PICT_OP_CLEAR, + dst->picture, + color, n, xrects); + } else { + int i; + cairo_xcb_picture_t *src; + + src = _cairo_xcb_transparent_picture (dst); + if (unlikely (src->base.status)) + return src->base.status; + + for (i = 0; i < n; i++) { + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_CLEAR, + src->picture, XCB_NONE, dst->picture, + 0, 0, + 0, 0, + xrects[i].x, xrects[i].y, + xrects[i].width, xrects[i].height); + } + cairo_surface_destroy (&src->base); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst, + const cairo_composite_rectangles_t *rects, + cairo_clip_t *clip) +{ + cairo_xcb_surface_t *mask; + int mask_x = 0, mask_y = 0; + + mask = get_clip_surface (clip, dst, &mask_x, &mask_y); + if (unlikely (mask->base.status)) + return mask->base.status; + + /* top */ + if (rects->bounded.y != rects->unbounded.y) { + int x = rects->unbounded.x; + int y = rects->unbounded.y; + int width = rects->unbounded.width; + int height = rects->bounded.y - y; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + /* left */ + if (rects->bounded.x != rects->unbounded.x) { + int x = rects->unbounded.x; + int y = rects->bounded.y; + int width = rects->bounded.x - x; + int height = rects->bounded.height; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + /* right */ + if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) { + int x = rects->bounded.x + rects->bounded.width; + int y = rects->bounded.y; + int width = rects->unbounded.x + rects->unbounded.width - x; + int height = rects->bounded.height; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + /* bottom */ + if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) { + int x = rects->unbounded.x; + int y = rects->bounded.y + rects->bounded.height; + int width = rects->unbounded.width; + int height = rects->unbounded.y + rects->unbounded.height - y; + + _cairo_xcb_connection_render_composite (dst->connection, + XCB_RENDER_PICT_OP_OUT_REVERSE, + mask->picture, XCB_NONE, dst->picture, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + cairo_surface_destroy (&mask->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + int i; + + if (boxes->num_boxes <= 1 && clip == NULL) + return _cairo_xcb_surface_fixup_unbounded (dst, extents); + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + _cairo_boxes_init_with_clip (&clear, clip); + + status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, + CAIRO_ANTIALIAS_DEFAULT, + &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) + status = _render_fill_boxes (dst, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + else + status = _cairo_xcb_surface_core_fill_boxes (dst, + CAIRO_COLOR_TRANSPARENT, + &clear); + } + + _cairo_boxes_fini (&clear); + + return status; +} + +cairo_status_t +_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst) +{ + xcb_gcontext_t gc; + xcb_rectangle_t rect; + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + rect.x = rect.y = 0; + rect.width = dst->width; + rect.height = dst->height; + + if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) { + xcb_render_color_t color; + uint8_t op; + + color.red = dst->deferred_clear_color.red_short; + color.green = dst->deferred_clear_color.green_short; + color.blue = dst->deferred_clear_color.blue_short; + color.alpha = dst->deferred_clear_color.alpha_short; + + if (color.alpha == 0) + op = XCB_RENDER_PICT_OP_CLEAR; + else + op = XCB_RENDER_PICT_OP_SRC; + + _cairo_xcb_surface_ensure_picture (dst); + _cairo_xcb_connection_render_fill_rectangles (dst->connection, + op, dst->picture, color, + 1, &rect); + } else { + gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth); + + /* XXX color */ + _cairo_xcb_connection_poly_fill_rectangle (dst->connection, + dst->drawable, gc, + 1, &rect); + + _cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc); + } + + _cairo_xcb_connection_release (dst->connection); + + dst->deferred_clear = FALSE; + return CAIRO_STATUS_SUCCESS; +} + +enum { + NEED_CLIP_REGION = 0x1, + NEED_CLIP_SURFACE = 0x2, + FORCE_CLIP_REGION = 0x4, +}; + +static cairo_bool_t +need_bounded_clip (cairo_composite_rectangles_t *extents) +{ + unsigned int flags = NEED_CLIP_REGION; + if (! _cairo_clip_is_region (extents->clip)) + flags |= NEED_CLIP_SURFACE; + return flags; +} + +static cairo_bool_t +need_unbounded_clip (cairo_composite_rectangles_t *extents) +{ + unsigned int flags = 0; + if (! extents->is_bounded) { + flags |= NEED_CLIP_REGION; + if (! _cairo_clip_is_region (extents->clip)) + flags |= NEED_CLIP_SURFACE; + } + if (extents->clip->path != NULL) + flags |= NEED_CLIP_SURFACE; + return flags; +} + +static cairo_status_t +_clip_and_composite (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + xcb_draw_func_t draw_func, + xcb_draw_func_t mask_func, + void *draw_closure, + cairo_composite_rectangles_t*extents, + unsigned int need_clip) +{ + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + if (dst->deferred_clear) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) { + _cairo_xcb_connection_release (dst->connection); + return status; + } + } + + _cairo_xcb_surface_ensure_picture (dst); + + if (need_clip & NEED_CLIP_REGION) { + clip_region = _cairo_clip_get_region (extents->clip); + if ((need_clip & FORCE_CLIP_REGION) == 0 && clip_region != NULL && + cairo_region_contains_rectangle (clip_region, + &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) + clip_region = NULL; + if (clip_region != NULL) { + status = _cairo_xcb_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) { + _cairo_xcb_connection_release (dst->connection); + return status; + } + } + } + + if (reduce_alpha_op (&dst->base, op, src)) { + op = CAIRO_OPERATOR_ADD; + src = NULL; + } + + if (extents->bounded.width != 0 && extents->bounded.height != 0) { + if (op == CAIRO_OPERATOR_SOURCE) { + status = _clip_and_composite_source (extents->clip, src, + draw_func, mask_func, draw_closure, + dst, &extents->bounded); + } else { + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + src = NULL; + } + + if (need_clip & NEED_CLIP_SURFACE) { + if (extents->is_bounded) { + status = _clip_and_composite_with_mask (extents->clip, op, src, + draw_func, + mask_func, + draw_closure, + dst, &extents->bounded); + } else { + status = _clip_and_composite_combine (extents->clip, op, src, + draw_func, draw_closure, + dst, &extents->bounded); + } + } else { + status = draw_func (draw_closure, + dst, op, src, + 0, 0, + &extents->bounded, + extents->clip); + } + } + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + if (need_clip & NEED_CLIP_SURFACE) + status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, extents->clip); + else + status = _cairo_xcb_surface_fixup_unbounded (dst, extents); + } + + if (clip_region) + _cairo_xcb_surface_clear_clip_region (dst); + + _cairo_xcb_connection_release (dst->connection); + + return status; +} + +static cairo_status_t +_core_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_clip_is_region (extents->clip)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op == CAIRO_OPERATOR_CLEAR) + return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes); + + if (op == CAIRO_OPERATOR_OVER) { + if (dst->base.is_clear || _cairo_pattern_is_opaque (src, &extents->bounded)) + op = CAIRO_OPERATOR_SOURCE; + } + if (op != CAIRO_OPERATOR_SOURCE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (src->type == CAIRO_PATTERN_TYPE_SOLID) { + return _cairo_xcb_surface_core_fill_boxes (dst, + &((cairo_solid_pattern_t *) src)->color, + boxes); + } + + return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes); +} + +static cairo_status_t +_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + const cairo_composite_rectangles_t *extents) +{ + cairo_clip_t *clip = extents->clip; + cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (clip); + cairo_status_t status; + + /* If the boxes are not pixel-aligned, we will need to compute a real mask */ + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (need_clip_mask && + (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_xcb_connection_acquire (dst->connection); + if (unlikely (status)) + return status; + + _cairo_xcb_surface_ensure_picture (dst); + if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask && + (op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID)) + { + const cairo_color_t *color; + + if (op == CAIRO_OPERATOR_CLEAR) + color = CAIRO_COLOR_TRANSPARENT; + else + color = &((cairo_solid_pattern_t *) src)->color; + + status = _render_fill_boxes (dst, op, color, boxes); + } + else + { + cairo_surface_pattern_t mask; + + if (need_clip_mask) { + cairo_xcb_surface_t *clip_surface; + int clip_x = 0, clip_y = 0; + + clip_surface = get_clip_surface (extents->clip, dst, + &clip_x, &clip_y); + if (unlikely (clip_surface->base.status)) + return clip_surface->base.status; + + _cairo_pattern_init_for_surface (&mask, &clip_surface->base); + mask.base.filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&mask.base.matrix, + -clip_x, + -clip_y); + cairo_surface_destroy (&clip_surface->base); + + if (op == CAIRO_OPERATOR_CLEAR) { + src = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + } + + status = _render_composite_boxes (dst, op, src, + need_clip_mask ? &mask.base : NULL, + &extents->bounded, boxes); + + if (need_clip_mask) + _cairo_pattern_fini (&mask.base); + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { + status = + _cairo_xcb_surface_fixup_unbounded_boxes (dst, extents, + clip, boxes); + } + + _cairo_xcb_connection_release (dst->connection); + + return status; +} + +static cairo_bool_t +cairo_boxes_for_each_box (cairo_boxes_t *boxes, + cairo_bool_t (*func) (cairo_box_t *box, + void *data), + void *data) +{ + struct _cairo_boxes_chunk *chunk; + int i; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) + if (! func (&chunk->base[i], data)) + return FALSE; + } + + return TRUE; +} + +struct _image_contains_box { + int width, height; + int tx, ty; +}; + +static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure) +{ + struct _image_contains_box *data = closure; + + /* The box is pixel-aligned so the truncation is safe. */ + return + _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 && + _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 && + _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width && + _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height; +} + +struct _image_upload_box { + cairo_xcb_surface_t *surface; + cairo_image_surface_t *image; + xcb_gcontext_t gc; + int tx, ty; +}; + +static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) +{ + const struct _image_upload_box *iub = closure; + /* The box is pixel-aligned so the truncation is safe. */ + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + int bpp = PIXMAN_FORMAT_BPP (iub->image->pixman_format); + int len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); + if (len == iub->image->stride) { + _cairo_xcb_connection_put_image (iub->surface->connection, + iub->surface->drawable, + iub->gc, + width, height, + x, y, + iub->image->depth, + iub->image->stride, + iub->image->data + + (y + iub->ty) * iub->image->stride + + (x + iub->tx) * bpp/8); + } else { + _cairo_xcb_connection_put_subimage (iub->surface->connection, + iub->surface->drawable, + iub->gc, + x + iub->tx, + y + iub->ty, + width, height, + bpp / 8, + iub->image->stride, + x, y, + iub->image->depth, + iub->image->data); + } + + return TRUE; +} + +static cairo_status_t +_upload_image_inplace (cairo_xcb_surface_t *surface, + const cairo_pattern_t *source, + cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *pattern; + struct _image_contains_box icb; + struct _image_upload_box iub; + cairo_image_surface_t *image; + cairo_status_t status; + int tx, ty; + + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pattern = (const cairo_surface_pattern_t *) source; + if (! _cairo_surface_is_image (pattern->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Have we already upload this image to a pixmap? */ + { + cairo_xcb_picture_t *snapshot; + + snapshot = (cairo_xcb_picture_t *) + _cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend); + if (snapshot != NULL) { + if (snapshot->screen == surface->screen) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + image = (cairo_image_surface_t *) pattern->surface; + if (image->format == CAIRO_FORMAT_INVALID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (image->depth != surface->depth) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Check that the data is entirely within the image */ + icb.width = image->width; + icb.height = image->height; + icb.tx = tx; + icb.ty = ty; + if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->deferred_clear) { + status = _cairo_xcb_surface_clear (surface); + if (unlikely (status)) + return status; + } + + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + iub.surface = surface; + iub.image = image; + iub.gc = _cairo_xcb_screen_get_gc (surface->screen, + surface->drawable, + image->depth); + iub.tx = tx; + iub.ty = ty; + cairo_boxes_for_each_box (boxes, image_upload_box, &iub); + + _cairo_xcb_screen_put_gc (surface->screen, image->depth, iub.gc); + _cairo_xcb_connection_release (surface->connection); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +trim_extents_to_traps (cairo_composite_rectangles_t *extents, + cairo_traps_t *traps) +{ + cairo_box_t box; + + /* X trims the affected area to the extents of the trapezoids, so + * we need to compensate when fixing up the unbounded area. + */ + _cairo_traps_extents (traps, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_bool_t +_mono_edge_is_vertical (const cairo_line_t *line) +{ + return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x); +} + +static cairo_bool_t +_traps_are_pixel_aligned (cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i; + + if (antialias == CAIRO_ANTIALIAS_NONE) { + for (i = 0; i < traps->num_traps; i++) { + if (! _mono_edge_is_vertical (&traps->traps[i].left) || + ! _mono_edge_is_vertical (&traps->traps[i].right)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } else { + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || + traps->traps[i].right.p1.x != traps->traps[i].right.p2.x || + ! _cairo_fixed_is_integer (traps->traps[i].top) || + ! _cairo_fixed_is_integer (traps->traps[i].bottom) || + ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || + ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) + { + traps->maybe_region = FALSE; + return FALSE; + } + } + } + + return TRUE; +} + +static void +_boxes_for_traps (cairo_boxes_t *boxes, + cairo_traps_t *traps, + cairo_antialias_t antialias) +{ + int i, j; + + _cairo_boxes_init (boxes); + + boxes->chunks.base = (cairo_box_t *) traps->traps; + boxes->chunks.size = traps->num_traps; + + if (antialias != CAIRO_ANTIALIAS_NONE) { + for (i = j = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + if (x1 == x2 || y1 == y2) + continue; + + boxes->chunks.base[j].p1.x = x1; + boxes->chunks.base[j].p1.y = y1; + boxes->chunks.base[j].p2.x = x2; + boxes->chunks.base[j].p2.y = y2; + j++; + + if (boxes->is_pixel_aligned) { + boxes->is_pixel_aligned = + _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) && + _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2); + } + } + } else { + boxes->is_pixel_aligned = TRUE; + + for (i = j = 0; i < traps->num_traps; i++) { + /* Note the traps and boxes alias so we need to take the local copies first. */ + cairo_fixed_t x1 = traps->traps[i].left.p1.x; + cairo_fixed_t x2 = traps->traps[i].right.p1.x; + cairo_fixed_t y1 = traps->traps[i].top; + cairo_fixed_t y2 = traps->traps[i].bottom; + + /* round down here to match Pixman's behavior when using traps. */ + boxes->chunks.base[j].p1.x = _cairo_fixed_round_down (x1); + boxes->chunks.base[j].p1.y = _cairo_fixed_round_down (y1); + boxes->chunks.base[j].p2.x = _cairo_fixed_round_down (x2); + boxes->chunks.base[j].p2.y = _cairo_fixed_round_down (y2); + + j += (boxes->chunks.base[j].p1.x != boxes->chunks.base[j].p2.x && + boxes->chunks.base[j].p1.y != boxes->chunks.base[j].p2.y); + } + } + + boxes->num_boxes = j; + boxes->chunks.count = j; +} + +static cairo_status_t +_composite_polygon (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_polygon_t *polygon, + cairo_antialias_t antialias, + cairo_fill_rule_t fill_rule, + cairo_composite_rectangles_t *extents) +{ + composite_traps_info_t traps; + cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip); + cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); + cairo_status_t status; + + if (polygon->num_edges == 0) { + status = CAIRO_STATUS_SUCCESS; + + if (! extents->is_bounded) { + if (cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) + clip_region = NULL; + + if (clip_surface == FALSE) { + if (clip_region != NULL) { + status = _cairo_xcb_surface_set_clip_region (dst, clip_region); + if (unlikely (status)) + return status; + } + + status = _cairo_xcb_surface_fixup_unbounded (dst, extents); + + if (clip_region != NULL) + _cairo_xcb_surface_clear_clip_region (dst); + } else { + status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, + extents, + extents->clip); + } + } + + return status; + } + + if (extents->clip->path != NULL && extents->is_bounded) { + cairo_polygon_t clipper; + cairo_fill_rule_t clipper_fill_rule; + cairo_antialias_t clipper_antialias; + + status = _cairo_clip_get_polygon (extents->clip, + &clipper, + &clipper_fill_rule, + &clipper_antialias); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + if (clipper_antialias == antialias) { + status = _cairo_polygon_intersect (polygon, fill_rule, + &clipper, clipper_fill_rule); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip); + _cairo_clip_destroy (extents->clip); + extents->clip = clip; + + fill_rule = CAIRO_FILL_RULE_WINDING; + } + _cairo_polygon_fini (&clipper); + } + } + } + + _cairo_traps_init (&traps.traps); + + status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule); + if (unlikely (status)) + goto CLEANUP_TRAPS; + + if (traps.traps.has_intersections) { + if (traps.traps.is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); + else if (traps.traps.is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) + goto CLEANUP_TRAPS; + } + + /* Use a fast path if the trapezoids consist of a simple region, + * but we can only do this if we do not have a clip surface, or can + * substitute the mask with the clip. + */ + if (traps.traps.maybe_region && + _traps_are_pixel_aligned (&traps.traps, antialias) && + (! clip_surface || + (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE))) + { + cairo_boxes_t boxes; + + _boxes_for_traps (&boxes, &traps.traps, antialias); + status = _clip_and_composite_boxes (dst, op, source, &boxes, extents); + } + else + { + /* Otherwise render the trapezoids to a mask and composite in the usual + * fashion. + */ + traps.antialias = antialias; + status = trim_extents_to_traps (extents, &traps.traps); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + unsigned int flags = 0; + + /* For unbounded operations, the X11 server will estimate the + * affected rectangle and apply the operation to that. However, + * there are cases where this is an overestimate (e.g. the + * clip-fill-{eo,nz}-unbounded test). + * + * The clip will trim that overestimate to our expectations. + */ + if (! extents->is_bounded) + flags |= FORCE_CLIP_REGION; + + status = _clip_and_composite (dst, op, source, _composite_traps, + NULL, &traps, extents, + need_unbounded_clip (extents) | flags); + } + } + +CLEANUP_TRAPS: + _cairo_traps_fini (&traps.traps); + + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_composite_rectangles_t *extents) +{ + composite_traps_info_t info; + cairo_int_status_t status; + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) && + (op == CAIRO_OPERATOR_SOURCE || + (dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)))) + { + if (boxes->num_boxes == 1 && + extents->bounded.width == dst->width && + extents->bounded.height == dst->height) + { + op = CAIRO_OPERATOR_SOURCE; + dst->deferred_clear = FALSE; + } + + status = _upload_image_inplace (dst, src, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ + if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS && + extents->clip->path != NULL && extents->is_bounded) { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; + cairo_clip_t *clip; + + clip = _cairo_clip_copy (extents->clip); + clip = _cairo_clip_intersect_boxes (clip, boxes); + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &antialias); + _cairo_clip_path_destroy (clip->path); + clip->path = NULL; + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + cairo_clip_t *saved_clip = extents->clip; + extents->clip = clip; + status = _composite_polygon (dst, op, src, + &polygon, + antialias, + fill_rule, + extents); + clip = extents->clip; + extents->clip = saved_clip; + _cairo_polygon_fini (&polygon); + } + if (clip) + _cairo_clip_destroy (clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + if (dst->deferred_clear) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) + return status; + } + + if (boxes->is_pixel_aligned && + _cairo_clip_is_region (extents->clip) && + op == CAIRO_OPERATOR_SOURCE) { + status = _upload_image_inplace (dst, src, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) + return _core_boxes (dst, op, src, boxes, extents); + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Otherwise render via a mask and composite in the usual fashion. */ + status = _cairo_traps_init_boxes (&info.traps, boxes); + if (unlikely (status)) + return status; + + info.antialias = CAIRO_ANTIALIAS_DEFAULT; + status = trim_extents_to_traps (extents, &info.traps); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = _clip_and_composite (dst, op, src, + _composite_traps, NULL, &info, + extents, need_unbounded_clip (extents)); + } + + _cairo_traps_fini (&info.traps); + return status; +} + +static cairo_int_status_t +_composite_mask (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + const cairo_pattern_t *mask_pattern = closure; + cairo_xcb_picture_t *src, *mask = NULL; + cairo_status_t status; + + if (dst->base.is_clear) { + if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) + op = CAIRO_OPERATOR_SOURCE; + } + + if (op == CAIRO_OPERATOR_SOURCE && clip == NULL) + dst->deferred_clear = FALSE; + + if (dst->deferred_clear) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) + return status; + } + + if (src_pattern != NULL) { + src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); + if (unlikely (mask->base.status)) { + cairo_surface_destroy (&src->base); + return mask->base.status; + } + + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + src->picture, + mask->picture, + dst->picture, + extents->x + src->x, extents->y + src->y, + extents->x + mask->x, extents->y + mask->y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + cairo_surface_destroy (&mask->base); + cairo_surface_destroy (&src->base); + } else { + src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + _cairo_xcb_connection_render_composite (dst->connection, + _render_operator (op), + src->picture, + XCB_NONE, + dst->picture, + extents->x + src->x, extents->y + src->y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + cairo_surface_destroy (&src->base); + } + + return CAIRO_STATUS_SUCCESS; +} + +struct composite_box_info { + cairo_xcb_surface_t *dst; + cairo_xcb_picture_t *src; + uint8_t op; +}; + +static void composite_box(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct composite_box_info *info = closure; + + if (coverage < 0xff00) { + cairo_xcb_picture_t *mask; + cairo_color_t color; + + color.red_short = color.green_short = color.blue_short = 0; + color.alpha_short = coverage; + + mask = _solid_picture (info->dst, &color); + if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { + _cairo_xcb_connection_render_composite (info->dst->connection, + info->op, + info->src->picture, + mask->picture, + info->dst->picture, + x + info->src->x, y + info->src->y, + 0, 0, + x, y, + w, h); + } + cairo_surface_destroy (&mask->base); + } else { + _cairo_xcb_connection_render_composite (info->dst->connection, + info->op, + info->src->picture, + XCB_NONE, + info->dst->picture, + x + info->src->x, y + info->src->y, + 0, 0, + x, y, + w, h); + } +} + +static cairo_int_status_t +_composite_mask_clip_boxes (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + struct composite_box_info info; + cairo_status_t status; + int i; + + assert (src_pattern == NULL); + assert (op == CAIRO_OPERATOR_ADD); + assert (dst->base.is_clear); + + if (clip->num_boxes > 1) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) + return status; + } + + info.op = XCB_RENDER_PICT_OP_SRC; + info.dst = dst; + info.src = _cairo_xcb_picture_for_pattern (dst, closure, extents); + if (unlikely (info.src->base.status)) + return info.src->base.status; + + info.src->x += dst_x; + info.src->y += dst_y; + + for (i = 0; i < clip->num_boxes; i++) + do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y); + cairo_surface_destroy (&info.src->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_composite_mask_clip (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + const cairo_pattern_t *mask_pattern = closure; + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + composite_traps_info_t info; + cairo_status_t status; + + assert (src_pattern == NULL); + assert (op == CAIRO_OPERATOR_ADD); + assert (dst->base.is_clear); + + status = _cairo_clip_get_polygon (clip, &polygon, + &fill_rule, &info.antialias); + if (unlikely (status)) + return status; + + _cairo_traps_init (&info.traps); + status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps, + &polygon, + fill_rule); + _cairo_polygon_fini (&polygon); + if (unlikely (status)) + return status; + + if (info.traps.has_intersections) { + if (info.traps.is_rectangular) + status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING); + else if (info.traps.is_rectilinear) + status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING); + else + status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING); + if (unlikely (status)) { + _cairo_traps_fini (&info.traps); + return status; + } + } + + status = _composite_traps (&info, + dst, CAIRO_OPERATOR_SOURCE, mask_pattern, + dst_x, dst_y, + extents, NULL); + _cairo_traps_fini (&info.traps); + + return status; +} + +struct composite_opacity_info { + uint8_t op; + cairo_xcb_surface_t *dst; + cairo_xcb_picture_t *src; + double opacity; +}; + +static void composite_opacity(void *closure, + int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t coverage) +{ + struct composite_opacity_info *info = closure; + cairo_xcb_picture_t *mask; + cairo_color_t color; + + color.red_short = color.green_short = color.blue_short = 0; + color.alpha_short = info->opacity * coverage; + + mask = _solid_picture (info->dst, &color); + if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { + if (info->src) { + _cairo_xcb_connection_render_composite (info->dst->connection, + info->op, + info->src->picture, + mask->picture, + info->dst->picture, + x + info->src->x, y + info->src->y, + 0, 0, + x, y, + w, h); + } else { + _cairo_xcb_connection_render_composite (info->dst->connection, + info->op, + mask->picture, + XCB_NONE, + info->dst->picture, + 0, 0, + 0, 0, + x, y, + w, h); + } + } + + cairo_surface_destroy (&mask->base); +} + +static cairo_int_status_t +_composite_opacity_boxes (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src_pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + const cairo_solid_pattern_t *mask_pattern = closure; + struct composite_opacity_info info; + cairo_status_t status; + int i; + + if (dst->base.is_clear) { + if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) + op = CAIRO_OPERATOR_SOURCE; + } + + if (op == CAIRO_OPERATOR_SOURCE && + (clip == NULL || + (clip->extents.width >= extents->width && + clip->extents.height >= extents->height))) + dst->deferred_clear = FALSE; + + if (dst->deferred_clear) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) + return status; + } + + info.op = _render_operator (op); + info.dst = dst; + + if (src_pattern != NULL) { + info.src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents); + if (unlikely (info.src->base.status)) + return info.src->base.status; + } else + info.src = NULL; + + info.opacity = mask_pattern->color.alpha; + + /* XXX for lots of boxes create a clip region for the fully opaque areas */ + if (clip) { + for (i = 0; i < clip->num_boxes; i++) + do_unaligned_box(composite_opacity, &info, + &clip->boxes[i], dst_x, dst_y); + } else { + composite_opacity(&info, + extents->x - dst_x, + extents->y - dst_y, + extents->width, + extents->height, + 0xffff); + } + cairo_surface_destroy (&info.src->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* high level rasteriser -> compositor */ + +cairo_int_status_t +_cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *composite) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; + cairo_operator_t op = composite->op; + cairo_pattern_t *source = &composite->source_pattern.base; + cairo_boxes_t boxes; + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->connection->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (composite->clip == NULL && + source->type == CAIRO_PATTERN_TYPE_SOLID && + (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_CLEAR || + (surface->base.is_clear && + (op == CAIRO_OPERATOR_ADD || op == CAIRO_OPERATOR_OVER)))) + { + surface->deferred_clear = TRUE; + surface->deferred_clear_color = composite->source_pattern.solid.color; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_clip_steal_boxes(composite->clip, &boxes); + status = _clip_and_composite_boxes (surface, op, source, &boxes, composite); + _cairo_clip_unsteal_boxes (composite->clip, &boxes); + + return status; +} + +cairo_int_status_t +_cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *composite) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; + cairo_operator_t op = composite->op; + cairo_pattern_t *source = &composite->source_pattern.base; + cairo_pattern_t *mask = &composite->mask_pattern.base; + cairo_status_t status; + + if (unlikely (! _operator_is_supported (surface->connection->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (mask->type == CAIRO_PATTERN_TYPE_SOLID && + composite->clip->path == NULL && + ! _cairo_clip_is_region (composite->clip)) { + status = _clip_and_composite (surface, op, source, + _composite_opacity_boxes, + _composite_opacity_boxes, + (void *) mask, + composite, need_unbounded_clip (composite)); + } else { + xcb_draw_func_t mask_func = NULL; + if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) + mask_func = composite->clip->path ? _composite_mask_clip : _composite_mask_clip_boxes; + status = _clip_and_composite (surface, op, source, + _composite_mask, mask_func, + (void *) mask, + composite, need_bounded_clip (composite)); + } + + return status; +} + +static cairo_int_status_t +_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst, + 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, + cairo_composite_rectangles_t *extents) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + _cairo_polygon_init_with_clip (&polygon, extents->clip); + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _composite_polygon (dst, op, source, + &polygon, antialias, + CAIRO_FILL_RULE_WINDING, + extents); + } + _cairo_polygon_fini (&polygon); + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t *dst, + 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, + cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_status_t status; + cairo_clip_t *clip; + int x, y; + + x = extents->bounded.x; + y = extents->bounded.y; + image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8, + extents->bounded.width, + extents->bounded.height); + if (unlikely (image->status)) + return image->status; + + clip = _cairo_clip_copy_region (extents->clip); + status = _cairo_surface_offset_stroke (image, x, y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + _cairo_clip_destroy (clip); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask; + + _cairo_pattern_init_for_surface (&mask, image); + mask.base.filter = CAIRO_FILTER_NEAREST; + + cairo_matrix_init_translate (&mask.base.matrix, -x, -y); + status = _clip_and_composite (dst, op, source, + _composite_mask, NULL, &mask.base, + extents, need_bounded_clip (extents)); + _cairo_pattern_fini (&mask.base); + } + + cairo_surface_finish (image); + cairo_surface_destroy (image); + + return status; +} + +cairo_int_status_t +_cairo_xcb_render_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_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; + cairo_operator_t op = composite->op; + cairo_pattern_t *source = &composite->source_pattern.base; + cairo_int_status_t status; + + if (unlikely (! _operator_is_supported (surface->connection->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, composite->clip); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, composite); + } + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) { + status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + composite); + } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { + status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + composite); + } else { + ASSERT_NOT_REACHED; + } + } + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst, + 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, + cairo_composite_rectangles_t *extents) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + _cairo_polygon_init_with_clip (&polygon, extents->clip); + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _composite_polygon (dst, op, source, + &polygon, + antialias, + fill_rule, + extents); + } + _cairo_polygon_fini (&polygon); + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t *dst, + 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, + cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_status_t status; + cairo_clip_t *clip; + int x, y; + + x = extents->bounded.x; + y = extents->bounded.y; + image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8, + extents->bounded.width, + extents->bounded.height); + if (unlikely (image->status)) + return image->status; + + clip = _cairo_clip_copy_region (extents->clip); + status = _cairo_surface_offset_fill (image, x, y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + path, fill_rule, tolerance, antialias, + clip); + _cairo_clip_destroy (clip); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask; + + _cairo_pattern_init_for_surface (&mask, image); + mask.base.filter = CAIRO_FILTER_NEAREST; + + cairo_matrix_init_translate (&mask.base.matrix, -x, -y); + status = _clip_and_composite (dst, op, source, + _composite_mask, NULL, &mask.base, + extents, need_bounded_clip (extents)); + + _cairo_pattern_fini (&mask.base); + } + + cairo_surface_finish (image); + cairo_surface_destroy (image); + + return status; +} + +cairo_int_status_t +_cairo_xcb_render_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_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; + cairo_operator_t op = composite->op; + cairo_pattern_t *source = &composite->source_pattern.base; + cairo_int_status_t status; + + if (unlikely (! _operator_is_supported (surface->connection->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS | + CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, composite->clip); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (surface, op, source, + &boxes, composite); + } + _cairo_boxes_fini (&boxes); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) { + status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path, + fill_rule, tolerance, antialias, + composite); + } else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) { + status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path, + fill_rule, tolerance, antialias, + composite); + } else { + ASSERT_NOT_REACHED; + } + } + + return status; +} + +static cairo_status_t +_cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_content_t content; + cairo_status_t status; + cairo_clip_t *clip; + int x, y; + + content = CAIRO_CONTENT_ALPHA; + if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) + content = CAIRO_CONTENT_COLOR_ALPHA; + + x = extents->bounded.x; + y = extents->bounded.y; + image = _cairo_xcb_surface_create_similar_image (dst, + _cairo_format_from_content (content), + extents->bounded.width, + extents->bounded.height); + if (unlikely (image->status)) + return image->status; + + clip = _cairo_clip_copy_region (extents->clip); + status = _cairo_surface_offset_glyphs (image, x, y, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + scaled_font, glyphs, num_glyphs, + clip); + _cairo_clip_destroy (clip); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask; + + _cairo_pattern_init_for_surface (&mask, image); + mask.base.filter = CAIRO_FILTER_NEAREST; + if (content & CAIRO_CONTENT_COLOR) + mask.base.has_component_alpha = TRUE; + + cairo_matrix_init_translate (&mask.base.matrix, -x, -y); + status = _clip_and_composite (dst, op, source, + _composite_mask, NULL, &mask.base, + extents, need_bounded_clip (extents)); + + _cairo_pattern_fini (&mask.base); + } + + cairo_surface_finish (image); + cairo_surface_destroy (image); + + return status; +} + +/* Build a struct of the same size of #cairo_glyph_t that can be used both as + * an input glyph with double coordinates, and as "working" glyph with + * integer from-current-point offsets. */ +typedef union { + cairo_glyph_t d; + unsigned long index; + struct { + unsigned long index; + int x; + int y; + } i; +} cairo_xcb_glyph_t; + +/* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */ +COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t)); + +typedef struct { + cairo_scaled_font_t *font; + cairo_xcb_glyph_t *glyphs; + int num_glyphs; + cairo_bool_t use_mask; +} composite_glyphs_info_t; + +static cairo_status_t +_can_composite_glyphs (cairo_xcb_surface_t *dst, + cairo_rectangle_int_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs) +{ +#define GLYPH_CACHE_SIZE 64 + cairo_box_t bbox_cache[GLYPH_CACHE_SIZE]; + unsigned long glyph_cache[GLYPH_CACHE_SIZE]; +#undef GLYPH_CACHE_SIZE + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_glyph_t *glyphs_end, *valid_glyphs; + const int max_glyph_size = dst->connection->maximum_request_length - 64; + + /* We must initialize the cache with values that cannot match the + * "hash" to guarantee that when compared for the first time they + * will result in a mismatch. The hash function is simply modulus, + * so we cannot use 0 in glyph_cache[0], but we can use it in all + * other array cells. + */ + memset (glyph_cache, 0, sizeof (glyph_cache)); + glyph_cache[0] = 1; + + /* Scan for oversized glyphs or glyphs outside the representable + * range and fallback in that case, discard glyphs outside of the + * image. + */ + valid_glyphs = glyphs; + for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) { + double x1, y1, x2, y2; + cairo_scaled_glyph_t *glyph; + cairo_box_t *bbox; + int width, height, len; + int g; + + g = glyphs->index % ARRAY_LENGTH (glyph_cache); + if (glyph_cache[g] != glyphs->index) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs->index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &glyph); + if (unlikely (status)) + break; + + glyph_cache[g] = glyphs->index; + bbox_cache[g] = glyph->bbox; + } + bbox = &bbox_cache[g]; + + /* Drop glyphs outside the clipping */ + x1 = _cairo_fixed_to_double (bbox->p1.x); + y1 = _cairo_fixed_to_double (bbox->p1.y); + y2 = _cairo_fixed_to_double (bbox->p2.y); + x2 = _cairo_fixed_to_double (bbox->p2.x); + if (unlikely (glyphs->x + x2 <= extents->x || + glyphs->y + y2 <= extents->y || + glyphs->x + x1 >= extents->x + extents->width || + glyphs->y + y1 >= extents->y + extents->height)) + { + (*num_glyphs)--; + continue; + } + + /* XRenderAddGlyph does not handle a glyph surface larger than + * the extended maximum XRequest size. + */ + width = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x); + height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y); + len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height; + if (unlikely (len >= max_glyph_size)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + /* The glyph coordinates must be representable in an int16_t. + * When possible, they will be expressed as an offset from the + * previous glyph, otherwise they will be an offset from the + * operation extents or from the surface origin. If the last + * two options are not valid, fallback. + */ + if (unlikely (glyphs->x > INT16_MAX || + glyphs->y > INT16_MAX || + glyphs->x - extents->x < INT16_MIN || + glyphs->y - extents->y < INT16_MIN)) + { + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + + if (unlikely (valid_glyphs != glyphs)) + *valid_glyphs = *glyphs; + valid_glyphs++; + } + + if (unlikely (valid_glyphs != glyphs)) { + for (; glyphs != glyphs_end; glyphs++) { + *valid_glyphs = *glyphs; + valid_glyphs++; + } + } + + return status; +} + +/* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs + * (Xrender limits each element to 252 glyphs, we limit them to 128) + * + * These same conditions need to be mirrored between + * _cairo_xcb_surface_emit_glyphs and _emit_glyph_chunks + */ +#define _start_new_glyph_elt(count, glyph) \ + (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) + +/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have + * enough room for padding */ +typedef struct { + uint8_t len; + uint8_t pad1; + uint16_t pad2; + int16_t deltax; + int16_t deltay; +} x_glyph_elt_t; +#define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4) + +static void +_cairo_xcb_font_destroy (cairo_xcb_font_t *font) +{ + int i; + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xcb_font_glyphset_info_t *info; + + info = &font->glyphset_info[i]; + free (info->pending_free_glyphs); + } + + cairo_list_del (&font->base.link); + cairo_list_del (&font->link); + + _cairo_xcb_connection_destroy (font->connection); + + free (font); +} + +static void +_cairo_xcb_font_fini (cairo_scaled_font_private_t *abstract_private, + cairo_scaled_font_t *scaled_font) +{ + cairo_xcb_font_t *font_private = (cairo_xcb_font_t *)abstract_private; + cairo_xcb_connection_t *connection; + cairo_bool_t have_connection; + cairo_status_t status; + int i; + + connection = font_private->connection; + + status = _cairo_xcb_connection_acquire (connection); + have_connection = status == CAIRO_STATUS_SUCCESS; + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xcb_font_glyphset_info_t *info; + + info = &font_private->glyphset_info[i]; + if (info->glyphset && status == CAIRO_STATUS_SUCCESS) { + _cairo_xcb_connection_render_free_glyph_set (connection, + info->glyphset); + } + } + + if (have_connection) + _cairo_xcb_connection_release (connection); + + _cairo_xcb_font_destroy (font_private); +} + + +static cairo_xcb_font_t * +_cairo_xcb_font_create (cairo_xcb_connection_t *connection, + cairo_scaled_font_t *font) +{ + cairo_xcb_font_t *priv; + int i; + + priv = _cairo_malloc (sizeof (cairo_xcb_font_t)); + if (unlikely (priv == NULL)) + return NULL; + + _cairo_scaled_font_attach_private (font, &priv->base, connection, + _cairo_xcb_font_fini); + + priv->scaled_font = font; + priv->connection = _cairo_xcb_connection_reference (connection); + cairo_list_add (&priv->link, &connection->fonts); + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xcb_font_glyphset_info_t *info = &priv->glyphset_info[i]; + switch (i) { + case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break; + case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break; + case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break; + default: ASSERT_NOT_REACHED; break; + } + info->xrender_format = 0; + info->glyphset = XCB_NONE; + info->pending_free_glyphs = NULL; + } + + return priv; +} + +void +_cairo_xcb_font_close (cairo_xcb_font_t *font) +{ + cairo_scaled_font_t *scaled_font; + + scaled_font = font->scaled_font; + + //scaled_font->surface_private = NULL; + _cairo_scaled_font_reset_cache (scaled_font); + + _cairo_xcb_font_destroy (font); +} + +static void +_cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection, + cairo_xcb_font_glyphset_free_glyphs_t *to_free) +{ + _cairo_xcb_connection_render_free_glyphs (connection, + to_free->glyphset, + to_free->glyph_count, + to_free->glyph_indices); +} + +static int +_cairo_xcb_get_glyphset_index_for_format (cairo_format_t format) +{ + if (format == CAIRO_FORMAT_A8) + return GLYPHSET_INDEX_A8; + if (format == CAIRO_FORMAT_A1) + return GLYPHSET_INDEX_A1; + + assert (format == CAIRO_FORMAT_ARGB32); + return GLYPHSET_INDEX_ARGB32; +} + + + +static inline cairo_xcb_font_t * +_cairo_xcb_font_get (const cairo_xcb_connection_t *c, + cairo_scaled_font_t *font) +{ + return (cairo_xcb_font_t *)_cairo_scaled_font_find_private (font, c); +} + + +static cairo_xcb_font_glyphset_info_t * +_cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_xcb_connection_t *c, + cairo_scaled_font_t *font, + cairo_format_t format) +{ + cairo_xcb_font_t *priv; + cairo_xcb_font_glyphset_info_t *info; + int glyphset_index; + + glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format); + + priv = _cairo_xcb_font_get (c, font); + if (priv == NULL) { + priv = _cairo_xcb_font_create (c, font); + if (priv == NULL) + return NULL; + } + + info = &priv->glyphset_info[glyphset_index]; + if (info->glyphset == XCB_NONE) { + info->glyphset = _cairo_xcb_connection_get_xid (c); + info->xrender_format = c->standard_formats[info->format]; + + _cairo_xcb_connection_render_create_glyph_set (c, + info->glyphset, + info->xrender_format); + } + + return info; +} + +static cairo_bool_t +_cairo_xcb_glyphset_info_has_pending_free_glyph ( + cairo_xcb_font_glyphset_info_t *info, + unsigned long glyph_index) +{ + if (info->pending_free_glyphs != NULL) { + cairo_xcb_font_glyphset_free_glyphs_t *to_free; + int i; + + to_free = info->pending_free_glyphs; + for (i = 0; i < to_free->glyph_count; i++) { + if (to_free->glyph_indices[i] == glyph_index) { + to_free->glyph_count--; + memmove (&to_free->glyph_indices[i], + &to_free->glyph_indices[i+1], + (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); + return TRUE; + } + } + } + + return FALSE; +} + +typedef struct { + cairo_scaled_glyph_private_t base; + + cairo_xcb_font_glyphset_info_t *glyphset; +} cairo_xcb_glyph_private_t; + +static cairo_xcb_font_glyphset_info_t * +_cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (cairo_xcb_connection_t *c, + cairo_scaled_font_t *font, + unsigned long glyph_index, + cairo_image_surface_t *surface) +{ + cairo_xcb_font_t *priv; + int i; + + priv = _cairo_xcb_font_get (c, font); + if (priv == NULL) + return NULL; + + if (surface != NULL) { + i = _cairo_xcb_get_glyphset_index_for_format (surface->format); + + if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( + &priv->glyphset_info[i], + glyph_index)) + { + return &priv->glyphset_info[i]; + } + } else { + for (i = 0; i < NUM_GLYPHSETS; i++) { + if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( + &priv->glyphset_info[i], + glyph_index)) + { + return &priv->glyphset_info[i]; + } + } + } + + return NULL; +} + +static void +_cairo_xcb_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, + cairo_scaled_glyph_t *glyph, + cairo_scaled_font_t *font) +{ + cairo_xcb_glyph_private_t *priv = (cairo_xcb_glyph_private_t *)glyph_private; + + if (! font->finished) { + cairo_xcb_font_glyphset_info_t *info = priv->glyphset; + cairo_xcb_font_glyphset_free_glyphs_t *to_free; + cairo_xcb_font_t *font_private; + + font_private = _cairo_xcb_font_get (glyph_private->key, font); + assert (font_private); + + to_free = info->pending_free_glyphs; + if (to_free != NULL && + to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) + { + _cairo_xcb_render_free_glyphs (font_private->connection, to_free); + to_free = info->pending_free_glyphs = NULL; + } + + if (to_free == NULL) { + to_free = _cairo_malloc (sizeof (cairo_xcb_font_glyphset_free_glyphs_t)); + if (unlikely (to_free == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return; /* XXX cannot propagate failure */ + } + + to_free->glyphset = info->glyphset; + to_free->glyph_count = 0; + info->pending_free_glyphs = to_free; + } + + to_free->glyph_indices[to_free->glyph_count++] = + _cairo_scaled_glyph_index (glyph); + } + + cairo_list_del (&glyph_private->link); + free (glyph_private); +} + + +static cairo_status_t +_cairo_xcb_glyph_attach (cairo_xcb_connection_t *c, + cairo_scaled_glyph_t *glyph, + cairo_xcb_font_glyphset_info_t *info) +{ + cairo_xcb_glyph_private_t *priv; + + priv = _cairo_malloc (sizeof (*priv)); + if (unlikely (priv == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_scaled_glyph_attach_private (glyph, &priv->base, c, + _cairo_xcb_glyph_fini); + priv->glyphset = info; + + glyph->dev_private = info; + glyph->dev_private_key = c; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, + cairo_scaled_font_t *font, + cairo_scaled_glyph_t **scaled_glyph_out) +{ + xcb_render_glyphinfo_t glyph_info; + uint32_t glyph_index; + uint8_t *data; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out; + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_bool_t already_had_glyph_surface; + cairo_xcb_font_glyphset_info_t *info; + + glyph_index = _cairo_scaled_glyph_index (scaled_glyph); + + /* check to see if we have a pending XRenderFreeGlyph for this glyph */ + info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (connection, font, glyph_index, glyph_surface); + if (info != NULL) + return _cairo_xcb_glyph_attach (connection, scaled_glyph, info); + + if (glyph_surface == NULL) { + status = _cairo_scaled_glyph_lookup (font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + scaled_glyph_out); + if (unlikely (status)) + return status; + + scaled_glyph = *scaled_glyph_out; + glyph_surface = scaled_glyph->surface; + already_had_glyph_surface = FALSE; + } else { + already_had_glyph_surface = TRUE; + } + + info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (connection, + font, + glyph_surface->format); + if (unlikely (info == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + +#if 0 + /* If the glyph surface has zero height or width, we create + * a clear 1x1 surface, to avoid various X server bugs. + */ + if (glyph_surface->width == 0 || glyph_surface->height == 0) { + cairo_surface_t *tmp_surface; + + tmp_surface = cairo_image_surface_create (info->format, 1, 1); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; + + tmp_surface->device_transform = glyph_surface->base.device_transform; + tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + + glyph_surface = (cairo_image_surface_t *) tmp_surface; + } +#endif + + /* If the glyph format does not match the font format, then we + * create a temporary surface for the glyph image with the font's + * format. + */ + if (glyph_surface->format != info->format) { + glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface, + info->format); + status = glyph_surface->base.status; + if (unlikely (status)) + goto BAIL; + } + + /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ + glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); + glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); + glyph_info.width = glyph_surface->width; + glyph_info.height = glyph_surface->height; + glyph_info.x_off = scaled_glyph->x_advance; + glyph_info.y_off = scaled_glyph->y_advance; + + data = glyph_surface->data; + + /* flip formats around */ + switch (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) { + case GLYPHSET_INDEX_A1: + /* local bitmaps are always stored with bit == byte */ + if (_cairo_is_little_endian() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) { + int c = glyph_surface->stride * glyph_surface->height; + const uint8_t *d; + uint8_t *new, *n; + + if (c == 0) + break; + + new = _cairo_malloc (c); + if (unlikely (new == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + n = new; + d = data; + do { + uint8_t b = *d++; + b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); + b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); + b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); + *n++ = b; + } while (--c); + data = new; + } + break; + + case GLYPHSET_INDEX_A8: + break; + + case GLYPHSET_INDEX_ARGB32: + if (_cairo_is_little_endian() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { + unsigned int c = glyph_surface->stride * glyph_surface->height / 4; + const uint32_t *d; + uint32_t *new, *n; + + if (c == 0) + break; + + new = _cairo_malloc (4 * c); + if (unlikely (new == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + n = new; + d = (uint32_t *) data; + do { + *n++ = bswap_32 (*d); + d++; + } while (--c); + data = (uint8_t *) new; + } + break; + + default: + ASSERT_NOT_REACHED; + break; + } + /* XXX assume X server wants pixman padding. Xft assumes this as well */ + + _cairo_xcb_connection_render_add_glyphs (connection, + info->glyphset, + 1, &glyph_index, &glyph_info, + glyph_surface->stride * glyph_surface->height, + data); + + if (data != glyph_surface->data) + free (data); + + status = _cairo_xcb_glyph_attach (connection, scaled_glyph, info); + + BAIL: + if (glyph_surface != scaled_glyph->surface) + cairo_surface_destroy (&glyph_surface->base); + + /* If the scaled glyph didn't already have a surface attached + * to it, release the created surface now that we have it + * uploaded to the X server. If the surface has already been + * there (e.g. because image backend requested it), leave it in + * the cache + */ + if (! already_had_glyph_surface) + _cairo_scaled_glyph_set_surface (scaled_glyph, font, NULL); + + return status; +} + +typedef void (*cairo_xcb_render_composite_text_func_t) + (cairo_xcb_connection_t *connection, + uint8_t op, + xcb_render_picture_t src, + xcb_render_picture_t dst, + xcb_render_pictformat_t mask_format, + xcb_render_glyphset_t glyphset, + int16_t src_x, + int16_t src_y, + uint32_t len, + uint8_t *cmd); + + +static cairo_status_t +_emit_glyphs_chunk (cairo_xcb_surface_t *dst, + cairo_operator_t op, + cairo_xcb_picture_t *src, + /* info for this chunk */ + cairo_xcb_glyph_t *glyphs, + int num_glyphs, + int width, + int estimated_req_size, + cairo_xcb_font_glyphset_info_t *info, + xcb_render_pictformat_t mask_format) +{ + cairo_xcb_render_composite_text_func_t composite_text_func; + uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *buf = stack_buf; + x_glyph_elt_t *elt = NULL; /* silence compiler */ + uint32_t len; + int i; + + if (estimated_req_size > ARRAY_LENGTH (stack_buf)) { + buf = _cairo_malloc (estimated_req_size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + len = 0; + for (i = 0; i < num_glyphs; i++) { + if (_start_new_glyph_elt (i, &glyphs[i])) { + if (len & 3) + len += 4 - (len & 3); + + elt = (x_glyph_elt_t *) (buf + len); + elt->len = 0; + elt->deltax = glyphs[i].i.x; + elt->deltay = glyphs[i].i.y; + len += sizeof (x_glyph_elt_t); + } + + switch (width) { + case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break; + case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break; + default: + case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break; + } + len += width; + elt->len++; + } + if (len & 3) + len += 4 - (len & 3); + + switch (width) { + case 1: + composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8; + break; + case 2: + composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16; + break; + default: + case 4: + composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32; + break; + } + composite_text_func (dst->connection, + _render_operator (op), + src->picture, + dst->picture, + mask_format, + info->glyphset, + src->x + glyphs[0].i.x, + src->y + glyphs[0].i.y, + len, buf); + + if (buf != stack_buf) + free (buf); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_composite_glyphs (void *closure, + cairo_xcb_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents, + cairo_clip_t *clip) +{ + composite_glyphs_info_t *info = closure; + cairo_scaled_glyph_t *glyph_cache[64]; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_fixed_t x = 0, y = 0; + cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; + const unsigned int max_request_size = dst->connection->maximum_request_length - 64; + cairo_xcb_picture_t *src; + + unsigned long max_index = 0; + int width = 1; + + unsigned int request_size = 0; + int i; + + if (dst->deferred_clear) { + status = _cairo_xcb_surface_clear (dst); + if (unlikely (status)) + return status; + } + + src = _cairo_xcb_picture_for_pattern (dst, pattern, extents); + if (unlikely (src->base.status)) + return src->base.status; + + memset (glyph_cache, 0, sizeof (glyph_cache)); + + for (i = 0; i < info->num_glyphs; i++) { + cairo_scaled_glyph_t *glyph; + unsigned long glyph_index = info->glyphs[i].index; + int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + int old_width = width; + int this_x, this_y; + + glyph = glyph_cache[cache_index]; + if (glyph == NULL || + _cairo_scaled_glyph_index (glyph) != glyph_index) + { + status = _cairo_scaled_glyph_lookup (info->font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &glyph); + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return status; + } + + /* Send unseen glyphs to the server */ + if (glyph->dev_private_key != dst->connection) { + status = _cairo_xcb_surface_add_glyph (dst->connection, + info->font, + &glyph); + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return status; + } + } + + glyph_cache[cache_index] = glyph; + } + + this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x; + this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y; + + this_glyphset_info = glyph->dev_private; + if (glyphset_info == NULL) + glyphset_info = this_glyphset_info; + + /* Update max glyph index */ + if (glyph_index > max_index) { + max_index = glyph_index; + if (max_index >= 65536) + width = 4; + else if (max_index >= 256) + width = 2; + if (width != old_width) + request_size += (width - old_width) * i; + } + + /* If we will pass the max request size by adding this glyph, + * flush current glyphs. Note that we account for a + * possible element being added below. + * + * Also flush if changing glyphsets, as Xrender limits one mask + * format per request, so we can either break up, or use a + * wide-enough mask format. We do the former. One reason to + * prefer the latter is the fact that Xserver ADDs all glyphs + * to the mask first, and then composes that to final surface, + * though it's not a big deal. + * + * If the glyph has a coordinate which cannot be represented + * as a 16-bit offset from the previous glyph, flush the + * current chunk. The current glyph will be the first one in + * the next chunk, thus its coordinates will be an offset from + * the destination origin. This offset is guaranteed to be + * representable as 16-bit offset in _can_composite_glyphs(). + */ + if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t || + this_x - x > INT16_MAX || this_x - x < INT16_MIN || + this_y - y > INT16_MAX || this_y - y < INT16_MIN || + this_glyphset_info != glyphset_info) + { + status = _emit_glyphs_chunk (dst, op, src, + info->glyphs, i, + old_width, request_size, + glyphset_info, + info->use_mask ? glyphset_info->xrender_format : 0); + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return status; + } + + info->glyphs += i; + info->num_glyphs -= i; + i = 0; + + max_index = info->glyphs[0].index; + width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; + + request_size = 0; + + x = y = 0; + glyphset_info = this_glyphset_info; + } + + /* Convert absolute glyph position to relative-to-current-point + * position */ + info->glyphs[i].i.x = this_x - x; + info->glyphs[i].i.y = this_y - y; + + /* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs. + * + * These same conditions are mirrored in _emit_glyphs_chunk(). + */ + if (_start_new_glyph_elt (i, &info->glyphs[i])) + request_size += _cairo_sz_x_glyph_elt_t; + + /* adjust current-position */ + x = this_x + glyph->x_advance; + y = this_y + glyph->y_advance; + + request_size += width; + } + + if (i) { + status = _emit_glyphs_chunk (dst, op, src, + info->glyphs, i, + width, request_size, + glyphset_info, + info->use_mask ? glyphset_info->xrender_format : 0); + } + + cairo_surface_destroy (&src->base); + + return status; +} + +cairo_int_status_t +_cairo_xcb_render_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_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; + cairo_operator_t op = composite->op; + cairo_pattern_t *source = &composite->source_pattern.base; + cairo_int_status_t status; + + if (unlikely (! _operator_is_supported (surface->connection->flags, op))) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) { + _cairo_scaled_font_freeze_cache (scaled_font); + + status = _can_composite_glyphs (surface, &composite->bounded, + scaled_font, glyphs, &num_glyphs); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + composite_glyphs_info_t info; + unsigned flags = 0; + + info.font = scaled_font; + info.glyphs = (cairo_xcb_glyph_t *) glyphs; + info.num_glyphs = num_glyphs; + info.use_mask = + overlap || + ! composite->is_bounded || + ! _cairo_clip_is_region(composite->clip); + + if (composite->mask.width > composite->unbounded.width || + composite->mask.height > composite->unbounded.height) + { + /* Glyphs are tricky since we do not directly control the + * geometry and their inked extents depend on the + * individual glyph-surface size. We must set a clip region + * so that the X server can trim the glyphs appropriately. + */ + flags |= FORCE_CLIP_REGION; + } + status = _clip_and_composite (surface, op, source, + _composite_glyphs, NULL, + &info, composite, + need_bounded_clip (composite) | + flags); + } + + _cairo_scaled_font_thaw_cache (scaled_font); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + assert (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE); + status = + _cairo_xcb_surface_render_glyphs_via_mask (surface, op, source, + scaled_font, glyphs, num_glyphs, + composite); + } + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface.c b/gfx/cairo/cairo/src/cairo-xcb-surface.c index da07f609fb98..7f601bb71c11 100644 --- a/gfx/cairo/cairo/src/cairo-xcb-surface.c +++ b/gfx/cairo/cairo/src/cairo-xcb-surface.c @@ -43,13 +43,12 @@ #include "cairo-xcb.h" #include "cairo-xcb-private.h" -#if CAIRO_HAS_XCB_DRM_FUNCTIONS -#include -#endif - -#define AllPlanes ((unsigned) -1) -#define CAIRO_ASSUME_PIXMAP 20 -#define XLIB_COORD_MAX 32767 +#include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-list-inline.h" +#include "cairo-surface-backend-private.h" +#include "cairo-compositor-private.h" #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_proto (cairo_xcb_surface_create); @@ -57,10 +56,6 @@ slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); #endif -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS -#include "drm/cairo-drm-private.h" -#endif - /** * SECTION:cairo-xcb * @Title: XCB Surfaces @@ -72,100 +67,16 @@ slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); * * Note that the XCB surface automatically takes advantage of the X render * extension if it is available. - */ + **/ -#if CAIRO_HAS_XCB_SHM_FUNCTIONS -static cairo_status_t -_cairo_xcb_surface_create_similar_shm (cairo_xcb_surface_t *other, - pixman_format_code_t pixman_format, - int width, int height, - cairo_surface_t **out) -{ - size_t size, stride; - cairo_xcb_shm_info_t *shm_info; - cairo_status_t status; - cairo_surface_t *image; - - stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format)); - size = stride * height; - if (size < CAIRO_XCB_SHM_SMALL_IMAGE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_xcb_connection_allocate_shm_info (other->connection, - size, &shm_info); - if (unlikely (status)) - return status; - - image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, - pixman_format, - width, height, - stride); - status = image->status; - if (unlikely (status)) { - _cairo_xcb_shm_info_destroy (shm_info); - return status; - } - - status = _cairo_user_data_array_set_data (&image->user_data, - (const cairo_user_data_key_t *) other->connection, - shm_info, - (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); - if (unlikely (status)) { - cairo_surface_destroy (image); - _cairo_xcb_shm_info_destroy (shm_info); - return status; - } - - *out = image; - return CAIRO_STATUS_SUCCESS; -} -#endif - -cairo_surface_t * -_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other, - cairo_content_t content, - int width, int height) -{ - cairo_surface_t *image = NULL; - pixman_format_code_t pixman_format; - - /* XXX choose pixman_format from connection->image_formats */ - switch (content) { - case CAIRO_CONTENT_ALPHA: - pixman_format = PIXMAN_a8; - break; - case CAIRO_CONTENT_COLOR: - pixman_format = PIXMAN_x8r8g8b8; - break; - default: - ASSERT_NOT_REACHED; - case CAIRO_CONTENT_COLOR_ALPHA: - pixman_format = PIXMAN_a8r8g8b8; - break; - } - -#if CAIRO_HAS_XCB_SHM_FUNCTIONS - if (other->flags & CAIRO_XCB_HAS_SHM) { - cairo_status_t status; - - status = _cairo_xcb_surface_create_similar_shm (other, - pixman_format, - width, height, - &image); - if (_cairo_status_is_error (status)) - return _cairo_surface_create_in_error (status); - } -#endif - - if (image == NULL) { - image = _cairo_image_surface_create_with_pixman_format (NULL, - pixman_format, - width, height, - 0); - } - - return image; -} +/** + * CAIRO_HAS_XCB_SURFACE: + * + * Defined if the xcb surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + * + * Since: 1.12 + **/ cairo_surface_t * _cairo_xcb_surface_create_similar (void *abstract_other, @@ -179,36 +90,23 @@ _cairo_xcb_surface_create_similar (void *abstract_other, xcb_pixmap_t pixmap; cairo_status_t status; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return NULL; + if (unlikely(width > XLIB_COORD_MAX || + height > XLIB_COORD_MAX || + width <= 0 || + height <= 0)) + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); - if (width <= 0 || height <= 0) - return NULL; - -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS - if (other->drm != NULL) { - cairo_surface_t *drm; - - drm = _cairo_drm_surface_create_similar (other->drm, content, width, height); - if (drm != NULL) - return drm; - } -#endif - - if ((other->flags & CAIRO_XCB_HAS_RENDER) == 0) - return _cairo_xcb_surface_create_similar_image (other, content, width, height); + if ((other->connection->flags & CAIRO_XCB_HAS_RENDER) == 0) + return _cairo_xcb_surface_create_similar_image (other, + _cairo_format_from_content (content), + width, height); connection = other->connection; status = _cairo_xcb_connection_acquire (connection); if (unlikely (status)) return _cairo_surface_create_in_error (status); - status =_cairo_xcb_connection_take_socket (connection); - if (unlikely (status)) { - _cairo_xcb_connection_release (connection); - return _cairo_surface_create_in_error (status); - } - if (content == other->base.content) { pixmap = _cairo_xcb_connection_create_pixmap (connection, other->depth, @@ -264,6 +162,42 @@ _cairo_xcb_surface_create_similar (void *abstract_other, return &surface->base; } +cairo_surface_t * +_cairo_xcb_surface_create_similar_image (void *abstract_other, + cairo_format_t format, + int width, + int height) +{ + cairo_xcb_surface_t *other = abstract_other; + cairo_xcb_connection_t *connection = other->connection; + + cairo_xcb_shm_info_t *shm_info; + cairo_image_surface_t *image; + cairo_status_t status; + pixman_format_code_t pixman_format; + + if (unlikely(width > XLIB_COORD_MAX || + height > XLIB_COORD_MAX || + width <= 0 || + height <= 0)) + return NULL; + + pixman_format = _cairo_format_to_pixman_format_code (format); + + status = _cairo_xcb_shm_image_create (connection, pixman_format, + width, height, &image, + &shm_info); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (! image->base.is_clear) { + memset (image->data, 0, image->stride * image->height); + image->base.is_clear = TRUE; + } + + return &image->base; +} + static cairo_status_t _cairo_xcb_surface_finish (void *abstract_surface) { @@ -271,33 +205,22 @@ _cairo_xcb_surface_finish (void *abstract_surface) cairo_status_t status; if (surface->fallback != NULL) { - cairo_surface_finish (surface->fallback); - cairo_surface_destroy (surface->fallback); + cairo_surface_finish (&surface->fallback->base); + cairo_surface_destroy (&surface->fallback->base); } + _cairo_boxes_fini (&surface->fallback_damage); cairo_list_del (&surface->link); -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS - if (surface->drm != NULL) { - cairo_surface_finish (surface->drm); - cairo_surface_destroy (surface->drm); - - xcb_dri2_destroy_drawable (surface->connection->xcb_connection, - surface->drawable); - } -#endif - status = _cairo_xcb_connection_acquire (surface->connection); if (status == CAIRO_STATUS_SUCCESS) { - if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) { - if (surface->picture != XCB_NONE) { - _cairo_xcb_connection_render_free_picture (surface->connection, - surface->picture); - } - - if (surface->owns_pixmap) - _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); + if (surface->picture != XCB_NONE) { + _cairo_xcb_connection_render_free_picture (surface->connection, + surface->picture); } + + if (surface->owns_pixmap) + _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable); _cairo_xcb_connection_release (surface->connection); } @@ -313,144 +236,156 @@ _destroy_image (pixman_image_t *image, void *data) } #if CAIRO_HAS_XCB_SHM_FUNCTIONS -static cairo_int_status_t -_cairo_xcb_surface_create_shm_image (cairo_xcb_surface_t *target, - cairo_image_surface_t **image_out, +static cairo_surface_t * +_cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection, + pixman_format_code_t pixman_format, + int width, int height, + cairo_bool_t might_reuse, cairo_xcb_shm_info_t **shm_info_out) { - cairo_image_surface_t *image; + cairo_surface_t *image; cairo_xcb_shm_info_t *shm_info; - cairo_status_t status; - size_t size, stride; + cairo_int_status_t status; + size_t stride; - if ((target->flags & CAIRO_XCB_HAS_SHM) == 0) - return CAIRO_INT_STATUS_UNSUPPORTED; + *shm_info_out = NULL; - stride = CAIRO_STRIDE_FOR_WIDTH_BPP (target->width, - PIXMAN_FORMAT_BPP (target->pixman_format)); - size = stride * target->height; - if (size < CAIRO_XCB_SHM_SMALL_IMAGE) { - target->flags &= ~CAIRO_XCB_HAS_SHM; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _cairo_xcb_connection_allocate_shm_info (target->screen->connection, - size, &shm_info); - if (unlikely (status)) - return status; - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (shm_info->mem, - target->pixman_format, - target->width, - target->height, - stride); - status = image->base.status; + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, + PIXMAN_FORMAT_BPP (pixman_format)); + status = _cairo_xcb_connection_allocate_shm_info (connection, + stride * height, + might_reuse, + &shm_info); if (unlikely (status)) { - _cairo_xcb_shm_info_destroy (shm_info); - return status; + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return NULL; + + return _cairo_surface_create_in_error (status); } - status = _cairo_user_data_array_set_data (&image->base.user_data, - (const cairo_user_data_key_t *) target->connection, + image = _cairo_image_surface_create_with_pixman_format (shm_info->mem, + pixman_format, + width, height, + stride); + if (unlikely (image->status)) { + _cairo_xcb_shm_info_destroy (shm_info); + return image; + } + + status = _cairo_user_data_array_set_data (&image->user_data, + (const cairo_user_data_key_t *) connection, shm_info, (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy); if (unlikely (status)) { - cairo_surface_destroy (&image->base); + cairo_surface_destroy (image); _cairo_xcb_shm_info_destroy (shm_info); - return status; + return _cairo_surface_create_in_error (status); } - *image_out = image; *shm_info_out = shm_info; - return CAIRO_STATUS_SUCCESS; + return image; } #endif -static cairo_status_t -_get_shm_image (cairo_xcb_surface_t *surface, - cairo_image_surface_t **image_out) +static cairo_surface_t * +_get_shm_image (cairo_xcb_surface_t *surface, + int x, int y, + int width, int height) { #if CAIRO_HAS_XCB_SHM_FUNCTIONS - cairo_image_surface_t *image; cairo_xcb_shm_info_t *shm_info; + cairo_surface_t *image; cairo_status_t status; - status = _cairo_xcb_surface_create_shm_image (surface, &image, &shm_info); - if (unlikely (status)) - return status; + if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0) + return NULL; - if (! surface->base.is_clear) { - status = _cairo_xcb_connection_shm_get_image (surface->connection, - surface->drawable, - 0, 0, - surface->width, - surface->height, - shm_info->shm, - shm_info->offset); - if (unlikely (status)) - return status; - } else { - memset (image->data, 0, image->stride * image->height); + image = _cairo_xcb_surface_create_shm_image (surface->connection, + surface->pixman_format, + width, height, + TRUE, + &shm_info); + if (unlikely (image == NULL || image->status)) + goto done; + + status = _cairo_xcb_connection_shm_get_image (surface->connection, + surface->drawable, + x, y, + width, height, + shm_info->shm, + shm_info->offset); + if (unlikely (status)) { + cairo_surface_destroy (image); + image = _cairo_surface_create_in_error (status); } - *image_out = image; - return CAIRO_STATUS_SUCCESS; +done: + return image; #else - return CAIRO_INT_STATUS_UNSUPPORTED; + return NULL; #endif } -static cairo_status_t +static cairo_surface_t * _get_image (cairo_xcb_surface_t *surface, cairo_bool_t use_shm, - cairo_image_surface_t **image_out) + int x, int y, + int width, int height) { - cairo_image_surface_t *image; + cairo_surface_t *image; cairo_xcb_connection_t *connection; xcb_get_image_reply_t *reply; - cairo_status_t status; + cairo_int_status_t status; - if (surface->base.is_clear || surface->deferred_clear) { - image = (cairo_image_surface_t *) + assert (surface->fallback == NULL); + assert (x >= 0); + assert (y >= 0); + assert (x + width <= surface->width); + assert (y + height <= surface->height); + + if (surface->deferred_clear) { + image = _cairo_image_surface_create_with_pixman_format (NULL, surface->pixman_format, - surface->width, - surface->height, + width, height, 0); - *image_out = image; - return image->base.status; + if (surface->deferred_clear_color.alpha_short > 0x00ff) { + cairo_solid_pattern_t solid; + + _cairo_pattern_init_solid (&solid, &surface->deferred_clear_color); + status = _cairo_surface_paint (image, + CAIRO_OPERATOR_SOURCE, + &solid.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (image); + image = _cairo_surface_create_in_error (status); + } + } + return image; } connection = surface->connection; status = _cairo_xcb_connection_acquire (connection); if (unlikely (status)) - return status; - - status = _cairo_xcb_connection_take_socket (connection); - if (unlikely (status)) - goto FAIL; + return _cairo_surface_create_in_error (status); if (use_shm) { - status = _get_shm_image (surface, image_out); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FAIL; + image = _get_shm_image (surface, x, y, width, height); + if (image) { + if (image->status == CAIRO_STATUS_SUCCESS) { + _cairo_xcb_connection_release (connection); + return image; + } + cairo_surface_destroy (image); + } } - if (surface->use_pixmap == 0) { - status = _cairo_xcb_connection_get_image (connection, - surface->drawable, - 0, 0, - surface->width, - surface->height, - &reply); - if (unlikely (status)) - goto FAIL; - } else { - surface->use_pixmap--; - reply = NULL; - } + reply =_cairo_xcb_connection_get_image (connection, + surface->drawable, + x, y, + width, height); if (reply == NULL && ! surface->owns_pixmap) { /* xcb_get_image_t from a window is dangerous because it can @@ -458,6 +393,10 @@ _get_image (cairo_xcb_surface_t *surface, * outside the screen. We could check for errors and * retry, but to keep things simple, we just create a * temporary pixmap + * + * If we hit this fallback too often, we should remember so and + * skip the round-trip from the above GetImage request, + * similar to what cairo-xlib does. */ xcb_pixmap_t pixmap; xcb_gcontext_t gc; @@ -468,30 +407,23 @@ _get_image (cairo_xcb_surface_t *surface, pixmap = _cairo_xcb_connection_create_pixmap (connection, surface->depth, surface->drawable, - surface->width, - surface->height); + width, height); /* XXX IncludeInferiors? */ _cairo_xcb_connection_copy_area (connection, surface->drawable, pixmap, gc, + x, y, 0, 0, - 0, 0, - surface->width, - surface->height); + width, height); _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); - status = _cairo_xcb_connection_get_image (connection, - pixmap, - 0, 0, - surface->width, - surface->height, - &reply); + reply = _cairo_xcb_connection_get_image (connection, + pixmap, + 0, 0, + width, height); _cairo_xcb_connection_free_pixmap (connection, pixmap); - - if (unlikely (status)) - goto FAIL; } if (unlikely (reply == NULL)) { @@ -503,35 +435,43 @@ _get_image (cairo_xcb_surface_t *surface, /* XXX format conversion */ assert (reply->depth == surface->depth); - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format + image = _cairo_image_surface_create_with_pixman_format (xcb_get_image_data (reply), surface->pixman_format, - surface->width, - surface->height, - CAIRO_STRIDE_FOR_WIDTH_BPP (surface->width, + width, height, + CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (surface->pixman_format))); - status = image->base.status; + status = image->status; if (unlikely (status)) { free (reply); goto FAIL; } - assert (xcb_get_image_data_length (reply) == image->height * image->stride); - - pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply); + /* XXX */ + pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply); _cairo_xcb_connection_release (connection); - /* synchronisation point */ - surface->marked_dirty = FALSE; - - *image_out = image; - return CAIRO_STATUS_SUCCESS; + return image; FAIL: _cairo_xcb_connection_release (connection); - return status; + return _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_xcb_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xcb_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 @@ -540,35 +480,28 @@ _cairo_xcb_surface_acquire_source_image (void *abstract_surface, void **image_extra) { cairo_xcb_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_acquire_source_image (surface->drm, - image_out, image_extra); - } + cairo_surface_t *image; if (surface->fallback != NULL) { - image = (cairo_image_surface_t *) cairo_surface_reference (surface->fallback); + image = cairo_surface_reference (&surface->fallback->base); goto DONE; } - image = (cairo_image_surface_t *) - _cairo_surface_has_snapshot (&surface->base, - &_cairo_image_surface_backend); + image = _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend); if (image != NULL) { - image = (cairo_image_surface_t *) cairo_surface_reference (&image->base); + image = cairo_surface_reference (image); goto DONE; } - status = _get_image (surface, FALSE, &image); - if (unlikely (status)) - return status; + image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height); + if (unlikely (image->status)) + return image->status; - cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); + _cairo_surface_attach_snapshot (&surface->base, image, NULL); DONE: - *image_out = image; + *image_out = (cairo_image_surface_t *) image; *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } @@ -578,17 +511,10 @@ _cairo_xcb_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) { - cairo_xcb_surface_t *surface = abstract_surface; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_release_source_image (surface->drm, - image, image_extra); - } - cairo_surface_destroy (&image->base); } -static cairo_bool_t +cairo_bool_t _cairo_xcb_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *extents) { @@ -604,9 +530,9 @@ static void _cairo_xcb_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) { - /* XXX copy from xlib */ - _cairo_font_options_init_default (options); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); + cairo_xcb_surface_t *surface = abstract_surface; + + *options = *_cairo_xcb_screen_get_font_options (surface->screen); } static cairo_status_t @@ -622,17 +548,17 @@ _put_shm_image (cairo_xcb_surface_t *surface, if (shm_info == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; - shm_info->seqno = - _cairo_xcb_connection_shm_put_image (surface->connection, - surface->drawable, - gc, - surface->width, surface->height, - 0, 0, - image->width, image->height, - 0, 0, - image->depth, - shm_info->shm, - shm_info->offset); + _cairo_xcb_connection_shm_put_image (surface->connection, + surface->drawable, + gc, + surface->width, surface->height, + 0, 0, + image->width, image->height, + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0, + image->depth, + shm_info->shm, + shm_info->offset); return CAIRO_STATUS_SUCCESS; #else @@ -644,7 +570,7 @@ static cairo_status_t _put_image (cairo_xcb_surface_t *surface, cairo_image_surface_t *image) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; /* XXX track damaged region? */ @@ -652,17 +578,9 @@ _put_image (cairo_xcb_surface_t *surface, if (unlikely (status)) return status; - status = _cairo_xcb_connection_take_socket (surface->connection); - if (unlikely (status)) { - _cairo_xcb_connection_release (surface->connection); - return status; - } - if (image->pixman_format == surface->pixman_format) { xcb_gcontext_t gc; - assert (image->width == surface->width); - assert (image->height == surface->height); assert (image->depth == surface->depth); assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); @@ -675,7 +593,8 @@ _put_image (cairo_xcb_surface_t *surface, _cairo_xcb_connection_put_image (surface->connection, surface->drawable, gc, image->width, image->height, - 0, 0, + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0, image->depth, image->stride, image->data); @@ -691,14 +610,116 @@ _put_image (cairo_xcb_surface_t *surface, return status; } +static cairo_int_status_t +_put_shm_image_boxes (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image, + xcb_gcontext_t gc, + cairo_boxes_t *boxes) +{ +#if CAIRO_HAS_XCB_SHM_FUNCTIONS + cairo_xcb_shm_info_t *shm_info; + + shm_info = _cairo_user_data_array_get_data (&image->base.user_data, + (const cairo_user_data_key_t *) surface->connection); + if (shm_info != NULL) { + struct _cairo_boxes_chunk *chunk; + + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + int i; + + 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 width = _cairo_fixed_integer_part (b->p2.x - b->p1.x); + int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y); + + _cairo_xcb_connection_shm_put_image (surface->connection, + surface->drawable, + gc, + surface->width, surface->height, + x, y, + width, height, + x, y, + image->depth, + shm_info->shm, + shm_info->offset); + } + } + return CAIRO_INT_STATUS_SUCCESS; + } +#endif + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + static cairo_status_t -_cairo_xcb_surface_flush (void *abstract_surface) +_put_image_boxes (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image, + cairo_boxes_t *boxes) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + xcb_gcontext_t gc; + + if (boxes->num_boxes == 0) + return CAIRO_STATUS_SUCCESS; + + /* XXX track damaged region? */ + + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + assert (image->pixman_format == surface->pixman_format); + assert (image->depth == surface->depth); + assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format))); + + gc = _cairo_xcb_screen_get_gc (surface->screen, + surface->drawable, + surface->depth); + + status = _put_shm_image_boxes (surface, image, gc, boxes); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + struct _cairo_boxes_chunk *chunk; + + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + int i; + + 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 width = _cairo_fixed_integer_part (b->p2.x - b->p1.x); + int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y); + _cairo_xcb_connection_put_subimage (surface->connection, + surface->drawable, gc, + x, y, + width, height, + PIXMAN_FORMAT_BPP (image->pixman_format) / 8, + image->stride, + x, y, + image->depth, + image->data); + + } + } + status = CAIRO_STATUS_SUCCESS; + } + + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); + _cairo_xcb_connection_release (surface->connection); + return status; +} + +static cairo_status_t +_cairo_xcb_surface_flush (void *abstract_surface, + unsigned flags) { cairo_xcb_surface_t *surface = abstract_surface; cairo_status_t status; - if (surface->drm != NULL && ! surface->marked_dirty) - return surface->drm->backend->flush (surface->drm); + if (flags) + return CAIRO_STATUS_SUCCESS; if (likely (surface->fallback == NULL)) { status = CAIRO_STATUS_SUCCESS; @@ -709,75 +730,241 @@ _cairo_xcb_surface_flush (void *abstract_surface) } status = surface->base.status; - if (status == CAIRO_STATUS_SUCCESS && ! surface->base.finished) { - status = cairo_surface_status (surface->fallback); + if (status == CAIRO_STATUS_SUCCESS && + (! surface->base._finishing || ! surface->owns_pixmap)) { + status = cairo_surface_status (&surface->fallback->base); - if (status == CAIRO_STATUS_SUCCESS) { - status = _put_image (surface, - (cairo_image_surface_t *) surface->fallback); - } + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_bentley_ottmann_tessellate_boxes (&surface->fallback_damage, + CAIRO_FILL_RULE_WINDING, + &surface->fallback_damage); - if (status == CAIRO_STATUS_SUCCESS) { - cairo_surface_attach_snapshot (&surface->base, - surface->fallback, + if (status == CAIRO_STATUS_SUCCESS) + status = _put_image_boxes (surface, + surface->fallback, + &surface->fallback_damage); + + if (status == CAIRO_STATUS_SUCCESS && ! surface->base._finishing) { + _cairo_surface_attach_snapshot (&surface->base, + &surface->fallback->base, cairo_surface_finish); } } - cairo_surface_destroy (surface->fallback); + _cairo_boxes_clear (&surface->fallback_damage); + cairo_surface_destroy (&surface->fallback->base); surface->fallback = NULL; return status; } -static cairo_status_t -_cairo_xcb_surface_mark_dirty (void *abstract_surface, - int x, int y, - int width, int height) +static cairo_image_surface_t * +_cairo_xcb_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) { cairo_xcb_surface_t *surface = abstract_surface; - surface->marked_dirty = TRUE; - return CAIRO_STATUS_SUCCESS; + cairo_surface_t *image; + cairo_status_t status; + + if (surface->fallback) + return _cairo_surface_map_to_image (&surface->fallback->base, extents); + + image = _get_image (surface, TRUE, + extents->x, extents->y, + extents->width, extents->height); + status = cairo_surface_status (image); + if (unlikely (status)) { + cairo_surface_destroy(image); + return _cairo_image_surface_create_in_error (status); + } + + /* Do we have a deferred clear and this image surface does NOT cover the + * whole xcb surface? Have to apply the clear in that case, else + * uploading the image will handle the problem for us. + */ + if (surface->deferred_clear && + ! (extents->width == surface->width && + extents->height == surface->height)) { + status = _cairo_xcb_surface_clear (surface); + if (unlikely (status)) { + cairo_surface_destroy(image); + return _cairo_image_surface_create_in_error (status); + } + } + surface->deferred_clear = FALSE; + + cairo_surface_set_device_offset (image, -extents->x, -extents->y); + return (cairo_image_surface_t *) image; +} + +static cairo_int_status_t +_cairo_xcb_surface_unmap (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + if (surface->fallback) + return _cairo_surface_unmap_image (&surface->fallback->base, image); + + status = _put_image (abstract_surface, image); + + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return status; } static cairo_surface_t * -_cairo_xcb_surface_map_to_image (cairo_xcb_surface_t *surface) +_cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface, + cairo_composite_rectangles_t *composite) { - cairo_status_t status; cairo_image_surface_t *image; + cairo_status_t status; - status = _get_image (surface, TRUE, &image); + status = _cairo_composite_rectangles_add_to_damage (composite, + &surface->fallback_damage); if (unlikely (status)) - return _cairo_surface_create_in_error (status); + return _cairo_surface_create_in_error (status); - return &image->base; + if (surface->fallback) + return &surface->fallback->base; + + image = (cairo_image_surface_t *) + _get_image (surface, TRUE, 0, 0, surface->width, surface->height); + + if (image->base.status != CAIRO_STATUS_SUCCESS) + return &image->base; + + /* If there was a deferred clear, _get_image applied it */ + surface->deferred_clear = FALSE; + + surface->fallback = image; + + return &surface->fallback->base; +} + +static cairo_int_status_t +_cairo_xcb_fallback_compositor_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; + cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); + + return _cairo_surface_paint (fallback, extents->op, + &extents->source_pattern.base, + extents->clip); +} + +static cairo_int_status_t +_cairo_xcb_fallback_compositor_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; + cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); + + return _cairo_surface_mask (fallback, extents->op, + &extents->source_pattern.base, + &extents->mask_pattern.base, + extents->clip); +} + +static cairo_int_status_t +_cairo_xcb_fallback_compositor_stroke (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + 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_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; + cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); + + return _cairo_surface_stroke (fallback, extents->op, + &extents->source_pattern.base, + path, style, ctm, ctm_inverse, + tolerance, antialias, + extents->clip); +} + +static cairo_int_status_t +_cairo_xcb_fallback_compositor_fill (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; + cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); + + return _cairo_surface_fill (fallback, extents->op, + &extents->source_pattern.base, + path, fill_rule, tolerance, + antialias, extents->clip); +} + +static cairo_int_status_t +_cairo_xcb_fallback_compositor_glyphs (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; + cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); + + return _cairo_surface_show_text_glyphs (fallback, extents->op, + &extents->source_pattern.base, + NULL, 0, glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, extents->clip); +} + +static const cairo_compositor_t _cairo_xcb_fallback_compositor = { + &__cairo_no_compositor, + + _cairo_xcb_fallback_compositor_paint, + _cairo_xcb_fallback_compositor_mask, + _cairo_xcb_fallback_compositor_stroke, + _cairo_xcb_fallback_compositor_fill, + _cairo_xcb_fallback_compositor_glyphs, +}; + +static const cairo_compositor_t _cairo_xcb_render_compositor = { + &_cairo_xcb_fallback_compositor, + + _cairo_xcb_render_compositor_paint, + _cairo_xcb_render_compositor_mask, + _cairo_xcb_render_compositor_stroke, + _cairo_xcb_render_compositor_fill, + _cairo_xcb_render_compositor_glyphs, +}; + +static inline const cairo_compositor_t * +get_compositor (cairo_surface_t **s) +{ + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t * )*s; + if (surface->fallback) { + *s = &surface->fallback->base; + return ((cairo_image_surface_t *) *s)->compositor; + } + + return &_cairo_xcb_render_compositor; } static cairo_int_status_t _cairo_xcb_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) - return _cairo_surface_paint (surface->drm, op, source, clip); - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_paint (surface, op, source, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_paint (surface->fallback, op, source, clip); + cairo_surface_t *surface = abstract_surface; + const cairo_compositor_t *compositor = get_compositor (&surface); + return _cairo_compositor_paint (compositor, surface, op, source, clip); } static cairo_int_status_t @@ -785,131 +972,47 @@ _cairo_xcb_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) - return _cairo_surface_mask (surface->drm, op, source, mask, clip); - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_mask (surface, - op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_mask (surface, - op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_mask (surface->fallback, - op, source, mask, - clip); + cairo_surface_t *surface = abstract_surface; + const cairo_compositor_t *compositor = get_compositor (&surface); + return _cairo_compositor_mask (compositor, surface, op, source, mask, clip); } static cairo_int_status_t _cairo_xcb_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_stroke (surface->drm, - op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - } - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_stroke (surface, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_stroke (surface, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_stroke (surface->fallback, - op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); + cairo_surface_t *surface = abstract_surface; + const cairo_compositor_t *compositor = get_compositor (&surface); + return _cairo_compositor_stroke (compositor, surface, op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, clip); } static cairo_int_status_t _cairo_xcb_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_fill (surface->drm, - op, source, - path, fill_rule, - tolerance, antialias, - clip); - } - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_fill (surface->fallback, - op, source, - path, fill_rule, - tolerance, antialias, - clip); + cairo_surface_t *surface = abstract_surface; + const cairo_compositor_t *compositor = get_compositor (&surface); + return _cairo_compositor_fill (compositor, surface, op, + source, path, fill_rule, + tolerance, antialias, clip); } static cairo_int_status_t @@ -919,152 +1022,48 @@ _cairo_xcb_surface_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *num_remaining) + const cairo_clip_t *clip) { - cairo_xcb_surface_t *surface = abstract_surface; - cairo_status_t status; - - *num_remaining = 0; - - if (surface->drm != NULL && ! surface->marked_dirty) { - return _cairo_surface_show_text_glyphs (surface->drm, - op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, - clip); - } - - if (surface->fallback == NULL) { - status = _cairo_xcb_surface_cairo_glyphs (surface, - op, source, - scaled_font, glyphs, num_glyphs, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_xcb_surface_render_glyphs (surface, - op, source, - scaled_font, glyphs, num_glyphs, - clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - surface->fallback = _cairo_xcb_surface_map_to_image (surface); - } - - return _cairo_surface_show_text_glyphs (surface->fallback, - op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, - clip); + cairo_surface_t *surface = abstract_surface; + const cairo_compositor_t *compositor = get_compositor (&surface); + return _cairo_compositor_glyphs (compositor, surface, op, + source, glyphs, num_glyphs, + scaled_font, clip); } const cairo_surface_backend_t _cairo_xcb_surface_backend = { CAIRO_SURFACE_TYPE_XCB, + _cairo_xcb_surface_finish, + _cairo_default_context_create, _cairo_xcb_surface_create_similar, - _cairo_xcb_surface_finish, + _cairo_xcb_surface_create_similar_image, + _cairo_xcb_surface_map_to_image, + _cairo_xcb_surface_unmap, + + _cairo_xcb_surface_source, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, - NULL, NULL, NULL, /* dest acquire/release/clone */ + NULL, /* snapshot */ - NULL, /* composite */ - NULL, /* fill */ - NULL, /* trapezoids */ - NULL, /* span */ - NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ + _cairo_xcb_surface_get_extents, - NULL, /* old-glyphs */ _cairo_xcb_surface_get_font_options, _cairo_xcb_surface_flush, - _cairo_xcb_surface_mark_dirty, - _cairo_xcb_surface_scaled_font_fini, - _cairo_xcb_surface_scaled_glyph_fini, + NULL, _cairo_xcb_surface_paint, _cairo_xcb_surface_mask, _cairo_xcb_surface_stroke, _cairo_xcb_surface_fill, + NULL, /* fill-stroke */ _cairo_xcb_surface_glyphs, }; -#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS -static cairo_surface_t * -_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, - cairo_xcb_screen_t *screen, - xcb_drawable_t drawable, - pixman_format_code_t pixman_format, - int width, int height) -{ - uint32_t attachments[] = { XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT }; - xcb_dri2_get_buffers_reply_t *buffers; - xcb_dri2_dri2_buffer_t *buffer; - cairo_surface_t *surface; - - if (! _cairo_drm_size_is_valid (screen->device, width, height)) - return NULL; - - xcb_dri2_create_drawable (connection->xcb_connection, - drawable); - - buffers = xcb_dri2_get_buffers_reply (connection->xcb_connection, - xcb_dri2_get_buffers (connection->xcb_connection, - drawable, 1, - ARRAY_LENGTH (attachments), - attachments), - 0); - if (buffers == NULL) { - xcb_dri2_destroy_drawable (connection->xcb_connection, - drawable); - return NULL; - } - - /* If the drawable is a window, we expect to receive an extra fake front, - * which would involve copying on each flush - contrary to the user - * expectations. But that is likely to be preferable to mixing drm/xcb - * operations. - */ - buffer = xcb_dri2_get_buffers_buffers (buffers); - if (buffers->count == 1 && buffer[0].attachment == XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT) { - assert (buffer[0].cpp == PIXMAN_FORMAT_BPP (pixman_format) / 8); - surface = cairo_drm_surface_create_for_name (screen->device, - buffer[0].name, - _cairo_format_from_pixman_format (pixman_format), - width, height, - buffer[0].pitch); - } else { - xcb_dri2_destroy_drawable (connection->xcb_connection, - drawable); - surface = NULL; - } - free (buffers); - - return surface; -} - -#else - -static cairo_surface_t * -_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection, - cairo_xcb_screen_t *screen, - xcb_drawable_t drawable, - pixman_format_code_t pixman_format, - int width, int height) -{ - return NULL; -} - -#endif - cairo_surface_t * _cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, xcb_drawable_t drawable, @@ -1076,47 +1075,41 @@ _cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen, { cairo_xcb_surface_t *surface; - surface = malloc (sizeof (cairo_xcb_surface_t)); + surface = _cairo_malloc (sizeof (cairo_xcb_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &_cairo_xcb_surface_backend, &screen->connection->device, - _cairo_content_from_pixman_format (pixman_format)); + _cairo_content_from_pixman_format (pixman_format), + FALSE); /* is_vector */ surface->connection = _cairo_xcb_connection_reference (screen->connection); surface->screen = screen; cairo_list_add (&surface->link, &screen->surfaces); - surface->fallback = NULL; - surface->drawable = drawable; surface->owns_pixmap = owns_pixmap; - surface->use_pixmap = 0; surface->deferred_clear = FALSE; + surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT; surface->width = width; surface->height = height; surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format); surface->picture = XCB_NONE; + if (screen->connection->force_precision != -1) + surface->precision = screen->connection->force_precision; + else + surface->precision = XCB_RENDER_POLY_MODE_IMPRECISE; surface->pixman_format = pixman_format; surface->xrender_format = xrender_format; - surface->flags = screen->connection->flags; - - surface->marked_dirty = FALSE; - surface->drm = NULL; - if (screen->device != NULL) { - surface->drm = _xcb_drm_create_surface_for_drawable (surface->connection, - surface->screen, - drawable, - pixman_format, - width, height); - } + surface->fallback = NULL; + _cairo_boxes_init (&surface->fallback_damage); return &surface->base; } @@ -1152,8 +1145,41 @@ _cairo_xcb_screen_from_visual (xcb_connection_t *connection, return NULL; } +/** + * cairo_xcb_surface_create: + * @connection: an XCB connection + * @drawable: an XCB drawable + * @visual: the visual to use for drawing to @drawable. The depth + * of the visual must match the depth of the drawable. + * Currently, only TrueColor visuals are fully supported. + * @width: the current width of @drawable + * @height: the current height of @drawable + * + * Creates an XCB surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided visual. + * + * Note: If @drawable is a Window, then the function + * cairo_xcb_surface_set_size() must be called whenever the size of the + * window changes. + * + * When @drawable is a Window containing child windows then drawing to + * the created surface will be clipped by those child windows. When + * the created surface is used as a source, the contents of the + * children will be included. + * + * 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: 1.12 + **/ cairo_surface_t * -cairo_xcb_surface_create (xcb_connection_t *xcb_connection, +cairo_xcb_surface_create (xcb_connection_t *connection, xcb_drawable_t drawable, xcb_visualtype_t *visual, int width, @@ -1166,20 +1192,25 @@ cairo_xcb_surface_create (xcb_connection_t *xcb_connection, xcb_render_pictformat_t xrender_format; int depth; - if (xcb_connection_has_error (xcb_connection)) - return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + if (xcb_connection_has_error (connection)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (unlikely (width <= 0 || height <= 0)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - xcb_screen = _cairo_xcb_screen_from_visual (xcb_connection, visual, &depth); + xcb_screen = _cairo_xcb_screen_from_visual (connection, visual, &depth); if (unlikely (xcb_screen == NULL)) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_VISUAL); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); image_masks.alpha_mask = 0; image_masks.red_mask = visual->red_mask; image_masks.green_mask = visual->green_mask; image_masks.blue_mask = visual->blue_mask; + if (depth == 32) /* XXX visuals have no alpha! */ + image_masks.alpha_mask = + 0xffffffff & ~(visual->red_mask | visual->green_mask | visual->blue_mask); if (depth > 16) image_masks.bpp = 32; else if (depth > 8) @@ -1192,7 +1223,7 @@ cairo_xcb_surface_create (xcb_connection_t *xcb_connection, if (! _pixman_format_from_masks (&image_masks, &pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); + screen = _cairo_xcb_screen_get (connection, xcb_screen); if (unlikely (screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -1209,28 +1240,51 @@ cairo_xcb_surface_create (xcb_connection_t *xcb_connection, slim_hidden_def (cairo_xcb_surface_create); #endif +/** + * cairo_xcb_surface_create_for_bitmap: + * @connection: an XCB connection + * @screen: the XCB screen associated with @bitmap + * @bitmap: an XCB drawable (a Pixmap with depth 1) + * @width: the current width of @bitmap + * @height: the current height of @bitmap + * + * Creates an XCB surface that draws to the given bitmap. + * This will be drawn to as a %CAIRO_FORMAT_A1 object. + * + * Return value: 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: 1.12 + **/ cairo_surface_t * -cairo_xcb_surface_create_for_bitmap (xcb_connection_t *xcb_connection, - xcb_screen_t *xcb_screen, +cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, + xcb_screen_t *screen, xcb_pixmap_t bitmap, int width, int height) { - cairo_xcb_screen_t *screen; + cairo_xcb_screen_t *cairo_xcb_screen; - if (xcb_connection_has_error (xcb_connection)) - return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + if (xcb_connection_has_error (connection)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (unlikely (width <= 0 || height <= 0)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); - screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); - if (unlikely (screen == NULL)) + cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen); + if (unlikely (cairo_xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - return _cairo_xcb_surface_create_internal (screen, bitmap, FALSE, + return _cairo_xcb_surface_create_internal (cairo_xcb_screen, bitmap, FALSE, PIXMAN_a1, - screen->connection->standard_formats[CAIRO_FORMAT_A1], + cairo_xcb_screen->connection->standard_formats[CAIRO_FORMAT_A1], width, height); } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS @@ -1255,25 +1309,40 @@ slim_hidden_def (cairo_xcb_surface_create_for_bitmap); * cairo_xcb_surface_set_size() must be called whenever the size of the * window changes. * - * Return value: the newly created surface. + * When @drawable is a Window containing child windows then drawing to + * the created surface will be clipped by those child windows. When + * the created surface is used as a source, the contents of the + * children will be included. + * + * 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: 1.12 **/ cairo_surface_t * -cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connection, - xcb_screen_t *xcb_screen, +cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection, + xcb_screen_t *screen, xcb_drawable_t drawable, xcb_render_pictforminfo_t *format, int width, int height) { - cairo_xcb_screen_t *screen; + cairo_xcb_screen_t *cairo_xcb_screen; cairo_format_masks_t image_masks; pixman_format_code_t pixman_format; - if (xcb_connection_has_error (xcb_connection)) - return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR); + if (xcb_connection_has_error (connection)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (unlikely (width <= 0 || height <= 0)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); image_masks.alpha_mask = (unsigned long) format->direct.alpha_mask << format->direct.alpha_shift; @@ -1299,11 +1368,11 @@ cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connecti if (! _pixman_format_from_masks (&image_masks, &pixman_format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); - screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen); - if (unlikely (screen == NULL)) + cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen); + if (unlikely (cairo_xcb_screen == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - return _cairo_xcb_surface_create_internal (screen, + return _cairo_xcb_surface_create_internal (cairo_xcb_screen, drawable, FALSE, pixman_format, @@ -1314,6 +1383,19 @@ cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connecti slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); #endif +/* This does the necessary fixup when a surface's drawable or size changed. */ +static void +_drawable_changed (cairo_xcb_surface_t *surface) +{ + _cairo_surface_set_error (&surface->base, + _cairo_surface_begin_modification (&surface->base)); + _cairo_boxes_clear (&surface->fallback_damage); + cairo_surface_destroy (&surface->fallback->base); + + surface->deferred_clear = FALSE; + surface->fallback = NULL; +} + /** * cairo_xcb_surface_set_size: * @surface: a #cairo_surface_t for the XCB backend @@ -1329,6 +1411,11 @@ slim_hidden_def (cairo_xcb_surface_create_with_xrender_format); * * A pixmap can never change size, so it is never necessary to call * this function on a surface created for a pixmap. + * + * If cairo_surface_flush() wasn't called, some pending operations + * might be discarded. + * + * Since: 1.12 **/ void cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, @@ -1336,33 +1423,109 @@ cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, int height) { cairo_xcb_surface_t *surface; - cairo_status_t status_ignored; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } - if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + if ( !_cairo_surface_is_xcb(abstract_surface)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } surface = (cairo_xcb_surface_t *) abstract_surface; + + _drawable_changed(surface); surface->width = width; surface->height = height; } #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_def (cairo_xcb_surface_set_size); #endif + +/** + * cairo_xcb_surface_set_drawable: + * @surface: a #cairo_surface_t for the XCB backend + * @drawable: the new drawable of the surface + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new drawable and size of the XCB drawable underlying the + * surface. + * + * If cairo_surface_flush() wasn't called, some pending operations + * might be discarded. + * + * Since: 1.12 + **/ +void +cairo_xcb_surface_set_drawable (cairo_surface_t *abstract_surface, + xcb_drawable_t drawable, + int width, + int height) +{ + cairo_xcb_surface_t *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_xcb(abstract_surface)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; + } + + surface = (cairo_xcb_surface_t *) abstract_surface; + + /* XXX: and what about this case? */ + if (surface->owns_pixmap) + return; + + _drawable_changed (surface); + + if (surface->drawable != drawable) { + cairo_status_t status; + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return; + + if (surface->picture != XCB_NONE) { + _cairo_xcb_connection_render_free_picture (surface->connection, + surface->picture); + surface->picture = XCB_NONE; + } + + _cairo_xcb_connection_release (surface->connection); + + surface->drawable = drawable; + } + surface->width = width; + surface->height = height; +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_set_drawable); +#endif diff --git a/gfx/cairo/cairo/src/cairo-xcb.h b/gfx/cairo/cairo/src/cairo-xcb.h index 3f64dcbdc5c9..e321d8482c2f 100644 --- a/gfx/cairo/cairo/src/cairo-xcb.h +++ b/gfx/cairo/cairo/src/cairo-xcb.h @@ -75,6 +75,15 @@ cairo_xcb_surface_set_size (cairo_surface_t *surface, int width, int height); +cairo_public void +cairo_xcb_surface_set_drawable (cairo_surface_t *surface, + xcb_drawable_t drawable, + int width, + int height); + +cairo_public xcb_connection_t * +cairo_xcb_device_get_connection (cairo_device_t *device); + /* debug interface */ cairo_public void @@ -87,6 +96,17 @@ cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device, int major_version, int minor_version); +/* + * @precision: -1 implies automatically choose based on antialiasing mode, + * any other value overrides and sets the corresponding PolyMode. + */ +cairo_public void +cairo_xcb_device_debug_set_precision (cairo_device_t *device, + int precision); + +cairo_public int +cairo_xcb_device_debug_get_precision (cairo_device_t *device); + CAIRO_END_DECLS #else /* CAIRO_HAS_XCB_SURFACE */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-core-compositor.c b/gfx/cairo/cairo/src/cairo-xlib-core-compositor.c new file mode 100644 index 000000000000..1392999617f7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-core-compositor.c @@ -0,0 +1,653 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +/* The original X drawing API was very restrictive in what it could handle, + * pixel-aligned fill/blits are all that map into Cairo's drawing model. + */ + +#include "cairoint.h" + +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib-private.h" +#include "cairo-xlib-surface-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-inline.h" +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-private.h" +#include "cairo-region-private.h" +#include "cairo-surface-offset-private.h" + +/* the low-level interface */ + +static cairo_int_status_t +acquire (void *abstract_dst) +{ + cairo_xlib_surface_t *dst = abstract_dst; + return _cairo_xlib_display_acquire (dst->base.device, &dst->display); +} + +static cairo_int_status_t +release (void *abstract_dst) +{ + cairo_xlib_surface_t *dst = abstract_dst; + + cairo_device_release (&dst->display->base); + dst->display = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +struct _fill_box { + Display *dpy; + Drawable drawable; + GC gc; + //cairo_surface_t *dither = NULL; +}; + +static cairo_bool_t fill_box (cairo_box_t *box, void *closure) +{ + struct _fill_box *data = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + + XFillRectangle (data->dpy, data->drawable, data->gc, x, y, width, height); + return TRUE; +} + +static void +_characterize_field (uint32_t mask, int *width, int *shift) +{ + *width = _cairo_popcount (mask); + /* The final '& 31' is to force a 0 mask to result in 0 shift. */ + *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; +} + +static uint32_t +color_to_pixel (cairo_xlib_surface_t *dst, + const cairo_color_t *color) +{ + uint32_t rgba = 0; + int width, shift; + + _characterize_field (dst->a_mask, &width, &shift); + rgba |= color->alpha_short >> (16 - width) << shift; + + _characterize_field (dst->r_mask, &width, &shift); + rgba |= color->red_short >> (16 - width) << shift; + + _characterize_field (dst->g_mask, &width, &shift); + rgba |= color->green_short >> (16 - width) << shift; + + _characterize_field (dst->b_mask, &width, &shift); + rgba |= color->blue_short >> (16 - width) << shift; + + return rgba; +} + +static cairo_int_status_t +_fill_box_init (struct _fill_box *fb, + cairo_xlib_surface_t *dst, + const cairo_color_t *color) +{ + cairo_int_status_t status; + + status = _cairo_xlib_surface_get_gc (dst->display, dst, &fb->gc); + if (unlikely (status)) + return status; + + fb->dpy = dst->display->display; + fb->drawable = dst->drawable; + + if (dst->visual && dst->visual->class != TrueColor && 0) { +#if 0 + cairo_solid_pattern_t solid; + cairo_surface_attributes_t attrs; + + _cairo_pattern_init_solid (&solid, color); + status = _cairo_pattern_acquire_surface (&solid.base, &dst->base, + 0, 0, + ARRAY_LENGTH (dither_pattern[0]), + ARRAY_LENGTH (dither_pattern), + CAIRO_PATTERN_ACQUIRE_NONE, + &dither, + &attrs); + if (unlikely (status)) { + _cairo_xlib_surface_put_gc (dst->display, dst, fb.gc); + return status; + } + + XSetTSOrigin (fb->dpy, fb->gc, + - (dst->base.device_transform.x0 + attrs.x_offset), + - (dst->base.device_transform.y0 + attrs.y_offset)); + XSetTile (fb->dpy, fb->gc, ((cairo_xlib_surface_t *) dither)->drawable); +#endif + } else { + XGCValues gcv; + + gcv.foreground = color_to_pixel (dst, color); + gcv.fill_style = FillSolid; + + XChangeGC (fb->dpy, fb->gc, GCFillStyle | GCForeground, &gcv); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static void +_fill_box_fini (struct _fill_box *fb, + cairo_xlib_surface_t *dst) +{ + _cairo_xlib_surface_put_gc (dst->display, dst, fb->gc); + //cairo_surface_destroy (fb->dither); +} + +cairo_int_status_t +_cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_int_status_t status; + struct _fill_box fb; + + status = _fill_box_init (&fb, dst, color); + if (unlikely (status)) + return status; + + _cairo_boxes_for_each_box (boxes, fill_box, &fb); + + _fill_box_fini (&fb, dst); + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst, + const cairo_color_t *color, + int num_rects, + cairo_rectangle_int_t *rects) +{ + cairo_int_status_t status; + struct _fill_box fb; + int i; + + status = _fill_box_init (&fb, dst, color); + if (unlikely (status)) + return status; + + for (i = 0; i < num_rects; i++) + XFillRectangle (fb.dpy, fb.drawable, fb.gc, + rects[i].x, rects[i].y, + rects[i].width, rects[i].height); + + _fill_box_fini (&fb, dst); + return CAIRO_STATUS_SUCCESS; +} + +struct _fallback_box { + cairo_xlib_surface_t *dst; + cairo_format_t format; + const cairo_pattern_t *pattern; +}; + +static cairo_bool_t fallback_box (cairo_box_t *box, void *closure) +{ + struct _fallback_box *data = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + cairo_surface_t *image; + cairo_status_t status; + + /* XXX for EXTEND_NONE and if the box is wholly outside we can just fill */ + + image = cairo_surface_create_similar_image (&data->dst->base, data->format, + width, height); + status = _cairo_surface_offset_paint (image, x, y, + CAIRO_OPERATOR_SOURCE, + data->pattern, NULL); + if (status == CAIRO_STATUS_SUCCESS) { + status = _cairo_xlib_surface_draw_image (data->dst, + (cairo_image_surface_t *)image, + 0, 0, + width, height, + x, y); + } + cairo_surface_destroy (image); + + return status == CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +fallback_boxes (cairo_xlib_surface_t *dst, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes) +{ + struct _fallback_box fb; + + /* XXX create_similar_image using pixman_format? */ + switch (dst->depth) { + case 8: fb.format = CAIRO_FORMAT_A8; break; + case 16: fb.format = CAIRO_FORMAT_RGB16_565; break; + case 24: fb.format = CAIRO_FORMAT_RGB24; break; + case 30: fb.format = CAIRO_FORMAT_RGB30; break; + case 32: fb.format = CAIRO_FORMAT_ARGB32; break; + default: return CAIRO_INT_STATUS_UNSUPPORTED; + } + + fb.dst = dst; + fb.pattern = pattern; + + if (! _cairo_boxes_for_each_box (boxes, fallback_box, &fb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +render_boxes (cairo_xlib_surface_t *dst, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes) +{ + if (pattern->filter != CAIRO_FILTER_NEAREST) + return fallback_boxes (dst, pattern, boxes); + + switch (pattern->extend) { + default: + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REFLECT: + case CAIRO_EXTEND_PAD: + return fallback_boxes (dst, pattern, boxes); + + case CAIRO_EXTEND_REPEAT: /* XXX Use tiling */ + return fallback_boxes (dst, pattern, boxes); + } +} + +/* the mid-level: converts boxes into drawing operations */ + +struct _box_data { + Display *dpy; + cairo_xlib_surface_t *dst; + cairo_surface_t *src; + GC gc; + int tx, ty; + int width, height; +}; + +static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure) +{ + struct _box_data *data = closure; + + /* The box is pixel-aligned so the truncation is safe. */ + return + _cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 && + _cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 && + _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width && + _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height; +} + +static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) +{ + const struct _box_data *iub = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + + return _cairo_xlib_surface_draw_image (iub->dst, + (cairo_image_surface_t *)iub->src, + x + iub->tx, y + iub->ty, + width, height, + x, y) == CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +surface_matches_image_format (cairo_xlib_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_format_masks_t format; + + return (_pixman_format_to_masks (image->pixman_format, &format) && + (format.alpha_mask == surface->a_mask || surface->a_mask == 0) && + (format.red_mask == surface->r_mask || surface->r_mask == 0) && + (format.green_mask == surface->g_mask || surface->g_mask == 0) && + (format.blue_mask == surface->b_mask || surface->b_mask == 0)); +} + +static cairo_status_t +upload_image_inplace (cairo_xlib_surface_t *dst, + const cairo_pattern_t *source, + cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *pattern; + struct _box_data iub; + cairo_image_surface_t *image; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pattern = (const cairo_surface_pattern_t *) source; + if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + image = (cairo_image_surface_t *) pattern->surface; + if (image->format == CAIRO_FORMAT_INVALID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (image->depth != dst->depth) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! surface_matches_image_format (dst, image)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX subsurface */ + + if (! _cairo_matrix_is_integer_translation (&source->matrix, + &iub.tx, &iub.ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + iub.dst = dst; + iub.src = &image->base; + iub.width = image->width; + iub.height = image->height; + + /* First check that the data is entirely within the image */ + if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &iub)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_boxes_for_each_box (boxes, image_upload_box, &iub)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t copy_box (cairo_box_t *box, void *closure) +{ + const struct _box_data *cb = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + + XCopyArea (cb->dpy, + ((cairo_xlib_surface_t *)cb->src)->drawable, + cb->dst->drawable, + cb->gc, + x + cb->tx, y + cb->ty, + width, height, + x, y); + return TRUE; +} + +static cairo_status_t +copy_boxes (cairo_xlib_surface_t *dst, + const cairo_pattern_t *source, + cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *pattern; + struct _box_data cb; + cairo_xlib_surface_t *src; + cairo_status_t status; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX subsurface */ + + pattern = (const cairo_surface_pattern_t *) source; + if (pattern->surface->backend->type != CAIRO_SURFACE_TYPE_XLIB) + return CAIRO_INT_STATUS_UNSUPPORTED; + + src = (cairo_xlib_surface_t *) pattern->surface; + if (src->depth != dst->depth) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only have a single control for subwindow_mode on the + * GC. If we have a Window destination, we need to set ClipByChildren, + * but if we have a Window source, we need IncludeInferiors. If we have + * both a Window destination and source, we must fallback. There is + * no convenient way to detect if a drawable is a Pixmap or Window, + * therefore we can only rely on those surfaces that we created + * ourselves to be Pixmaps, and treat everything else as a potential + * Window. + */ + if (! src->owns_pixmap && ! dst->owns_pixmap) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_xlib_surface_same_screen (dst, src)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, + &cb.tx, &cb.ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cb.dpy = dst->display->display; + cb.dst = dst; + cb.src = &src->base; + cb.width = src->width; + cb.height = src->height; + + /* First check that the data is entirely within the image */ + if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_xlib_surface_get_gc (dst->display, dst, &cb.gc); + if (unlikely (status)) + return status; + + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv); + } + + status = CAIRO_STATUS_SUCCESS; + if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb)) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv); + } + + _cairo_xlib_surface_put_gc (dst->display, dst, cb.gc); + + return status; +} + +static cairo_status_t +draw_boxes (cairo_composite_rectangles_t *extents, + cairo_boxes_t *boxes) +{ + cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface; + cairo_operator_t op = extents->op; + const cairo_pattern_t *src = &extents->source_pattern.base; + cairo_int_status_t status; + + if (boxes->num_boxes == 0 && extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op == CAIRO_OPERATOR_CLEAR) + op = CAIRO_OPERATOR_SOURCE; + + if (op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_opaque (src, &extents->bounded)) + op = CAIRO_OPERATOR_SOURCE; + + if (dst->base.is_clear && op == CAIRO_OPERATOR_OVER) + op = CAIRO_OPERATOR_SOURCE; + + if (op != CAIRO_OPERATOR_SOURCE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = acquire (dst); + if (unlikely (status)) + return status; + + if (src->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_xlib_core_fill_boxes + (dst, &((cairo_solid_pattern_t *) src)->color, boxes); + } else { + status = upload_image_inplace (dst, src, boxes); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = copy_boxes (dst, src, boxes); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = render_boxes (dst, src, boxes); + } + + release (dst); + + return status; +} + +/* high-level compositor interface */ + +static cairo_int_status_t +_cairo_xlib_core_compositor_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_int_status_t status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_clip_is_region (extents->clip)) { + cairo_boxes_t boxes; + + _cairo_clip_steal_boxes (extents->clip, &boxes); + status = draw_boxes (extents, &boxes); + _cairo_clip_unsteal_boxes (extents->clip, &boxes); + } + + return status; +} + +static cairo_int_status_t +_cairo_xlib_core_compositor_stroke (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + 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; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (extents->clip->path == NULL && + _cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents->clip); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = draw_boxes (extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + return status; +} + +static cairo_int_status_t +_cairo_xlib_core_compositor_fill (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_int_status_t status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (extents->clip->path == NULL && + _cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init_with_clip (&boxes, extents->clip); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = draw_boxes (extents, &boxes); + _cairo_boxes_fini (&boxes); + } + + return status; +} + +const cairo_compositor_t * +_cairo_xlib_core_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)) { + compositor.delegate = _cairo_xlib_fallback_compositor_get (); + + compositor.paint = _cairo_xlib_core_compositor_paint; + compositor.mask = NULL; + compositor.fill = _cairo_xlib_core_compositor_fill; + compositor.stroke = _cairo_xlib_core_compositor_stroke; + compositor.glyphs = NULL; /* XXX PolyGlyph? */ + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor; +} + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-display.c b/gfx/cairo/cairo/src/cairo-xlib-display.c index 139e63149614..108897e92a2e 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-display.c +++ b/gfx/cairo/cairo/src/cairo-xlib-display.c @@ -35,96 +35,21 @@ #include "cairoint.h" +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-freelist-private.h" #include "cairo-error-private.h" +#include "cairo-list-inline.h" #include /* For XESetCloseDisplay */ typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); -struct _cairo_xlib_job { - cairo_xlib_job_t *next; - enum { - RESOURCE, - WORK - } type; - union { - struct { - cairo_xlib_notify_resource_func notify; - XID xid; - } resource; - struct { - cairo_xlib_notify_func notify; - void *data; - void (*destroy) (void *); - } work; - } func; -}; - static cairo_xlib_display_t *_cairo_xlib_display_list; -static void -_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook); - -static void -_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display) -{ - cairo_xlib_screen_t *screen; - cairo_xlib_hook_t *hook; - - cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link) - _cairo_xlib_screen_close_display (display, screen); - - while (TRUE) { - hook = display->close_display_hooks; - if (hook == NULL) - break; - - _cairo_xlib_remove_close_display_hook_internal (display, hook); - - hook->func (display, hook); - } - display->closed = TRUE; -} - -static void -_cairo_xlib_display_finish (void *abstract_display) -{ - cairo_xlib_display_t *display = abstract_display; - - display->display = NULL; -} - -static void -_cairo_xlib_display_destroy (void *abstract_display) -{ - cairo_xlib_display_t *display = abstract_display; - - /* destroy all outstanding notifies */ - while (display->workqueue != NULL) { - cairo_xlib_job_t *job = display->workqueue; - display->workqueue = job->next; - - if (job->type == WORK && job->func.work.destroy != NULL) - job->func.work.destroy (job->func.work.data); - - _cairo_freelist_free (&display->wq_freelist, job); - } - _cairo_freelist_fini (&display->wq_freelist); - - while (! cairo_list_is_empty (&display->screens)) { - _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens, - cairo_xlib_screen_t, - link)); - } - - free (display); -} - static int _noop_error_handler (Display *display, XErrorEvent *event) @@ -133,63 +58,52 @@ _noop_error_handler (Display *display, } static void -_cairo_xlib_display_notify (cairo_xlib_display_t *display) +_cairo_xlib_display_finish (void *abstract_display) { - cairo_xlib_job_t *jobs, *job, *freelist; + cairo_xlib_display_t *display = abstract_display; Display *dpy = display->display; - /* Optimistic atomic pointer read -- don't care if it is wrong due to - * contention as we will check again very shortly. - */ - if (display->workqueue == NULL) - return; + _cairo_xlib_display_fini_shm (display); - jobs = display->workqueue; - while (jobs != NULL) { - display->workqueue = NULL; + if (! cairo_device_acquire (&display->base)) { + cairo_xlib_error_func_t old_handler; - /* reverse the list to obtain FIFO order */ - job = NULL; - do { - cairo_xlib_job_t *next = jobs->next; - jobs->next = job; - job = jobs; - jobs = next; - } while (jobs != NULL); - freelist = jobs = job; + /* protect the notifies from triggering XErrors */ + XSync (dpy, False); + old_handler = XSetErrorHandler (_noop_error_handler); - do { - job = jobs; - jobs = job->next; + while (! cairo_list_is_empty (&display->fonts)) { + _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts, + cairo_xlib_font_t, + link)); + } - switch (job->type){ - case WORK: - job->func.work.notify (dpy, job->func.work.data); - if (job->func.work.destroy != NULL) - job->func.work.destroy (job->func.work.data); - break; + while (! cairo_list_is_empty (&display->screens)) { + _cairo_xlib_screen_destroy (display, + cairo_list_first_entry (&display->screens, + cairo_xlib_screen_t, + link)); + } - case RESOURCE: - job->func.resource.notify (dpy, job->func.resource.xid); - break; - } - } while (jobs != NULL); + XSync (dpy, False); + XSetErrorHandler (old_handler); - do { - job = freelist; - freelist = job->next; - _cairo_freelist_free (&display->wq_freelist, job); - } while (freelist != NULL); - - jobs = display->workqueue; + cairo_device_release (&display->base); } } +static void +_cairo_xlib_display_destroy (void *abstract_display) +{ + cairo_xlib_display_t *display = abstract_display; + + free (display); +} + static int _cairo_xlib_close_display (Display *dpy, XExtCodes *codes) { cairo_xlib_display_t *display, **prev, *next; - cairo_xlib_error_func_t old_handler; CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); for (display = _cairo_xlib_display_list; display; display = display->next) @@ -199,22 +113,7 @@ _cairo_xlib_close_display (Display *dpy, XExtCodes *codes) if (display == NULL) return 0; - if (! cairo_device_acquire (&display->base)) { - /* protect the notifies from triggering XErrors */ - XSync (dpy, False); - old_handler = XSetErrorHandler (_noop_error_handler); - - _cairo_xlib_display_notify (display); - _cairo_xlib_call_close_display_hooks (display); - - /* catch any that arrived before marking the display as closed */ - _cairo_xlib_display_notify (display); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - - cairo_device_release (&display->base); - } + cairo_device_finish (&display->base); /* * Unhook from the global list @@ -231,9 +130,7 @@ _cairo_xlib_close_display (Display *dpy, XExtCodes *codes) } CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); - assert (display != NULL); - - cairo_device_finish (&display->base); + display->display = NULL; /* catch any later invalid access */ cairo_device_destroy (&display->base); /* Return value in accordance with requirements of @@ -252,8 +149,22 @@ static const cairo_device_backend_t _cairo_xlib_device_backend = { _cairo_xlib_display_destroy, }; +static void _cairo_xlib_display_select_compositor (cairo_xlib_display_t *display) +{ +#if 1 + if (display->render_major > 0 || display->render_minor >= 4) + display->compositor = _cairo_xlib_traps_compositor_get (); + else if (display->render_major > 0 || display->render_minor >= 0) + display->compositor = _cairo_xlib_mask_compositor_get (); + else + display->compositor = _cairo_xlib_core_compositor_get (); +#else + display->compositor = _cairo_xlib_fallback_compositor_get (); +#endif +} + /** - * cairo_xlib_device_create: + * _cairo_xlib_device_create: * @dpy: the display to create the device for * * Gets the device belonging to @dpy, or creates it if it doesn't exist yet. @@ -269,8 +180,6 @@ _cairo_xlib_device_create (Display *dpy) XExtCodes *codes; const char *env; - static int buggy_repeat_force = -1; - CAIRO_MUTEX_INITIALIZE (); /* There is an apparent deadlock between this mutex and the @@ -298,12 +207,19 @@ _cairo_xlib_device_create (Display *dpy) } } - display = malloc (sizeof (cairo_xlib_display_t)); + display = _cairo_malloc (sizeof (cairo_xlib_display_t)); if (unlikely (display == NULL)) { device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); goto UNLOCK; } + _cairo_device_init (&display->base, &_cairo_xlib_device_backend); + + display->display = dpy; + cairo_list_init (&display->screens); + cairo_list_init (&display->fonts); + display->closed = FALSE; + /* Xlib calls out to the extension close_display hooks in LIFO * order. So we have to ensure that all extensions that we depend * on in our close_display hook are properly initialized before we @@ -329,29 +245,21 @@ _cairo_xlib_device_create (Display *dpy) } } - codes = XAddExtension (dpy); - if (unlikely (codes == NULL)) { - device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); - free (display); - goto UNLOCK; - } + _cairo_xlib_display_select_compositor (display); - _cairo_device_init (&display->base, &_cairo_xlib_device_backend); - - XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); - - _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t)); - - cairo_device_reference (&display->base); /* add one for the CloseDisplay */ - display->display = dpy; - cairo_list_init (&display->screens); - display->workqueue = NULL; - display->close_display_hooks = NULL; - display->closed = FALSE; + display->white = NULL; + memset (display->alpha, 0, sizeof (display->alpha)); + memset (display->solid, 0, sizeof (display->solid)); + memset (display->solid_cache, 0, sizeof (display->solid_cache)); + memset (display->last_solid_cache, 0, sizeof (display->last_solid_cache)); memset (display->cached_xrender_formats, 0, sizeof (display->cached_xrender_formats)); + display->force_precision = -1; + + _cairo_xlib_display_init_shm (display); + /* Prior to Render 0.10, there is no protocol support for gradients and * we call function stubs instead, which would silently consume the drawing. */ @@ -383,7 +291,7 @@ _cairo_xlib_device_create (Display *dpy) * * 1. The original bug that led to the buggy_repeat * workaround. This was a bug that Owen Taylor investigated, - * understood well, and characterized against carious X + * understood well, and characterized against various X * servers. Confirmed X servers with this bug include: * * "XFree86" <= 40500000 @@ -407,7 +315,7 @@ _cairo_xlib_device_create (Display *dpy) * safest to just blacklist all old-versioning-scheme X servers, * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. */ - if (strstr (ServerVendor (dpy), "X.Org") != NULL) { + if (_cairo_xlib_vendor_is_xorg (dpy)) { if (VendorRelease (dpy) >= 60700000) { if (VendorRelease (dpy) < 70000000) display->buggy_repeat = TRUE; @@ -434,29 +342,15 @@ _cairo_xlib_device_create (Display *dpy) display->buggy_pad_reflect = TRUE; } - /* gradients don't seem to work */ - display->buggy_gradients = TRUE; - - - /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */ - /* If buggy_repeat_force == -1, then initialize. - * - set to -2, meaning "nothing was specified", and we trust the above detection. - * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off - * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on - */ - if (buggy_repeat_force == -1) { - const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT"); - - buggy_repeat_force = -2; - - if (flag && flag[0] == '0') - buggy_repeat_force = 0; - else if (flag && flag[0] == '1') - buggy_repeat_force = 1; + codes = XAddExtension (dpy); + if (unlikely (codes == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + free (display); + goto UNLOCK; } - if (buggy_repeat_force != -2) - display->buggy_repeat = (buggy_repeat_force == 1); + XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); + cairo_device_reference (&display->base); /* add one for the CloseDisplay */ display->next = _cairo_xlib_display_list; _cairo_xlib_display_list = display; @@ -468,92 +362,6 @@ UNLOCK: return device; } -void -_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - hook->prev = NULL; - hook->next = display->close_display_hooks; - if (hook->next != NULL) - hook->next->prev = hook; - display->close_display_hooks = hook; -} - -static void -_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - if (display->close_display_hooks == hook) - display->close_display_hooks = hook->next; - else if (hook->prev != NULL) - hook->prev->next = hook->next; - - if (hook->next != NULL) - hook->next->prev = hook->prev; - - hook->prev = NULL; - hook->next = NULL; -} - -void -_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - _cairo_xlib_remove_close_display_hook_internal (display, hook); -} - -cairo_status_t -_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, - cairo_xlib_notify_resource_func notify, - XID xid) -{ - cairo_xlib_job_t *job; - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - - if (display->closed == FALSE) { - job = _cairo_freelist_alloc (&display->wq_freelist); - if (job != NULL) { - job->type = RESOURCE; - job->func.resource.xid = xid; - job->func.resource.notify = notify; - - job->next = display->workqueue; - display->workqueue = job; - - status = CAIRO_STATUS_SUCCESS; - } - } - - return status; -} - -cairo_status_t -_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, - cairo_xlib_notify_func notify, - void *data, - void (*destroy) (void *)) -{ - cairo_xlib_job_t *job; - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - - if (display->closed == FALSE) { - job = _cairo_freelist_alloc (&display->wq_freelist); - if (job != NULL) { - job->type = WORK; - job->func.work.data = data; - job->func.work.notify = notify; - job->func.work.destroy = destroy; - - job->next = display->workqueue; - display->workqueue = job; - - status = CAIRO_STATUS_SUCCESS; - } - } - - return status; -} - cairo_status_t _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) { @@ -564,8 +372,121 @@ _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **disp return status; *display = (cairo_xlib_display_t *) device; - _cairo_xlib_display_notify (*display); - return status; + return CAIRO_STATUS_SUCCESS; +} + +XRenderPictFormat * +_cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display, + pixman_format_code_t format) +{ + Display *dpy = display->display; + XRenderPictFormat tmpl; + int mask; + + /* No equivalent in X11 yet. */ + if (format == PIXMAN_rgba_float || format == PIXMAN_rgb_float) + return NULL; + +#define MASK(x) ((1<<(x))-1) + + tmpl.depth = PIXMAN_FORMAT_DEPTH(format); + mask = PictFormatType | PictFormatDepth; + + switch (PIXMAN_FORMAT_TYPE(format)) { + case PIXMAN_TYPE_ARGB: + tmpl.type = PictTypeDirect; + + tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); + if (PIXMAN_FORMAT_A(format)) + tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) + + PIXMAN_FORMAT_G(format) + + PIXMAN_FORMAT_B(format)); + + tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); + tmpl.direct.red = (PIXMAN_FORMAT_G(format) + + PIXMAN_FORMAT_B(format)); + + tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); + tmpl.direct.green = PIXMAN_FORMAT_B(format); + + tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); + tmpl.direct.blue = 0; + + mask |= PictFormatRed | PictFormatRedMask; + mask |= PictFormatGreen | PictFormatGreenMask; + mask |= PictFormatBlue | PictFormatBlueMask; + mask |= PictFormatAlpha | PictFormatAlphaMask; + break; + + case PIXMAN_TYPE_ABGR: + tmpl.type = PictTypeDirect; + + tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); + if (tmpl.direct.alphaMask) + tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) + + PIXMAN_FORMAT_G(format) + + PIXMAN_FORMAT_R(format)); + + tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); + tmpl.direct.blue = (PIXMAN_FORMAT_G(format) + + PIXMAN_FORMAT_R(format)); + + tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); + tmpl.direct.green = PIXMAN_FORMAT_R(format); + + tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); + tmpl.direct.red = 0; + + mask |= PictFormatRed | PictFormatRedMask; + mask |= PictFormatGreen | PictFormatGreenMask; + mask |= PictFormatBlue | PictFormatBlueMask; + mask |= PictFormatAlpha | PictFormatAlphaMask; + break; + + case PIXMAN_TYPE_BGRA: + tmpl.type = PictTypeDirect; + + tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); + tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format)); + + tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); + tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - + PIXMAN_FORMAT_G(format)); + + tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); + tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - + PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format)); + + tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); + tmpl.direct.alpha = 0; + + mask |= PictFormatRed | PictFormatRedMask; + mask |= PictFormatGreen | PictFormatGreenMask; + mask |= PictFormatBlue | PictFormatBlueMask; + mask |= PictFormatAlpha | PictFormatAlphaMask; + break; + + case PIXMAN_TYPE_A: + tmpl.type = PictTypeDirect; + + tmpl.direct.alpha = 0; + tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); + + mask |= PictFormatAlpha | PictFormatAlphaMask; + break; + + case PIXMAN_TYPE_COLOR: + case PIXMAN_TYPE_GRAY: + /* XXX Find matching visual/colormap */ + tmpl.type = PictTypeIndexed; + //tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid; + //mask |= PictFormatColormap; + return NULL; + } +#undef MASK + + /* XXX caching? */ + return XRenderFindFormat(dpy, mask, &tmpl, 0); } XRenderPictFormat * @@ -574,15 +495,9 @@ _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, { XRenderPictFormat *xrender_format; -#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER - xrender_format = display->cached_xrender_formats[format]; - if (likely (xrender_format != NULL)) - return xrender_format; -#endif - xrender_format = display->cached_xrender_formats[format]; if (xrender_format == NULL) { - int pict_format; + int pict_format = PictStandardNUM; switch (format) { case CAIRO_FORMAT_A1: @@ -591,34 +506,31 @@ _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, pict_format = PictStandardA8; break; case CAIRO_FORMAT_RGB24: pict_format = PictStandardRGB24; break; - case CAIRO_FORMAT_RGB16_565: { - Visual *visual = NULL; - Screen *screen = DefaultScreenOfDisplay(display->display); - int j; - for (j = 0; j < screen->ndepths; j++) { - Depth *d = &screen->depths[j]; - if (d->depth == 16 && d->nvisuals && &d->visuals[0]) { - if (d->visuals[0].red_mask == 0xf800 && - d->visuals[0].green_mask == 0x7e0 && - d->visuals[0].blue_mask == 0x1f) - visual = &d->visuals[0]; - break; - } - } - if (!visual) - return NULL; - xrender_format = XRenderFindVisualFormat(display->display, visual); + case CAIRO_FORMAT_RGB16_565: + xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, + PIXMAN_r5g6b5); + break; + case CAIRO_FORMAT_RGB30: + xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, + PIXMAN_x2r10g10b10); + break; + case CAIRO_FORMAT_RGBA128F: + xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, + PIXMAN_rgba_float); + break; + case CAIRO_FORMAT_RGB96F: + xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display, + PIXMAN_rgb_float); break; - } case CAIRO_FORMAT_INVALID: default: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: pict_format = PictStandardARGB32; break; } - if (!xrender_format) - xrender_format = XRenderFindStandardFormat (display->display, - pict_format); + if (pict_format != PictStandardNUM) + xrender_format = + XRenderFindStandardFormat (display->display, pict_format); display->cached_xrender_formats[format] = xrender_format; } @@ -642,14 +554,6 @@ _cairo_xlib_display_get_screen (cairo_xlib_display_t *display, return NULL; } -void -_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, - int *major, int *minor) -{ - *major = display->render_major; - *minor = display->render_minor; -} - cairo_bool_t _cairo_xlib_display_has_repeat (cairo_device_t *device) { @@ -667,3 +571,97 @@ _cairo_xlib_display_has_gradients (cairo_device_t *device) { return ! ((cairo_xlib_display_t *) device)->buggy_gradients; } + +/** + * cairo_xlib_device_debug_cap_xrender_version: + * @device: a #cairo_device_t for the Xlib backend + * @major_version: major version to restrict to + * @minor_version: minor version to restrict to + * + * Restricts all future Xlib surfaces for this devices to the specified version + * of the RENDER extension. This function exists solely for debugging purpose. + * It lets you find out how cairo would behave with an older version of + * the RENDER extension. + * + * Use the special values -1 and -1 for disabling the RENDER extension. + * + * Since: 1.12 + **/ +void +cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, + int major_version, + int minor_version) +{ + cairo_xlib_display_t *display = (cairo_xlib_display_t *) device; + + if (device == NULL || device->status) + return; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) + return; + + if (major_version < display->render_major || + (major_version == display->render_major && + minor_version < display->render_minor)) + { + display->render_major = major_version; + display->render_minor = minor_version; + } + + _cairo_xlib_display_select_compositor (display); +} + +/** + * cairo_xlib_device_debug_set_precision: + * @device: a #cairo_device_t for the Xlib backend + * @precision: the precision to use + * + * Render supports two modes of precision when rendering trapezoids. Set + * the precision to the desired mode. + * + * Since: 1.12 + **/ +void +cairo_xlib_device_debug_set_precision (cairo_device_t *device, + int precision) +{ + if (device == NULL || device->status) + return; + if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return; + } + + ((cairo_xlib_display_t *) device)->force_precision = precision; +} + +/** + * cairo_xlib_device_debug_get_precision: + * @device: a #cairo_device_t for the Xlib backend + * + * Get the Xrender precision mode. + * + * Returns: the render precision mode + * + * Since: 1.12 + **/ +int +cairo_xlib_device_debug_get_precision (cairo_device_t *device) +{ + if (device == NULL || device->status) + return -1; + if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return -1; + } + + return ((cairo_xlib_display_t *) device)->force_precision; +} + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-fallback-compositor.c b/gfx/cairo/cairo/src/cairo-xlib-fallback-compositor.c new file mode 100644 index 000000000000..ed2845db57db --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-fallback-compositor.c @@ -0,0 +1,248 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +#include "cairoint.h" + +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-offset-private.h" + +static const cairo_compositor_t * +_get_compositor (cairo_surface_t *surface) +{ + return ((cairo_image_surface_t *)surface)->compositor; +} + +static cairo_bool_t +unclipped (cairo_xlib_surface_t *xlib, cairo_clip_t *clip) +{ + cairo_rectangle_int_t r; + + r.x = r.y = 0; + r.width = xlib->width; + r.height = xlib->height; + return _cairo_clip_contains_rectangle (clip, &r); +} + +static cairo_int_status_t +_cairo_xlib_shm_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; + cairo_int_status_t status; + cairo_surface_t *shm; + cairo_bool_t overwrite; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + overwrite = + extents->op <= CAIRO_OPERATOR_SOURCE && unclipped (xlib, extents->clip); + + shm = _cairo_xlib_surface_get_shm (xlib, overwrite); + if (shm == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_compositor_paint (_get_compositor (shm), shm, + extents->op, + &extents->source_pattern.base, + extents->clip); + if (unlikely (status)) + return status; + + xlib->base.is_clear = + extents->op == CAIRO_OPERATOR_CLEAR && unclipped (xlib, extents->clip); + xlib->base.serial++; + xlib->fallback++; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_xlib_shm_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; + cairo_int_status_t status; + cairo_surface_t *shm; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + shm = _cairo_xlib_surface_get_shm (xlib, FALSE); + if (shm == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_compositor_mask (_get_compositor (shm), shm, + extents->op, + &extents->source_pattern.base, + &extents->mask_pattern.base, + extents->clip); + if (unlikely (status)) + return status; + + xlib->base.is_clear = FALSE; + xlib->base.serial++; + xlib->fallback++; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_xlib_shm_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; + cairo_int_status_t status; + cairo_surface_t *shm; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + shm = _cairo_xlib_surface_get_shm (xlib, FALSE); + if (shm == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_compositor_stroke (_get_compositor (shm), shm, + extents->op, + &extents->source_pattern.base, + path, style, + ctm, ctm_inverse, + tolerance, + antialias, + extents->clip); + if (unlikely (status)) + return status; + + xlib->base.is_clear = FALSE; + xlib->base.serial++; + xlib->fallback++; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_xlib_shm_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; + cairo_int_status_t status; + cairo_surface_t *shm; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + shm = _cairo_xlib_surface_get_shm (xlib, FALSE); + if (shm == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_compositor_fill (_get_compositor (shm), shm, + extents->op, + &extents->source_pattern.base, + path, + fill_rule, tolerance, antialias, + extents->clip); + if (unlikely (status)) + return status; + + xlib->base.is_clear = FALSE; + xlib->base.serial++; + xlib->fallback++; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static cairo_int_status_t +_cairo_xlib_shm_compositor_glyphs (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface; + cairo_int_status_t status; + cairo_surface_t *shm; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + shm = _cairo_xlib_surface_get_shm (xlib, FALSE); + if (shm == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_compositor_glyphs (_get_compositor (shm), shm, + extents->op, + &extents->source_pattern.base, + glyphs, num_glyphs, scaled_font, + extents->clip); + if (unlikely (status)) + return status; + + xlib->base.is_clear = FALSE; + xlib->base.serial++; + xlib->fallback++; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +} + +static const cairo_compositor_t _cairo_xlib_shm_compositor = { + &_cairo_fallback_compositor, + + _cairo_xlib_shm_compositor_paint, + _cairo_xlib_shm_compositor_mask, + _cairo_xlib_shm_compositor_stroke, + _cairo_xlib_shm_compositor_fill, + _cairo_xlib_shm_compositor_glyphs, +}; + +const cairo_compositor_t * +_cairo_xlib_fallback_compositor_get (void) +{ + return &_cairo_xlib_shm_compositor; +} + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-private.h b/gfx/cairo/cairo/src/cairo-xlib-private.h index bd260bc0ed4b..8e338aea6fad 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-private.h +++ b/gfx/cairo/cairo/src/cairo-xlib-private.h @@ -37,8 +37,8 @@ #ifndef CAIRO_XLIB_PRIVATE_H #define CAIRO_XLIB_PRIVATE_H -#include "cairo-xlib.h" #include "cairo-xlib-xrender-private.h" +#include "cairo-xlib.h" #include "cairo-compiler-private.h" #include "cairo-device-private.h" @@ -46,24 +46,27 @@ #include "cairo-list-private.h" #include "cairo-reference-count-private.h" #include "cairo-types-private.h" +#include "cairo-scaled-font-private.h" +#include "cairo-surface-private.h" + +#include +#include typedef struct _cairo_xlib_display cairo_xlib_display_t; +typedef struct _cairo_xlib_shm_display cairo_xlib_shm_display_t; typedef struct _cairo_xlib_screen cairo_xlib_screen_t; - -typedef struct _cairo_xlib_hook cairo_xlib_hook_t; -typedef struct _cairo_xlib_job cairo_xlib_job_t; -typedef void (*cairo_xlib_notify_func) (Display *, void *); -typedef void (*cairo_xlib_notify_resource_func) (Display *, XID); - -struct _cairo_xlib_hook { - cairo_xlib_hook_t *prev, *next; /* private */ - void (*func) (cairo_xlib_display_t *display, void *data); -}; +typedef struct _cairo_xlib_source cairo_xlib_source_t; +typedef struct _cairo_xlib_proxy cairo_xlib_proxy_t; +typedef struct _cairo_xlib_surface cairo_xlib_surface_t; /* size of color cube */ #define CUBE_SIZE 6 /* size of gray ramp */ #define RAMP_SIZE 16 +/* maximum number of cached GC's */ +#define GC_CACHE_SIZE 4 +/* maximum width/height of an X11 drawable */ +#define XLIB_COORD_MAX 32767 struct _cairo_xlib_display { cairo_device_t base; @@ -72,18 +75,45 @@ struct _cairo_xlib_display { Display *display; cairo_list_t screens; + cairo_list_t fonts; + + cairo_xlib_shm_display_t *shm; + + const cairo_compositor_t *compositor; int render_major; int render_minor; - XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1]; + XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB30 + 1]; - cairo_xlib_job_t *workqueue; - cairo_freelist_t wq_freelist; + int force_precision; - cairo_xlib_hook_t *close_display_hooks; - unsigned int buggy_gradients :1; - unsigned int buggy_pad_reflect :1; - unsigned int buggy_repeat :1; + cairo_surface_t *white; + cairo_surface_t *alpha[256]; + cairo_surface_t *solid[32]; + uint32_t solid_cache[32]; /* low 16 are opaque, high 16 transparent */ + struct { + uint32_t color; + int index; + } last_solid_cache[2]; + + /* TRUE if the server has a bug with repeating pictures + * + * https://bugs.freedesktop.org/show_bug.cgi?id=3566 + * + * We can't test for this because it depends on whether the + * picture is in video memory or not. + * + * We also use this variable as a guard against a second + * independent bug with transformed repeating pictures: + * + * https://lists.freedesktop.org/archives/cairo/2004-September/001839.html + * + * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so + * we can reuse the test for now. + */ + unsigned int buggy_gradients : 1; + unsigned int buggy_pad_reflect : 1; + unsigned int buggy_repeat : 1; unsigned int closed :1; }; @@ -103,45 +133,122 @@ struct _cairo_xlib_screen { cairo_device_t *device; Screen *screen; + cairo_list_t surfaces; + cairo_bool_t has_font_options; cairo_font_options_t font_options; - GC gc[4]; - cairo_atomic_int_t gc_depths; /* 4 x uint8_t */ + GC gc[GC_CACHE_SIZE]; + uint8_t gc_depths[GC_CACHE_SIZE]; cairo_list_t visuals; }; +enum { + GLYPHSET_INDEX_ARGB32, + GLYPHSET_INDEX_A8, + GLYPHSET_INDEX_A1, + NUM_GLYPHSETS +}; + +typedef struct _cairo_xlib_font_glyphset { + GlyphSet glyphset; + cairo_format_t format; + XRenderPictFormat *xrender_format; + struct _cairo_xlib_font_glyphset_free_glyphs { + int count; + unsigned long indices[128]; + } to_free; +} cairo_xlib_font_glyphset_t; + +typedef struct _cairo_xlib_font { + cairo_scaled_font_private_t base; + cairo_scaled_font_t *font; + cairo_device_t *device; + cairo_list_t link; + cairo_xlib_font_glyphset_t glyphset[NUM_GLYPHSETS]; +} cairo_xlib_font_t; + +struct _cairo_xlib_surface { + cairo_surface_t base; + + Picture picture; + Drawable drawable; + + const cairo_compositor_t *compositor; + cairo_surface_t *shm; + int fallback; + + cairo_xlib_display_t *display; + cairo_xlib_screen_t *screen; + cairo_list_t link; + + Display *dpy; /* only valid between acquire/release */ + cairo_bool_t owns_pixmap; + Visual *visual; + + int use_pixmap; + + int width; + int height; + int depth; + + int precision; + XRenderPictFormat *xrender_format; + /* XXX pixman_format instead of masks? */ + uint32_t a_mask; + uint32_t r_mask; + uint32_t g_mask; + uint32_t b_mask; + + struct _cairo_xlib_source { + cairo_surface_t base; + + Picture picture; + Pixmap pixmap; + Display *dpy; + + unsigned int filter:3; + unsigned int extend:3; + unsigned int has_matrix:1; + unsigned int has_component_alpha:1; + } embedded_source; +}; + +struct _cairo_xlib_proxy { + struct _cairo_xlib_source source; + cairo_surface_t *owner; +}; + +inline static cairo_bool_t +_cairo_xlib_vendor_is_xorg (Display *dpy) +{ + const char *const vendor = ServerVendor (dpy); + return strstr (vendor, "X.Org") || strstr (vendor, "Xorg"); +} + +cairo_private cairo_status_t +_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + GC *gc); + cairo_private cairo_device_t * _cairo_xlib_device_create (Display *display); +cairo_private void +_cairo_xlib_display_init_shm (cairo_xlib_display_t *display); + +cairo_private void +_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display); + cairo_private cairo_xlib_screen_t * _cairo_xlib_display_get_screen (cairo_xlib_display_t *display, Screen *screen); -cairo_private void -_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); - -cairo_private void -_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook); - -cairo_private cairo_status_t -_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, - cairo_xlib_notify_func notify, - void *data, - void (*destroy)(void *)); -cairo_private cairo_status_t -_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, - cairo_xlib_notify_resource_func notify, - XID resource); cairo_private cairo_status_t _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display); -cairo_private void -_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, - int *major, int *minor); - cairo_private cairo_bool_t _cairo_xlib_display_has_repeat (cairo_device_t *device); @@ -151,31 +258,35 @@ _cairo_xlib_display_has_reflect (cairo_device_t *device); cairo_private cairo_bool_t _cairo_xlib_display_has_gradients (cairo_device_t *device); +cairo_private void +_cairo_xlib_display_set_precision(cairo_device_t *device, + int precision); + cairo_private XRenderPictFormat * _cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, cairo_format_t format); +cairo_private XRenderPictFormat * +_cairo_xlib_display_get_xrender_format_for_pixman (cairo_xlib_display_t *display, + pixman_format_code_t format); + cairo_private cairo_status_t _cairo_xlib_screen_get (Display *dpy, Screen *screen, cairo_xlib_screen_t **out); cairo_private void -_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info); - -cairo_private void -_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info); +_cairo_xlib_screen_destroy (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info); cairo_private GC _cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, cairo_xlib_screen_t *info, int depth, Drawable drawable); - cairo_private void _cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info, + cairo_xlib_screen_t *info, int depth, GC gc); @@ -197,4 +308,165 @@ _cairo_xlib_visual_info_create (Display *dpy, cairo_private void _cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info); +cairo_private const cairo_compositor_t * +_cairo_xlib_core_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_xlib_fallback_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_xlib_mask_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_xlib_traps_compositor_get (void); + +cairo_private void +_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface); + +cairo_private void +_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface, + cairo_antialias_t antialias); + +cairo_private cairo_int_status_t +_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + cairo_surface_attributes_t *attributes, + double xc, + double yc); + +cairo_private cairo_status_t +_cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface, + cairo_image_surface_t *image, + int src_x, + int src_y, + int width, + int height, + int dst_x, + int dst_y); + +cairo_private cairo_surface_t * +_cairo_xlib_source_create_for_pattern (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 void +_cairo_xlib_font_close (cairo_xlib_font_t *font); + +#define CAIRO_RENDER_AT_LEAST(surface, major, minor) \ + (((surface)->render_major > major) || \ + (((surface)->render_major == major) && ((surface)->render_minor >= minor))) + +#define CAIRO_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0) +#define CAIRO_RENDER_HAS_COMPOSITE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0) +#define CAIRO_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0) + +#define CAIRO_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 1) + +#define CAIRO_RENDER_HAS_DISJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2) +#define CAIRO_RENDER_HAS_CONJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2) + +#define CAIRO_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_RENDER_HAS_TRIANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_RENDER_HAS_TRISTRIP(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_RENDER_HAS_TRIFAN(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4) + +#define CAIRO_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_RENDER_HAS_FILTERS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_RENDER_HAS_FILTER_GOOD(surface) FALSE +#define CAIRO_RENDER_HAS_FILTER_BEST(surface) FALSE + +#define CAIRO_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10) +#define CAIRO_RENDER_HAS_GRADIENTS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10) + +#define CAIRO_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 11) + +#define CAIRO_RENDER_SUPPORTS_OPERATOR(surface, op) \ + ((op) <= CAIRO_OPERATOR_SATURATE || \ + (CAIRO_RENDER_HAS_PDF_OPERATORS(surface) && \ + (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) + +/* + * Return whether two xlib surfaces share the same + * screen. Both core and Render drawing require this + * when using multiple drawables in an operation. + */ +static inline cairo_bool_t +_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, + cairo_xlib_surface_t *src) +{ + return dst->screen == src->screen; +} + +cairo_private cairo_int_status_t +_cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes); + +cairo_private cairo_int_status_t +_cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst, + const cairo_color_t *color, + int num_rects, + cairo_rectangle_int_t *rects); + +static inline void +_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + GC gc) +{ + _cairo_xlib_screen_put_gc (display, + surface->screen, + surface->depth, + gc); +} + +cairo_private cairo_surface_t * +_cairo_xlib_surface_create_similar_shm (void *surface, + cairo_format_t format, + int width, int height); + +cairo_private cairo_surface_t * +_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, + cairo_bool_t overwrite); + +cairo_private cairo_int_status_t +_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface); + +cairo_private cairo_surface_t * +_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, + pixman_format_code_t format, + int width, int height); + +cairo_private cairo_surface_t * +_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, + pixman_format_code_t format, + int width, int height); + +cairo_private void +_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, + XImage *ximage); + +cairo_private void * +_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface); + +cairo_private void +_cairo_xlib_shm_surface_mark_active (cairo_surface_t *shm); + +cairo_private cairo_bool_t +_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface); + +cairo_private Pixmap +_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface); + +cairo_private XRenderPictFormat * +_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface); + +cairo_private pixman_format_code_t +_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface); + #endif /* CAIRO_XLIB_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-render-compositor.c b/gfx/cairo/cairo/src/cairo-xlib-render-compositor.c new file mode 100644 index 000000000000..bf8d20546b03 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-render-compositor.c @@ -0,0 +1,2022 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +#include "cairoint.h" + +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-damage-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-pattern-private.h" +#include "cairo-pixman-private.h" +#include "cairo-traps-private.h" +#include "cairo-tristrip-private.h" + +static cairo_int_status_t +check_composite (const cairo_composite_rectangles_t *extents) +{ + cairo_xlib_display_t *display = ((cairo_xlib_surface_t *)extents->surface)->display; + + if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +acquire (void *abstract_dst) +{ + cairo_xlib_surface_t *dst = abstract_dst; + cairo_int_status_t status; + + status = _cairo_xlib_display_acquire (dst->base.device, &dst->display); + if (unlikely (status)) + return status; + + dst->dpy = dst->display->display; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +release (void *abstract_dst) +{ + cairo_xlib_surface_t *dst = abstract_dst; + + cairo_device_release (&dst->display->base); + dst->dpy = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +set_clip_region (void *_surface, + cairo_region_t *region) +{ + cairo_xlib_surface_t *surface = _surface; + + _cairo_xlib_surface_ensure_picture (surface); + + if (region != NULL) { + XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *rects = stack_rects; + int n_rects, i; + + n_rects = cairo_region_num_rectangles (region); + if (n_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; + } + XRenderSetPictureClipRectangles (surface->dpy, + surface->picture, + 0, 0, + rects, n_rects); + if (rects != stack_rects) + free (rects); + } else { + XRenderPictureAttributes pa; + pa.clip_mask = None; + XRenderChangePicture (surface->dpy, + surface->picture, + CPClipMask, &pa); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +copy_image_boxes (void *_dst, + cairo_image_surface_t *image, + cairo_boxes_t *boxes, + int dx, int dy) +{ + cairo_xlib_surface_t *dst = _dst; + struct _cairo_boxes_chunk *chunk; + cairo_int_status_t status; + Pixmap src; + GC gc; + int i, j; + + assert (image->depth == dst->depth); + + status = acquire (dst); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc); + if (unlikely (status)) { + release (dst); + return status; + } + + src = _cairo_xlib_shm_surface_get_pixmap (&image->base); + if (boxes->num_boxes == 1) { + int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); + int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); + int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); + int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); + + _cairo_xlib_shm_surface_mark_active (&image->base); + XCopyArea (dst->dpy, src, dst->drawable, gc, + x1 + dx, y1 + dy, + x2 - x1, y2 - y1, + x1, y1); + } else { + XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *rects = stack_rects; + + if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + j = 0; + 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); + + if (x2 > x1 && y2 > y1) { + rects[j].x = x1; + rects[j].y = y1; + rects[j].width = x2 - x1; + rects[j].height = y2 - y1; + j++; + } + } + } + + XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted); + _cairo_xlib_shm_surface_mark_active (&image->base); + XCopyArea (dst->dpy, src, dst->drawable, gc, + 0, 0, image->width, image->height, -dx, -dy); + XSetClipMask (dst->dpy, gc, None); + + if (rects != stack_rects) + free (rects); + } + + _cairo_xlib_surface_put_gc (dst->display, dst, gc); + release (dst); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +boxes_cover_surface (cairo_boxes_t *boxes, + cairo_xlib_surface_t *surface) +{ + cairo_box_t *b; + + if (boxes->num_boxes != 1) + return FALSE; + + b = &boxes->chunks.base[0]; + + if (_cairo_fixed_integer_part (b->p1.x) > 0 || + _cairo_fixed_integer_part (b->p1.y) > 0) + return FALSE; + + if (_cairo_fixed_integer_part (b->p2.x) < surface->width || + _cairo_fixed_integer_part (b->p2.y) < surface->height) + return FALSE; + + return TRUE; +} + +static cairo_int_status_t +draw_image_boxes (void *_dst, + cairo_image_surface_t *image, + cairo_boxes_t *boxes, + int dx, int dy) +{ + cairo_xlib_surface_t *dst = _dst; + struct _cairo_boxes_chunk *chunk; + cairo_image_surface_t *shm = NULL; + cairo_int_status_t status; + int i; + + if (image->base.device == dst->base.device) { + if (image->depth != dst->depth) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) + return copy_image_boxes (dst, image, boxes, dx, dy); + + goto draw_image_boxes; + } + + if (boxes_cover_surface (boxes, dst)) + shm = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (dst, TRUE); + if (shm) { + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + cairo_box_t *b = &chunk->base[i]; + cairo_rectangle_int_t r; + + r.x = _cairo_fixed_integer_part (b->p1.x); + r.y = _cairo_fixed_integer_part (b->p1.y); + r.width = _cairo_fixed_integer_part (b->p2.x) - r.x; + r.height = _cairo_fixed_integer_part (b->p2.y) - r.y; + + if (shm->pixman_format != image->pixman_format || + ! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data, + image->stride / sizeof (uint32_t), + shm->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (image->pixman_format), + PIXMAN_FORMAT_BPP (shm->pixman_format), + r.x + dx, r.y + dy, + r.x, r.y, + r.width, r.height)) + { + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, shm->pixman_image, + r.x + dx, r.y + dy, + 0, 0, + r.x, r.y, + r.width, r.height); + } + + shm->base.damage = + _cairo_damage_add_rectangle (shm->base.damage, &r); + } + } + dst->base.is_clear = FALSE; + dst->fallback++; + dst->base.serial++; + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + if (image->depth == dst->depth && + ((cairo_xlib_display_t *)dst->display)->shm) { + cairo_box_t extents; + cairo_rectangle_int_t r; + + _cairo_boxes_extents (boxes, &extents); + _cairo_box_round_to_rectangle (&extents, &r); + + shm = (cairo_image_surface_t *) + _cairo_xlib_surface_create_shm (dst, image->pixman_format, + r.width, r.height); + if (shm) { + int tx = -r.x, ty = -r.y; + + assert (shm->pixman_format == image->pixman_format); + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + cairo_box_t *b = &chunk->base[i]; + + r.x = _cairo_fixed_integer_part (b->p1.x); + r.y = _cairo_fixed_integer_part (b->p1.y); + r.width = _cairo_fixed_integer_part (b->p2.x) - r.x; + r.height = _cairo_fixed_integer_part (b->p2.y) - r.y; + + if (! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data, + image->stride / sizeof (uint32_t), + shm->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (image->pixman_format), + PIXMAN_FORMAT_BPP (shm->pixman_format), + r.x + dx, r.y + dy, + r.x + tx, r.y + ty, + r.width, r.height)) + { + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, shm->pixman_image, + r.x + dx, r.y + dy, + 0, 0, + r.x + tx, r.y + ty, + r.width, r.height); + } + } + } + + dx = tx; + dy = ty; + image = shm; + + if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) { + status = copy_image_boxes (dst, image, boxes, dx, dy); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto out; + } + } + } + +draw_image_boxes: + status = CAIRO_STATUS_SUCCESS; + for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + cairo_box_t *b = &chunk->base[i]; + int x1 = _cairo_fixed_integer_part (b->p1.x); + int y1 = _cairo_fixed_integer_part (b->p1.y); + int x2 = _cairo_fixed_integer_part (b->p2.x); + int y2 = _cairo_fixed_integer_part (b->p2.y); + if (_cairo_xlib_surface_draw_image (dst, image, + x1 + dx, y1 + dy, + x2 - x1, y2 - y1, + x1, y1)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto out; + } + } + } + +out: + cairo_surface_destroy (&shm->base); + return status; +} + +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_xlib_surface_t *dst = _dst; + cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src; + struct _cairo_boxes_chunk *chunk; + cairo_int_status_t status; + GC gc; + Drawable d; + int i, j; + + if (! _cairo_xlib_surface_same_screen (dst, src)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (dst->depth != src->depth) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = acquire (dst); + if (unlikely (status)) + return status; + + status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc); + if (unlikely (status)) { + release (dst); + return status; + } + + if (src->fallback && src->shm->damage->dirty) { + assert (src != dst); + d = _cairo_xlib_shm_surface_get_pixmap (src->shm); + assert (d != 0); + } else { + if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); + } + d = src->drawable; + } + + if (boxes->num_boxes == 1) { + int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); + int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); + int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); + int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); + + XCopyArea (dst->dpy, d, dst->drawable, gc, + x1 + dx, y1 + dy, + x2 - x1, y2 - y1, + x1, y1); + } else { + /* We can only have a single control for subwindow_mode on the + * GC. If we have a Window destination, we need to set ClipByChildren, + * but if we have a Window source, we need IncludeInferiors. If we have + * both a Window destination and source, we must fallback. There is + * no convenient way to detect if a drawable is a Pixmap or Window, + * therefore we can only rely on those surfaces that we created + * ourselves to be Pixmaps, and treat everything else as a potential + * Window. + */ + if (src == dst || (!src->owns_pixmap && !dst->owns_pixmap)) { + 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); + XCopyArea (dst->dpy, d, dst->drawable, gc, + x1 + dx, y1 + dy, + x2 - x1, y2 - y1, + x1, y1); + } + } + } else { + XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *rects = stack_rects; + + if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + j = 0; + 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); + + rects[j].x = x1; + rects[j].y = y1; + rects[j].width = x2 - x1; + rects[j].height = y2 - y1; + j++; + } + } + assert (j == boxes->num_boxes); + + XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted); + + XCopyArea (dst->dpy, d, dst->drawable, gc, + extents->x + dx, extents->y + dy, + extents->width, extents->height, + extents->x, extents->y); + + XSetClipMask (dst->dpy, gc, None); + + if (rects != stack_rects) + free (rects); + } + } + + if (src->fallback && src->shm->damage->dirty) { + _cairo_xlib_shm_surface_mark_active (src->shm); + } else if (! src->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv); + } + + _cairo_xlib_surface_put_gc (dst->display, dst, gc); + release (dst); + return CAIRO_STATUS_SUCCESS; +} + +static int +_render_operator (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return PictOpClear; + + case CAIRO_OPERATOR_SOURCE: + return PictOpSrc; + case CAIRO_OPERATOR_OVER: + return PictOpOver; + case CAIRO_OPERATOR_IN: + return PictOpIn; + case CAIRO_OPERATOR_OUT: + return PictOpOut; + case CAIRO_OPERATOR_ATOP: + return PictOpAtop; + + case CAIRO_OPERATOR_DEST: + return PictOpDst; + case CAIRO_OPERATOR_DEST_OVER: + return PictOpOverReverse; + case CAIRO_OPERATOR_DEST_IN: + return PictOpInReverse; + case CAIRO_OPERATOR_DEST_OUT: + return PictOpOutReverse; + case CAIRO_OPERATOR_DEST_ATOP: + return PictOpAtopReverse; + + case CAIRO_OPERATOR_XOR: + return PictOpXor; + case CAIRO_OPERATOR_ADD: + return PictOpAdd; + case CAIRO_OPERATOR_SATURATE: + return PictOpSaturate; + + case CAIRO_OPERATOR_MULTIPLY: + return PictOpMultiply; + case CAIRO_OPERATOR_SCREEN: + return PictOpScreen; + case CAIRO_OPERATOR_OVERLAY: + return PictOpOverlay; + case CAIRO_OPERATOR_DARKEN: + return PictOpDarken; + case CAIRO_OPERATOR_LIGHTEN: + return PictOpLighten; + case CAIRO_OPERATOR_COLOR_DODGE: + return PictOpColorDodge; + case CAIRO_OPERATOR_COLOR_BURN: + return PictOpColorBurn; + case CAIRO_OPERATOR_HARD_LIGHT: + return PictOpHardLight; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PictOpSoftLight; + case CAIRO_OPERATOR_DIFFERENCE: + return PictOpDifference; + case CAIRO_OPERATOR_EXCLUSION: + return PictOpExclusion; + case CAIRO_OPERATOR_HSL_HUE: + return PictOpHSLHue; + case CAIRO_OPERATOR_HSL_SATURATION: + return PictOpHSLSaturation; + case CAIRO_OPERATOR_HSL_COLOR: + return PictOpHSLColor; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PictOpHSLLuminosity; + + default: + ASSERT_NOT_REACHED; + return PictOpOver; + } +} + +static cairo_bool_t +fill_reduces_to_source (cairo_operator_t op, + const cairo_color_t *color, + cairo_xlib_surface_t *dst) +{ + if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color)) { + if (op == CAIRO_OPERATOR_OVER) + return TRUE; + if (op == CAIRO_OPERATOR_ADD) + return (dst->base.content & CAIRO_CONTENT_COLOR) == 0; + } + + return FALSE; +} + +static cairo_int_status_t +fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_xlib_surface_t *dst = abstract_surface; + XRenderColor render_color; + int i; + + //X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + + if (fill_reduces_to_source (op, color, dst)) + op = CAIRO_OPERATOR_SOURCE; + + if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { + cairo_int_status_t status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (op == CAIRO_OPERATOR_SOURCE) + status = _cairo_xlib_core_fill_rectangles (dst, color, num_rects, rects); + return status; + } + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + _cairo_xlib_surface_ensure_picture (dst); + if (num_rects == 1) { + /* Take advantage of the protocol compaction that libXrender performs + * to amalgamate sequences of XRenderFillRectangle(). + */ + XRenderFillRectangle (dst->dpy, + _render_operator (op), + dst->picture, + &render_color, + rects->x, rects->y, + rects->width, rects->height); + } else { + XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *xrects = stack_xrects; + + if (num_rects > ARRAY_LENGTH (stack_xrects)) { + xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); + if (unlikely (xrects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < num_rects; i++) { + xrects[i].x = rects[i].x; + xrects[i].y = rects[i].y; + xrects[i].width = rects[i].width; + xrects[i].height = rects[i].height; + } + + XRenderFillRectangles (dst->dpy, + _render_operator (op), + dst->picture, + &render_color, xrects, num_rects); + + if (xrects != stack_xrects) + free (xrects); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +fill_boxes (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_xlib_surface_t *dst = abstract_surface; + XRenderColor render_color; + + if (fill_reduces_to_source (op, color, dst)) + op = CAIRO_OPERATOR_SOURCE; + + if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { + cairo_int_status_t status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (op == CAIRO_OPERATOR_SOURCE) + status = _cairo_xlib_core_fill_boxes (dst, color, boxes); + return status; + } + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + _cairo_xlib_surface_ensure_picture (dst); + if (boxes->num_boxes == 1) { + int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); + int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); + int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); + int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); + + /* Take advantage of the protocol compaction that libXrender performs + * to amalgamate sequences of XRenderFillRectangle(). + */ + XRenderFillRectangle (dst->dpy, + _render_operator (op), + dst->picture, + &render_color, + x1, y1, + x2 - x1, y2 - y1); + } else { + XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *xrects = stack_xrects; + struct _cairo_boxes_chunk *chunk; + int i, j; + + if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) { + xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); + if (unlikely (xrects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + j = 0; + 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); + + xrects[j].x = x1; + xrects[j].y = y1; + xrects[j].width = x2 - x1; + xrects[j].height = y2 - y1; + j++; + } + } + + XRenderFillRectangles (dst->dpy, + _render_operator (op), + dst->picture, + &render_color, xrects, j); + + if (xrects != stack_xrects) + free (xrects); + } + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 +check_composite () + operation = _categorize_composite_operation (dst, op, src_pattern, + mask_pattern != NULL); + if (operation == DO_UNSUPPORTED) + return UNSUPPORTED ("unsupported operation"); + + //X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); + + operation = _recategorize_composite_operation (dst, op, src, &src_attr, + mask_pattern != NULL); + if (operation == DO_UNSUPPORTED) { + status = UNSUPPORTED ("unsupported operation"); + goto BAIL; + } +#endif + +static cairo_int_status_t +composite (void *abstract_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_xlib_surface_t *dst = abstract_dst; + cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; + + op = _render_operator (op); + + _cairo_xlib_surface_ensure_picture (dst); + if (abstract_mask) { + cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask; + + XRenderComposite (dst->dpy, op, + src->picture, mask->picture, dst->picture, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); + } else { + XRenderComposite (dst->dpy, op, + src->picture, 0, dst->picture, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +lerp (void *abstract_dst, + 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_xlib_surface_t *dst = abstract_dst; + cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; + cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask; + + _cairo_xlib_surface_ensure_picture (dst); + XRenderComposite (dst->dpy, PictOpOutReverse, + mask->picture, None, dst->picture, + mask_x, mask_y, + 0, 0, + dst_x, dst_y, + width, height); + XRenderComposite (dst->dpy, PictOpAdd, + src->picture, mask->picture, dst->picture, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +composite_boxes (void *abstract_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_xlib_surface_t *dst = abstract_dst; + Picture src = ((cairo_xlib_source_t *)abstract_src)->picture; + Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0; + XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *rects = stack_rects; + struct _cairo_boxes_chunk *chunk; + int i, j; + + op = _render_operator (op); + _cairo_xlib_surface_ensure_picture (dst); + if (boxes->num_boxes == 1) { + int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x); + int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y); + int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x); + int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y); + + XRenderComposite (dst->dpy, op, + src, mask, dst->picture, + x1 + src_x, y1 + src_y, + x1 + mask_x, y1 + mask_y, + x1 - dst_x, y1 - dst_y, + x2 - x1, y2 - y1); + return CAIRO_STATUS_SUCCESS; + } + + if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle)); + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + j = 0; + 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); + + rects[j].x = x1 - dst_x; + rects[j].y = y1 - dst_y; + rects[j].width = x2 - x1; + rects[j].height = y2 - y1; + j++; + } + } + assert (j == boxes->num_boxes); + + XRenderSetPictureClipRectangles (dst->dpy, + dst->picture, + 0, 0, + rects, j); + if (rects != stack_rects) + free (rects); + + XRenderComposite (dst->dpy, op, + src, mask, dst->picture, + extents->x + src_x, extents->y + src_y, + extents->x + mask_x, extents->y + mask_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + set_clip_region (dst, NULL); + + return CAIRO_STATUS_SUCCESS; +} + +/* font rendering */ + +void +_cairo_xlib_font_close (cairo_xlib_font_t *priv) +{ + cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key; + int i; + + /* XXX All I really want is to do is zap my glyphs... */ + _cairo_scaled_font_reset_cache (priv->font); + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_t *info; + + info = &priv->glyphset[i]; + if (info->glyphset) + XRenderFreeGlyphSet (display->display, info->glyphset); + } + + /* XXX locking */ + cairo_list_del (&priv->link); + cairo_list_del (&priv->base.link); + free (priv); +} + +static void +_cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private, + cairo_scaled_font_t *font) +{ + cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private; + cairo_status_t status; + cairo_xlib_display_t *display; + int i; + + cairo_list_del (&priv->base.link); + cairo_list_del (&priv->link); + + status = _cairo_xlib_display_acquire (priv->device, &display); + if (unlikely (status)) /* this should be impossible but leak just in case */ + goto BAIL; + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_t *info; + + info = &priv->glyphset[i]; + if (info->glyphset) + XRenderFreeGlyphSet (display->display, info->glyphset); + } + + cairo_device_release (&display->base); +BAIL: + cairo_device_destroy (priv->device); + free (priv); +} + +static cairo_xlib_font_t * +_cairo_xlib_font_create (cairo_xlib_display_t *display, + cairo_scaled_font_t *font) +{ + cairo_xlib_font_t *priv; + int i; + + priv = _cairo_malloc (sizeof (cairo_xlib_font_t)); + if (unlikely (priv == NULL)) + return NULL; + + _cairo_scaled_font_attach_private (font, &priv->base, display, + _cairo_xlib_font_fini); + + priv->device = cairo_device_reference (&display->base); + priv->font = font; + cairo_list_add (&priv->link, &display->fonts); + + for (i = 0; i < NUM_GLYPHSETS; i++) { + cairo_xlib_font_glyphset_t *info = &priv->glyphset[i]; + switch (i) { + case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break; + case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break; + case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break; + default: ASSERT_NOT_REACHED; break; + } + info->xrender_format = NULL; + info->glyphset = None; + info->to_free.count = 0; + } + + return priv; +} + +static int +_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) +{ + if (format == CAIRO_FORMAT_A8) + return GLYPHSET_INDEX_A8; + if (format == CAIRO_FORMAT_A1) + return GLYPHSET_INDEX_A1; + + assert (format == CAIRO_FORMAT_ARGB32); + return GLYPHSET_INDEX_ARGB32; +} + +static inline cairo_xlib_font_t * +_cairo_xlib_font_get (const cairo_xlib_display_t *display, + cairo_scaled_font_t *font) +{ + return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display); +} + +typedef struct { + cairo_scaled_glyph_private_t base; + + + cairo_xlib_font_glyphset_t *glyphset; +} cairo_xlib_glyph_private_t; + +static void +_cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, + cairo_scaled_glyph_t *glyph, + cairo_scaled_font_t *font) +{ + cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private; + + if (! font->finished) { + cairo_xlib_font_t *font_private; + struct _cairo_xlib_font_glyphset_free_glyphs *to_free; + cairo_xlib_font_glyphset_t *info; + + font_private = _cairo_xlib_font_get (glyph_private->key, font); + assert (font_private); + + info = priv->glyphset; + to_free = &info->to_free; + if (to_free->count == ARRAY_LENGTH (to_free->indices)) { + cairo_xlib_display_t *display; + + if (_cairo_xlib_display_acquire (font_private->device, + &display) == CAIRO_STATUS_SUCCESS) { + XRenderFreeGlyphs (display->display, + info->glyphset, + to_free->indices, + to_free->count); + cairo_device_release (&display->base); + } + + to_free->count = 0; + } + + to_free->indices[to_free->count++] = glyph->hash_entry.hash; + } + + cairo_list_del (&glyph_private->link); + free (glyph_private); +} + +static cairo_status_t +_cairo_xlib_glyph_attach (cairo_xlib_display_t *display, + cairo_scaled_glyph_t *glyph, + cairo_xlib_font_glyphset_t *info) +{ + cairo_xlib_glyph_private_t *priv; + + priv = _cairo_malloc (sizeof (*priv)); + if (unlikely (priv == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_scaled_glyph_attach_private (glyph, &priv->base, display, + _cairo_xlib_glyph_fini); + priv->glyphset = info; + + glyph->dev_private = info; + glyph->dev_private_key = display; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_xlib_font_glyphset_t * +_cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display, + cairo_scaled_font_t *font, + cairo_format_t format) +{ + cairo_xlib_font_t *priv; + cairo_xlib_font_glyphset_t *info; + int glyphset_index; + + glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); + + priv = _cairo_xlib_font_get (display, font); + if (priv == NULL) { + priv = _cairo_xlib_font_create (display, font); + if (priv == NULL) + return NULL; + } + + info = &priv->glyphset[glyphset_index]; + if (info->glyphset == None) { + info->xrender_format = + _cairo_xlib_display_get_xrender_format (display, info->format); + info->glyphset = XRenderCreateGlyphSet (display->display, + info->xrender_format); + } + + return info; +} + +static cairo_bool_t +has_pending_free_glyph (cairo_xlib_font_glyphset_t *info, + unsigned long glyph_index) +{ + struct _cairo_xlib_font_glyphset_free_glyphs *to_free; + int i; + + to_free = &info->to_free; + for (i = 0; i < to_free->count; i++) { + if (to_free->indices[i] == glyph_index) { + to_free->count--; + memmove (&to_free->indices[i], + &to_free->indices[i+1], + (to_free->count - i) * sizeof (to_free->indices[0])); + return TRUE; + } + } + + return FALSE; +} + +static cairo_xlib_font_glyphset_t * +find_pending_free_glyph (cairo_xlib_display_t *display, + cairo_scaled_font_t *font, + unsigned long glyph_index, + cairo_image_surface_t *surface) +{ + cairo_xlib_font_t *priv; + int i; + + priv = _cairo_xlib_font_get (display, font); + if (priv == NULL) + return NULL; + + if (surface != NULL) { + i = _cairo_xlib_get_glyphset_index_for_format (surface->format); + if (has_pending_free_glyph (&priv->glyphset[i], glyph_index)) + return &priv->glyphset[i]; + } else { + for (i = 0; i < NUM_GLYPHSETS; i++) { + if (has_pending_free_glyph (&priv->glyphset[i], glyph_index)) + return &priv->glyphset[i]; + } + } + + return NULL; +} + +static cairo_status_t +_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, + cairo_scaled_font_t *font, + cairo_scaled_glyph_t **pscaled_glyph) +{ + XGlyphInfo glyph_info; + unsigned long glyph_index; + unsigned char *data; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *glyph = *pscaled_glyph; + cairo_image_surface_t *glyph_surface = glyph->surface; + cairo_bool_t already_had_glyph_surface; + cairo_xlib_font_glyphset_t *info; + + glyph_index = glyph->hash_entry.hash; + + /* check to see if we have a pending XRenderFreeGlyph for this glyph */ + info = find_pending_free_glyph (display, font, glyph_index, glyph_surface); + if (info != NULL) + return _cairo_xlib_glyph_attach (display, glyph, info); + + if (glyph_surface == NULL) { + status = _cairo_scaled_glyph_lookup (font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + pscaled_glyph); + if (unlikely (status)) + return status; + + glyph = *pscaled_glyph; + glyph_surface = glyph->surface; + already_had_glyph_surface = FALSE; + } else { + already_had_glyph_surface = TRUE; + } + + info = _cairo_xlib_font_get_glyphset_info_for_format (display, font, + glyph_surface->format); + +#if 0 + /* If the glyph surface has zero height or width, we create + * a clear 1x1 surface, to avoid various X server bugs. + */ + if (glyph_surface->width == 0 || glyph_surface->height == 0) { + cairo_surface_t *tmp_surface; + + tmp_surface = cairo_image_surface_create (info->format, 1, 1); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; + + tmp_surface->device_transform = glyph_surface->base.device_transform; + tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + + glyph_surface = (cairo_image_surface_t *) tmp_surface; + } +#endif + + /* If the glyph format does not match the font format, then we + * create a temporary surface for the glyph image with the font's + * format. + */ + if (glyph_surface->format != info->format) { + cairo_surface_pattern_t pattern; + cairo_surface_t *tmp_surface; + + tmp_surface = cairo_image_surface_create (info->format, + glyph_surface->width, + glyph_surface->height); + status = tmp_surface->status; + if (unlikely (status)) + goto BAIL; + + tmp_surface->device_transform = glyph_surface->base.device_transform; + tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; + + _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); + status = _cairo_surface_paint (tmp_surface, + CAIRO_OPERATOR_SOURCE, &pattern.base, + NULL); + _cairo_pattern_fini (&pattern.base); + + glyph_surface = (cairo_image_surface_t *) tmp_surface; + + if (unlikely (status)) + goto BAIL; + } + + /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ + glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); + glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); + glyph_info.width = glyph_surface->width; + glyph_info.height = glyph_surface->height; + glyph_info.xOff = glyph->x_advance; + glyph_info.yOff = glyph->y_advance; + + data = glyph_surface->data; + + /* flip formats around */ + switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) { + case GLYPHSET_INDEX_A1: + /* local bitmaps are always stored with bit == byte */ + if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) { + int c = glyph_surface->stride * glyph_surface->height; + unsigned char *d; + unsigned char *new, *n; + + if (c == 0) + break; + + new = _cairo_malloc (c); + if (!new) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + n = new; + d = data; + do { + char b = *d++; + b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); + b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); + b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); + *n++ = b; + } while (--c); + data = new; + } + break; + case GLYPHSET_INDEX_A8: + break; + case GLYPHSET_INDEX_ARGB32: + if (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) { + unsigned int c = glyph_surface->stride * glyph_surface->height / 4; + const uint32_t *d; + uint32_t *new, *n; + + if (c == 0) + break; + + new = _cairo_malloc (4 * c); + if (unlikely (new == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + n = new; + d = (uint32_t *) data; + do { + *n++ = bswap_32 (*d); + d++; + } while (--c); + data = (uint8_t *) new; + } + break; + default: + ASSERT_NOT_REACHED; + break; + } + /* XXX assume X server wants pixman padding. Xft assumes this as well */ + + XRenderAddGlyphs (display->display, info->glyphset, + &glyph_index, &glyph_info, 1, + (char *) data, + glyph_surface->stride * glyph_surface->height); + + if (data != glyph_surface->data) + free (data); + + status = _cairo_xlib_glyph_attach (display, glyph, info); + + BAIL: + if (glyph_surface != glyph->surface) + cairo_surface_destroy (&glyph_surface->base); + + /* if the scaled glyph didn't already have a surface attached + * to it, release the created surface now that we have it + * uploaded to the X server. If the surface has already been + * there (eg. because image backend requested it), leave it in + * the cache + */ + if (!already_had_glyph_surface) + _cairo_scaled_glyph_set_surface (glyph, font, NULL); + + return status; +} + +typedef void (*cairo_xrender_composite_text_func_t) + (Display *dpy, + int op, + Picture src, + Picture dst, + _Xconst XRenderPictFormat *maskFormat, + int xSrc, + int ySrc, + int xDst, + int yDst, + _Xconst XGlyphElt8 *elts, + int nelt); + +/* Build a struct of the same size of #cairo_glyph_t that can be used both as + * an input glyph with double coordinates, and as "working" glyph with + * integer from-current-point offsets. */ +typedef union { + cairo_glyph_t d; + unsigned long index; + struct { + unsigned long index; + int x; + int y; + } i; +} cairo_xlib_glyph_t; + +/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ +COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); + +/* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs + * (Xrender limits each element to 252 glyphs, we limit them to 128) + * + * These same conditions need to be mirrored between + * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks + */ +#define _start_new_glyph_elt(count, glyph) \ + (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) + +static cairo_status_t +_emit_glyphs_chunk (cairo_xlib_display_t *display, + cairo_xlib_surface_t *dst, + int dst_x, int dst_y, + cairo_xlib_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *font, + cairo_bool_t use_mask, + cairo_operator_t op, + cairo_xlib_source_t *src, + int src_x, int src_y, + /* info for this chunk */ + int num_elts, + int width, + cairo_xlib_font_glyphset_t *info) +{ + /* Which XRenderCompositeText function to use */ + cairo_xrender_composite_text_func_t composite_text_func; + int size; + + /* Element buffer stuff */ + XGlyphElt8 *elts; + XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; + + /* Reuse the input glyph array for output char generation */ + char *char8 = (char *) glyphs; + unsigned short *char16 = (unsigned short *) glyphs; + unsigned int *char32 = (unsigned int *) glyphs; + + int i; + int nelt; /* Element index */ + int n; /* Num output glyphs in current element */ + int j; /* Num output glyphs so far */ + + switch (width) { + case 1: + /* don't cast the 8-variant, to catch possible mismatches */ + composite_text_func = XRenderCompositeText8; + size = sizeof (char); + break; + case 2: + composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; + size = sizeof (unsigned short); + break; + default: + case 4: + composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; + size = sizeof (unsigned int); + } + + /* Allocate element array */ + if (num_elts <= ARRAY_LENGTH (stack_elts)) { + elts = stack_elts; + } else { + elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); + if (unlikely (elts == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* Fill them in */ + nelt = 0; + n = 0; + j = 0; + for (i = 0; i < num_glyphs; i++) { + /* Start a new element for first output glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs. + * + * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() + */ + if (_start_new_glyph_elt (j, &glyphs[i])) { + if (j) { + elts[nelt].nchars = n; + nelt++; + n = 0; + } + elts[nelt].chars = char8 + size * j; + elts[nelt].glyphset = info->glyphset; + elts[nelt].xOff = glyphs[i].i.x; + elts[nelt].yOff = glyphs[i].i.y; + } + + switch (width) { + case 1: char8 [j] = (char) glyphs[i].index; break; + case 2: char16[j] = (unsigned short) glyphs[i].index; break; + default: + case 4: char32[j] = (unsigned int) glyphs[i].index; break; + } + + n++; + j++; + } + + if (n) { + elts[nelt].nchars = n; + nelt++; + } + + /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the + * expected number of xGlyphElts. */ + assert (nelt == num_elts); + + composite_text_func (display->display, op, + src->picture, + dst->picture, + use_mask ? info->xrender_format : NULL, + src_x + elts[0].xOff + dst_x, + src_y + elts[0].yOff + dst_y, + elts[0].xOff, elts[0].yOff, + (XGlyphElt8 *) elts, nelt); + + if (elts != stack_elts) + free (elts); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +check_composite_glyphs (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *font, + cairo_glyph_t *glyphs, + int *num_glyphs) +{ + cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface; + cairo_xlib_display_t *display = dst->display; + int max_request_size, size; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* The glyph coordinates must be representable in an int16_t. + * When possible, they will be expressed as an offset from the + * previous glyph, otherwise they will be an offset from the + * surface origin. If we can't guarantee this to be possible, + * fallback. + */ + if (extents->bounded.x + extents->bounded.width > INT16_MAX || + extents->bounded.y + extents->bounded.height> INT16_MAX || + extents->bounded.x < INT16_MIN || + extents->bounded.y < INT16_MIN) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Approximate the size of the largest glyph and fallback if we can not + * upload it to the xserver. + */ + size = ceil (font->max_scale); + size = 4 * size * size; + max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) + : XMaxRequestSize (display->display)) * 4 - + sz_xRenderAddGlyphsReq - + sz_xGlyphInfo - + 8; + if (size >= max_request_size) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have + * enough room for padding */ +#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) + +#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) +#define POSITION(x) ((int) floor (x + 0.125)) + +static cairo_int_status_t +composite_glyphs (void *surface, + 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_xlib_surface_t *dst = surface; + cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs; + cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src; + cairo_xlib_display_t *display = dst->display; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + cairo_scaled_glyph_t *glyph; + cairo_fixed_t x = dst_x, y = dst_y; + cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info; + + unsigned long max_index = 0; + int width = 1; + int num_elts = 0; + int num_out_glyphs = 0; + int num_glyphs = info->num_glyphs; + + int max_request_size = XMaxRequestSize (display->display) * 4 + - MAX (sz_xRenderCompositeGlyphs8Req, + MAX(sz_xRenderCompositeGlyphs16Req, + sz_xRenderCompositeGlyphs32Req)); + int request_size = 0; + int i; + + op = _render_operator (op), + _cairo_xlib_surface_ensure_picture (dst); + for (i = 0; i < num_glyphs; i++) { + unsigned long xphase, yphase; + int this_x, this_y; + int old_width; + + xphase = PHASE(glyphs[i].d.x); + yphase = PHASE(glyphs[i].d.y); + + glyphs[i].index |= (xphase << 24) | (yphase << 26); + + status = _cairo_scaled_glyph_lookup (info->font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &glyph); + if (unlikely (status)) + return status; + + this_x = POSITION (glyphs[i].d.x); + this_y = POSITION (glyphs[i].d.y); + + /* Send unsent glyphs to the server */ + if (glyph->dev_private_key != display) { + status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph); + if (unlikely (status)) + return status; + } + + this_glyphset_info = glyph->dev_private; + if (!glyphset) + glyphset = this_glyphset_info; + + /* The invariant here is that we can always flush the glyphs + * accumulated before this one, using old_width, and they + * would fit in the request. + */ + old_width = width; + + /* Update max glyph index */ + if (glyphs[i].index > max_index) { + max_index = glyphs[i].index; + if (max_index >= 65536) + width = 4; + else if (max_index >= 256) + width = 2; + if (width != old_width) + request_size += (width - old_width) * num_out_glyphs; + } + + /* If we will pass the max request size by adding this glyph, + * flush current glyphs. Note that we account for a + * possible element being added below. + * + * Also flush if changing glyphsets, as Xrender limits one mask + * format per request, so we can either break up, or use a + * wide-enough mask format. We do the former. One reason to + * prefer the latter is the fact that Xserver ADDs all glyphs + * to the mask first, and then composes that to final surface, + * though it's not a big deal. + * + * If the glyph has a coordinate which cannot be represented + * as a 16-bit offset from the previous glyph, flush the + * current chunk. The current glyph will be the first one in + * the next chunk, thus its coordinates will be an offset from + * the destination origin. This offset is guaranteed to be + * representable as 16-bit offset (otherwise we would have + * fallen back). + */ + if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || + this_x - x > INT16_MAX || this_x - x < INT16_MIN || + this_y - y > INT16_MAX || this_y - y < INT16_MIN || + (this_glyphset_info != glyphset)) { + status = _emit_glyphs_chunk (display, dst, dst_x, dst_y, + glyphs, i, info->font, info->use_mask, + op, src, src_x, src_y, + num_elts, old_width, glyphset); + if (unlikely (status)) + return status; + + glyphs += i; + num_glyphs -= i; + i = 0; + max_index = glyphs[i].index; + width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; + request_size = 0; + num_elts = 0; + num_out_glyphs = 0; + x = y = 0; + glyphset = this_glyphset_info; + } + + /* Convert absolute glyph position to relative-to-current-point + * position */ + glyphs[i].i.x = this_x - x; + glyphs[i].i.y = this_y - y; + + /* Start a new element for the first glyph, + * or for any glyph that has unexpected position, + * or if current element has too many glyphs. + * + * These same conditions are mirrored in _emit_glyphs_chunk(). + */ + if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { + num_elts++; + request_size += _cairo_sz_xGlyphElt; + } + + /* adjust current-position */ + x = this_x + glyph->x_advance; + y = this_y + glyph->y_advance; + + num_out_glyphs++; + request_size += width; + } + + if (num_elts) { + status = _emit_glyphs_chunk (display, dst, dst_x, dst_y, + glyphs, i, info->font, info->use_mask, + op, src, src_x, src_y, + num_elts, width, glyphset); + } + + return status; +} + +const cairo_compositor_t * +_cairo_xlib_mask_compositor_get (void) +{ + static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; + static cairo_mask_compositor_t compositor; + + if (_cairo_atomic_init_once_enter(&once)) { + _cairo_mask_compositor_init (&compositor, + _cairo_xlib_fallback_compositor_get ()); + + compositor.acquire = acquire; + compositor.release = release; + compositor.set_clip_region = set_clip_region; + compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern; + compositor.draw_image_boxes = draw_image_boxes; + compositor.fill_rectangles = fill_rectangles; + compositor.fill_boxes = fill_boxes; + compositor.copy_boxes = copy_boxes; + compositor.check_composite = check_composite; + compositor.composite = composite; + //compositor.check_composite_boxes = check_composite_boxes; + compositor.composite_boxes = composite_boxes; + compositor.check_composite_glyphs = check_composite_glyphs; + compositor.composite_glyphs = composite_glyphs; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} + +#define CAIRO_FIXED_16_16_MIN -32768 +#define CAIRO_FIXED_16_16_MAX 32767 + +static cairo_bool_t +line_exceeds_16_16 (const cairo_line_t *line) +{ + return + line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || + line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || + line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); +} + +static void +project_line_x_onto_16_16 (const cairo_line_t *line, + cairo_fixed_t top, + cairo_fixed_t bottom, + XLineFixed *out) +{ + cairo_point_double_t p1, p2; + double m; + + p1.x = _cairo_fixed_to_double (line->p1.x); + p1.y = _cairo_fixed_to_double (line->p1.y); + + p2.x = _cairo_fixed_to_double (line->p2.x); + p2.y = _cairo_fixed_to_double (line->p2.y); + + m = (p2.x - p1.x) / (p2.y - p1.y); + out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); + out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); +} +#if 0 +static cairo_int_status_T +check_composite_trapezoids () +{ + operation = _categorize_composite_operation (dst, op, pattern, TRUE); + if (operation == DO_UNSUPPORTED) + return UNSUPPORTED ("unsupported operation"); + + operation = _recategorize_composite_operation (dst, op, src, + &attributes, TRUE); + if (operation == DO_UNSUPPORTED) { + status = UNSUPPORTED ("unsupported operation"); + goto BAIL; + } + +} +#endif + +static cairo_int_status_t +composite_traps (void *abstract_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_xlib_surface_t *dst = abstract_dst; + cairo_xlib_display_t *display = dst->display; + cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; + XRenderPictFormat *pict_format; + XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; + XTrapezoid *xtraps = xtraps_stack; + int dx, dy; + int i; + + //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); + + if (traps->num_traps == 0) + return CAIRO_STATUS_SUCCESS; + + if (dst->base.is_clear && + (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) + { + op = CAIRO_OPERATOR_SOURCE; + } + + pict_format = + _cairo_xlib_display_get_xrender_format (display, + antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); + + if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) { + xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid)); + if (unlikely (xtraps == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + dx = -dst_x << 16; + dy = -dst_y << 16; + for (i = 0; i < traps->num_traps; i++) { + cairo_trapezoid_t *t = &traps->traps[i]; + + /* top/bottom will be clamped to surface bounds */ + xtraps[i].top = _cairo_fixed_to_16_16(t->top) + dy; + xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy; + + /* However, all the other coordinates will have been left untouched so + * as not to introduce numerical error. Recompute them if they + * exceed the 16.16 limits. + */ + if (unlikely (line_exceeds_16_16 (&t->left))) { + project_line_x_onto_16_16 (&t->left, t->top, t->bottom, + &xtraps[i].left); + xtraps[i].left.p1.x += dx; + xtraps[i].left.p2.x += dx; + xtraps[i].left.p1.y = xtraps[i].top; + xtraps[i].left.p2.y = xtraps[i].bottom; + } else { + xtraps[i].left.p1.x = _cairo_fixed_to_16_16(t->left.p1.x) + dx; + xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy; + xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx; + xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy; + } + + if (unlikely (line_exceeds_16_16 (&t->right))) { + project_line_x_onto_16_16 (&t->right, t->top, t->bottom, + &xtraps[i].right); + xtraps[i].right.p1.x += dx; + xtraps[i].right.p2.x += dx; + xtraps[i].right.p1.y = xtraps[i].top; + xtraps[i].right.p2.y = xtraps[i].bottom; + } else { + xtraps[i].right.p1.x = _cairo_fixed_to_16_16(t->right.p1.x) + dx; + xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy; + xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx; + xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy; + } + } + + if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { + src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); + src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); + } else { + src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); + src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); + } + src_x += dst_x; + src_y += dst_y; + + _cairo_xlib_surface_ensure_picture (dst); + _cairo_xlib_surface_set_precision (dst, antialias); + XRenderCompositeTrapezoids (dst->dpy, + _render_operator (op), + src->picture, dst->picture, + pict_format, + src_x, src_y, + xtraps, traps->num_traps); + + if (xtraps != xtraps_stack) + free (xtraps); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +composite_tristrip (void *abstract_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_xlib_surface_t *dst = abstract_dst; + cairo_xlib_display_t *display = dst->display; + cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src; + XRenderPictFormat *pict_format; + XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)]; + XPointFixed *points = points_stack; + int dx, dy; + int i; + + //X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); + + pict_format = + _cairo_xlib_display_get_xrender_format (display, + antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); + + if (strip->num_points > ARRAY_LENGTH (points_stack)) { + points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed)); + if (unlikely (points == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + dx = -dst_x << 16; + dy = -dst_y << 16; + for (i = 0; i < strip->num_points; i++) { + cairo_point_t *p = &strip->points[i]; + + points[i].x = _cairo_fixed_to_16_16(p->x) + dx; + points[i].y = _cairo_fixed_to_16_16(p->y) + dy; + } + + src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x; + src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y; + + _cairo_xlib_surface_ensure_picture (dst); + _cairo_xlib_surface_set_precision (dst, antialias); + XRenderCompositeTriStrip (dst->dpy, + _render_operator (op), + src->picture, dst->picture, + pict_format, + src_x, src_y, + points, strip->num_points); + + if (points != points_stack) + free (points); + + return CAIRO_STATUS_SUCCESS; +} + +const cairo_compositor_t * +_cairo_xlib_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_xlib_mask_compositor_get ()); + + compositor.acquire = acquire; + compositor.release = release; + compositor.set_clip_region = set_clip_region; + compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern; + 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_tristrip; + compositor.composite_tristrip = composite_tristrip; + compositor.check_composite_glyphs = check_composite_glyphs; + compositor.composite_glyphs = composite_glyphs; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-screen.c b/gfx/cairo/cairo/src/cairo-xlib-screen.c index 0a7824498988..ff3bdfcf9e1a 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-screen.c +++ b/gfx/cairo/cairo/src/cairo-xlib-screen.c @@ -54,11 +54,14 @@ #include "cairoint.h" +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + #include "cairo-xlib-private.h" #include "cairo-xlib-xrender-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-error-private.h" +#include "cairo-list-inline.h" #include "cairo-fontconfig-private.h" @@ -262,29 +265,35 @@ _cairo_xlib_init_screen_font_options (Display *dpy, cairo_font_options_set_hint_style (&info->font_options, hint_style); cairo_font_options_set_antialias (&info->font_options, antialias); cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order); - cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter); + _cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter); cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); } void -_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info) +_cairo_xlib_screen_destroy (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info) { Display *dpy; int i; dpy = display->display; - for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if ((info->gc_depths >> (8*i)) & 0xff) - XFreeGC (dpy, info->gc[i]); - } - info->gc_depths = 0; -} + while (! cairo_list_is_empty (&info->surfaces)) { + cairo_xlib_surface_t *surface; + + surface = cairo_list_first_entry (&info->surfaces, + cairo_xlib_surface_t, + link); + cairo_surface_finish (&surface->base); + } + + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (info->gc_depths[i] != 0) { + XFreeGC (dpy, info->gc[i]); + info->gc_depths[i] = 0; + } + } -void -_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info) -{ while (! cairo_list_is_empty (&info->visuals)) { _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals, cairo_xlib_visual_info_t, @@ -321,7 +330,7 @@ _cairo_xlib_screen_get (Display *dpy, goto CLEANUP_DISPLAY; } - info = malloc (sizeof (cairo_xlib_screen_t)); + info = _cairo_malloc (sizeof (cairo_xlib_screen_t)); if (unlikely (info == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_DISPLAY; @@ -330,9 +339,10 @@ _cairo_xlib_screen_get (Display *dpy, info->device = device; info->screen = screen; info->has_font_options = FALSE; - info->gc_depths = 0; + memset (info->gc_depths, 0, sizeof (info->gc_depths)); memset (info->gc, 0, sizeof (info->gc)); + cairo_list_init (&info->surfaces); cairo_list_init (&info->visuals); cairo_list_add (&info->link, &display->screens); @@ -356,8 +366,8 @@ _cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, int i; for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if (((info->gc_depths >> (8*i)) & 0xff) == depth) { - info->gc_depths &= ~(0xff << (8*i)); + if (info->gc_depths[i] == depth) { + info->gc_depths[i] = 0; gc = info->gc[i]; break; } @@ -385,29 +395,18 @@ _cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, int i; for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { - if (((info->gc_depths >> (8*i)) & 0xff) == 0) + if (info->gc_depths[i] == 0) break; } if (i == ARRAY_LENGTH (info->gc)) { - cairo_status_t status; - /* perform random substitution to ensure fair caching over depths */ i = rand () % ARRAY_LENGTH (info->gc); - status = - _cairo_xlib_display_queue_work (display, - (cairo_xlib_notify_func) XFreeGC, - info->gc[i], - NULL); - if (unlikely (status)) { - /* leak the server side resource... */ - XFree ((char *) info->gc[i]); - } + XFreeGC(display->display, info->gc[i]); } info->gc[i] = gc; - info->gc_depths &= ~(0xff << (8*i)); - info->gc_depths |= depth << (8*i); + info->gc_depths[i] = depth; } cairo_status_t @@ -464,3 +463,5 @@ _cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) return &info->font_options; } + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-source.c b/gfx/cairo/cairo/src/cairo-xlib-source.c new file mode 100644 index 000000000000..4c3b99d9e7db --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-source.c @@ -0,0 +1,1171 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ +#include "cairoint.h" + +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib-private.h" +#include "cairo-xlib-surface-private.h" + +#include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-inline.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-backend-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-surface-observer-private.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-subsurface-inline.h" + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +static cairo_xlib_surface_t * +unwrap_source (const cairo_surface_pattern_t *pattern) +{ + cairo_rectangle_int_t limits; + return (cairo_xlib_surface_t *)_cairo_pattern_get_source (pattern, &limits); +} + +static cairo_status_t +_cairo_xlib_source_finish (void *abstract_surface) +{ + cairo_xlib_source_t *source = abstract_surface; + + XRenderFreePicture (source->dpy, source->picture); + if (source->pixmap) + XFreePixmap (source->dpy, source->pixmap); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_xlib_source_backend = { + CAIRO_SURFACE_TYPE_XLIB, + _cairo_xlib_source_finish, + NULL, /* read-only wrapper */ +}; + +static cairo_status_t +_cairo_xlib_proxy_finish (void *abstract_surface) +{ + cairo_xlib_proxy_t *proxy = abstract_surface; + + _cairo_xlib_shm_surface_mark_active (proxy->owner); + XRenderFreePicture (proxy->source.dpy, proxy->source.picture); + if (proxy->source.pixmap) + XFreePixmap (proxy->source.dpy, proxy->source.pixmap); + cairo_surface_destroy (proxy->owner); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_xlib_proxy_backend = { + CAIRO_SURFACE_TYPE_XLIB, + _cairo_xlib_proxy_finish, + NULL, /* read-only wrapper */ +}; + +static cairo_surface_t * +source (cairo_xlib_surface_t *dst, Picture picture, Pixmap pixmap) +{ + cairo_xlib_source_t *source; + + if (picture == None) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + source = _cairo_malloc (sizeof (*source)); + if (unlikely (source == NULL)) { + XRenderFreePicture (dst->display->display, picture); + if (pixmap) + XFreePixmap (dst->display->display, pixmap); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&source->base, + &cairo_xlib_source_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + FALSE); /* is_vector */ + + /* The source exists only within an operation */ + source->picture = picture; + source->pixmap = pixmap; + source->dpy = dst->display->display; + + return &source->base; +} + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static const XTransform identity = { + { + { 1 << 16, 0x00000, 0x00000 }, + { 0x00000, 1 << 16, 0x00000 }, + { 0x00000, 0x00000, 1 << 16 }, + } +}; + +static cairo_bool_t +picture_set_matrix (cairo_xlib_display_t *display, + Picture picture, + const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, + double yc, + int *x_offset, + int *y_offset) +{ + XTransform xtransform; + pixman_transform_t *pixman_transform; + cairo_int_status_t status; + + /* Casting between pixman_transform_t and XTransform is safe because + * they happen to be the exact same type. + */ + pixman_transform = (pixman_transform_t *) &xtransform; + status = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, + pixman_transform, + x_offset, y_offset); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return TRUE; + if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) + return FALSE; + + if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0) + return TRUE; + + /* a late check in case we perturb the matrix too far */ + if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) + return FALSE; + + XRenderSetPictureTransform (display->display, picture, &xtransform); + return TRUE; +} + +static cairo_status_t +picture_set_filter (Display *dpy, + Picture picture, + cairo_filter_t filter) +{ + const char *render_filter; + + switch (filter) { + case CAIRO_FILTER_FAST: + render_filter = FilterFast; + break; + case CAIRO_FILTER_GOOD: + render_filter = FilterGood; + break; + case CAIRO_FILTER_BEST: + render_filter = FilterBest; + break; + case CAIRO_FILTER_NEAREST: + render_filter = FilterNearest; + break; + case CAIRO_FILTER_BILINEAR: + render_filter = FilterBilinear; + break; + case CAIRO_FILTER_GAUSSIAN: + /* XXX: The GAUSSIAN value has no implementation in cairo + * whatsoever, so it was really a mistake to have it in the + * API. We could fix this by officially deprecating it, or + * else inventing semantics and providing an actual + * implementation for it. */ + default: + render_filter = FilterBest; + break; + } + + XRenderSetPictureFilter (dpy, picture, (char *) render_filter, NULL, 0); + return CAIRO_STATUS_SUCCESS; +} + +static int +extend_to_repeat (cairo_extend_t extend) +{ + switch (extend) { + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_NONE: + return RepeatNone; + case CAIRO_EXTEND_REPEAT: + return RepeatNormal; + case CAIRO_EXTEND_REFLECT: + return RepeatReflect; + case CAIRO_EXTEND_PAD: + return RepeatPad; + } +} + +static cairo_bool_t +picture_set_properties (cairo_xlib_display_t *display, + Picture picture, + const cairo_pattern_t *pattern, + const cairo_matrix_t *matrix, + const cairo_rectangle_int_t *extents, + int *x_off, int *y_off) +{ + XRenderPictureAttributes pa; + int mask = 0; + + if (! picture_set_matrix (display, picture, matrix, pattern->filter, + extents->x + extents->width / 2, + extents->y + extents->height / 2, + x_off, y_off)) + return FALSE; + + picture_set_filter (display->display, picture, pattern->filter); + + if (pattern->has_component_alpha) { + pa.component_alpha = 1; + mask |= CPComponentAlpha; + } + + if (pattern->extend != CAIRO_EXTEND_NONE) { + pa.repeat = extend_to_repeat (pattern->extend); + mask |= CPRepeat; + } + + if (mask) + XRenderChangePicture (display->display, picture, mask, &pa); + + return TRUE; +} + +static cairo_surface_t * +render_pattern (cairo_xlib_surface_t *dst, + const cairo_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + int *src_x, int *src_y) +{ + Display *dpy = dst->display->display; + cairo_xlib_surface_t *src; + cairo_image_surface_t *image; + cairo_status_t status; + cairo_rectangle_int_t map_extents; + + src = (cairo_xlib_surface_t *) + _cairo_surface_create_scratch (&dst->base, + is_mask ? CAIRO_CONTENT_ALPHA : CAIRO_CONTENT_COLOR_ALPHA, + extents->width, + extents->height, + NULL); + if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + map_extents = *extents; + map_extents.x = map_extents.y = 0; + + image = _cairo_surface_map_to_image (&src->base, &map_extents); + status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y, + CAIRO_OPERATOR_SOURCE, pattern, + NULL); + status = _cairo_surface_unmap_image (&src->base, image); + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (status); + } + + status = _cairo_xlib_surface_put_shm (src); + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (status); + } + + src->picture = XRenderCreatePicture (dpy, + src->drawable, src->xrender_format, + 0, NULL); + + *src_x = -extents->x; + *src_y = -extents->y; + return &src->base; +} + +static cairo_surface_t * +gradient_source (cairo_xlib_surface_t *dst, + const cairo_gradient_pattern_t *gradient, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + int *src_x, int *src_y) +{ + cairo_xlib_display_t *display = dst->display; + cairo_matrix_t matrix = gradient->base.matrix; + char buf[CAIRO_STACK_BUFFER_SIZE]; + cairo_circle_double_t extremes[2]; + XFixed *stops; + XRenderColor *colors; + Picture picture; + unsigned int i, n_stops; + + /* The RENDER specification says that the inner circle has + * to be completely contained inside the outer one. */ + if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL && + ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) gradient)) + return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y); + + assert (gradient->n_stops > 0); + n_stops = MAX (gradient->n_stops, 2); + + if (n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) + { + stops = (XFixed *) buf; + } + else + { + stops = + _cairo_malloc_ab (n_stops, + sizeof (XFixed) + sizeof (XRenderColor)); + if (unlikely (stops == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + colors = (XRenderColor *) (stops + n_stops); + for (i = 0; i < gradient->n_stops; i++) { + stops[i] = + _cairo_fixed_16_16_from_double (gradient->stops[i].offset); + + colors[i].red = gradient->stops[i].color.red_short; + colors[i].green = gradient->stops[i].color.green_short; + colors[i].blue = gradient->stops[i].color.blue_short; + colors[i].alpha = gradient->stops[i].color.alpha_short; + } + + /* RENDER does not support gradients with less than 2 + * stops. If a gradient has only a single stop, duplicate + * it to make RENDER happy. */ + if (gradient->n_stops == 1) { + stops[1] = + _cairo_fixed_16_16_from_double (gradient->stops[0].offset); + + colors[1].red = gradient->stops[0].color.red_short; + colors[1].green = gradient->stops[0].color.green_short; + colors[1].blue = gradient->stops[0].color.blue_short; + colors[1].alpha = gradient->stops[0].color.alpha_short; + } + +#if 0 + /* For some weird reason the X server is sometimes getting + * CreateGradient requests with bad length. So far I've only seen + * XRenderCreateLinearGradient request with 4 stops sometime end up + * with length field matching 0 stops at the server side. I've + * looked at the libXrender code and I can't see anything that + * could cause this behavior. However, for some reason having a + * XSync call here seems to avoid the issue so I'll keep it here + * until it's solved. + */ + XSync (display->display, False); +#endif + + _cairo_gradient_pattern_fit_to_range (gradient, PIXMAN_MAX_INT >> 1, &matrix, extremes); + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + XLinearGradient grad; + + grad.p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); + grad.p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); + grad.p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); + grad.p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); + + picture = XRenderCreateLinearGradient (display->display, &grad, + stops, colors, + n_stops); + } else { + XRadialGradient grad; + + grad.inner.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); + grad.inner.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); + grad.inner.radius = _cairo_fixed_16_16_from_double (extremes[0].radius); + grad.outer.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); + grad.outer.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); + grad.outer.radius = _cairo_fixed_16_16_from_double (extremes[1].radius); + + picture = XRenderCreateRadialGradient (display->display, &grad, + stops, colors, + n_stops); + } + + if (stops != (XFixed *) buf) + free (stops); + + *src_x = *src_y = 0; + if (! picture_set_properties (display, picture, + &gradient->base, &gradient->base.matrix, + extents, + src_x, src_y)) { + XRenderFreePicture (display->display, picture); + return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y); + } + + return source (dst, picture, None); +} + +static cairo_surface_t * +color_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) +{ + Display *dpy = dst->display->display; + XRenderColor xcolor; + Picture picture; + Pixmap pixmap = None; + + xcolor.red = color->red_short; + xcolor.green = color->green_short; + xcolor.blue = color->blue_short; + xcolor.alpha = color->alpha_short; + + if (CAIRO_RENDER_HAS_GRADIENTS(dst->display)) { + picture = XRenderCreateSolidFill (dpy, &xcolor); + } else { + XRenderPictureAttributes pa; + int mask = 0; + + pa.repeat = RepeatNormal; + mask |= CPRepeat; + + pixmap = XCreatePixmap (dpy, dst->drawable, 1, 1, 32); + picture = XRenderCreatePicture (dpy, pixmap, + _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_ARGB32), + mask, &pa); + + if (CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { + XRectangle r = { 0, 0, 1, 1}; + XRenderFillRectangles (dpy, PictOpSrc, picture, &xcolor, &r, 1); + } else { + XGCValues gcv; + GC gc; + + gc = _cairo_xlib_screen_get_gc (dst->display, dst->screen, + 32, pixmap); + if (unlikely (gc == NULL)) { + XFreePixmap (dpy, pixmap); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + gcv.foreground = 0; + gcv.foreground |= (uint32_t)color->alpha_short >> 8 << 24; + gcv.foreground |= color->red_short >> 8 << 16; + gcv.foreground |= color->green_short >> 8 << 8; + gcv.foreground |= color->blue_short >> 8 << 0; + gcv.fill_style = FillSolid; + + XChangeGC (dpy, gc, GCFillStyle | GCForeground, &gcv); + XFillRectangle (dpy, pixmap, gc, 0, 0, 1, 1); + + _cairo_xlib_screen_put_gc (dst->display, dst->screen, 32, gc); + } + } + + return source (dst, picture, pixmap); +} + +static cairo_surface_t * +alpha_source (cairo_xlib_surface_t *dst, uint8_t alpha) +{ + cairo_xlib_display_t *display = dst->display; + + if (display->alpha[alpha] == NULL) { + cairo_color_t color; + + color.red_short = color.green_short = color.blue_short = 0; + color.alpha_short = alpha << 8 | alpha; + + display->alpha[alpha] = color_source (dst, &color); + } + + return cairo_surface_reference (display->alpha[alpha]); +} + +static cairo_surface_t * +white_source (cairo_xlib_surface_t *dst) +{ + cairo_xlib_display_t *display = dst->display; + + if (display->white == NULL) + display->white = color_source (dst, CAIRO_COLOR_WHITE); + + return cairo_surface_reference (display->white); +} + +static cairo_surface_t * +opaque_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) +{ + cairo_xlib_display_t *display = dst->display; + uint32_t pixel = + 0xff000000 | + color->red_short >> 8 << 16 | + color->green_short >> 8 << 8 | + color->blue_short >> 8 << 0; + int i; + + if (display->last_solid_cache[0].color == pixel) + return cairo_surface_reference (display->solid[display->last_solid_cache[0].index]); + + for (i = 0; i < 16; i++) { + if (display->solid_cache[i] == pixel) + goto done; + } + + i = hars_petruska_f54_1_random () % 16; + cairo_surface_destroy (display->solid[i]); + + display->solid[i] = color_source (dst, color); + display->solid_cache[i] = pixel; + +done: + display->last_solid_cache[0].color = pixel; + display->last_solid_cache[0].index = i; + return cairo_surface_reference (display->solid[i]); +} + +static cairo_surface_t * +transparent_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) +{ + cairo_xlib_display_t *display = dst->display; + uint32_t pixel = + (uint32_t)color->alpha_short >> 8 << 24 | + color->red_short >> 8 << 16 | + color->green_short >> 8 << 8 | + color->blue_short >> 8 << 0; + int i; + + if (display->last_solid_cache[1].color == pixel) { + assert (display->solid[display->last_solid_cache[1].index]); + return cairo_surface_reference (display->solid[display->last_solid_cache[1].index]); + } + + for (i = 16; i < 32; i++) { + if (display->solid_cache[i] == pixel) + goto done; + } + + i = 16 + (hars_petruska_f54_1_random () % 16); + cairo_surface_destroy (display->solid[i]); + + display->solid[i] = color_source (dst, color); + display->solid_cache[i] = pixel; + +done: + display->last_solid_cache[1].color = pixel; + display->last_solid_cache[1].index = i; + assert (display->solid[i]); + return cairo_surface_reference (display->solid[i]); +} + +static cairo_surface_t * +solid_source (cairo_xlib_surface_t *dst, + const cairo_color_t *color) +{ + if ((color->red_short | color->green_short | color->blue_short) <= 0xff) + return alpha_source (dst, color->alpha_short >> 8); + + if (CAIRO_ALPHA_SHORT_IS_OPAQUE (color->alpha_short)) { + if (color->red_short >= 0xff00 && color->green_short >= 0xff00 && color->blue_short >= 0xff00) + return white_source (dst); + + return opaque_source (dst, color); + } else + return transparent_source (dst, color); +} + +static cairo_xlib_source_t *init_source (cairo_xlib_surface_t *dst, + cairo_xlib_surface_t *src) +{ + Display *dpy = dst->display->display; + cairo_xlib_source_t *source = &src->embedded_source; + + /* As these are frequent and meant to be fast, we track pictures for + * native surface and minimise update requests. + */ + if (source->picture == None) { + XRenderPictureAttributes pa; + + _cairo_surface_init (&source->base, + &cairo_xlib_source_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + FALSE); /* is_vector */ + + pa.subwindow_mode = IncludeInferiors; + source->picture = XRenderCreatePicture (dpy, + src->drawable, + src->xrender_format, + CPSubwindowMode, &pa); + + source->has_component_alpha = 0; + source->has_matrix = 0; + source->filter = CAIRO_FILTER_NEAREST; + source->extend = CAIRO_EXTEND_NONE; + } + + return (cairo_xlib_source_t *) cairo_surface_reference (&source->base); +} + +static cairo_surface_t * +embedded_source (cairo_xlib_surface_t *dst, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + int *src_x, int *src_y, + cairo_xlib_source_t *source) +{ + Display *dpy = dst->display->display; + cairo_int_status_t status; + XTransform xtransform; + XRenderPictureAttributes pa; + unsigned mask = 0; + + status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix, + pattern->base.filter, + extents->x + extents->width / 2, + extents->y + extents->height / 2, + (pixman_transform_t *)&xtransform, + src_x, src_y); + + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + if (source->has_matrix) { + source->has_matrix = 0; + memcpy (&xtransform, &identity, sizeof (identity)); + status = CAIRO_INT_STATUS_SUCCESS; + } + } else + source->has_matrix = 1; + if (status == CAIRO_INT_STATUS_SUCCESS) + XRenderSetPictureTransform (dpy, source->picture, &xtransform); + + if (source->filter != pattern->base.filter) { + picture_set_filter (dpy, source->picture, pattern->base.filter); + source->filter = pattern->base.filter; + } + + if (source->has_component_alpha != pattern->base.has_component_alpha) { + pa.component_alpha = pattern->base.has_component_alpha; + mask |= CPComponentAlpha; + source->has_component_alpha = pattern->base.has_component_alpha; + } + + if (source->extend != pattern->base.extend) { + pa.repeat = extend_to_repeat (pattern->base.extend); + mask |= CPRepeat; + source->extend = pattern->base.extend; + } + + if (mask) + XRenderChangePicture (dpy, source->picture, mask, &pa); + + return &source->base; +} + +static cairo_surface_t * +subsurface_source (cairo_xlib_surface_t *dst, + const cairo_surface_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_surface_subsurface_t *sub; + cairo_xlib_surface_t *src; + cairo_xlib_source_t *source; + Display *dpy = dst->display->display; + cairo_int_status_t status; + cairo_surface_pattern_t local_pattern; + XTransform xtransform; + XRenderPictureAttributes pa; + unsigned mask = 0; + + sub = (cairo_surface_subsurface_t *) pattern->surface; + + if (sample->x >= 0 && sample->y >= 0 && + sample->x + sample->width <= sub->extents.width && + sample->y + sample->height <= sub->extents.height) + { + src = (cairo_xlib_surface_t *) sub->target; + status = _cairo_surface_flush (&src->base, 0); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (pattern->base.filter == CAIRO_FILTER_NEAREST && + _cairo_matrix_is_translation (&pattern->base.matrix)) + { + *src_x += pattern->base.matrix.x0 + sub->extents.x; + *src_y += pattern->base.matrix.y0 + sub->extents.y; + + _cairo_xlib_surface_ensure_picture (src); + return cairo_surface_reference (&src->base); + } + else + { + cairo_surface_pattern_t local_pattern = *pattern; + local_pattern.base.matrix.x0 += sub->extents.x; + local_pattern.base.matrix.y0 += sub->extents.y; + local_pattern.base.extend = CAIRO_EXTEND_NONE; + return embedded_source (dst, &local_pattern, extents, + src_x, src_y, init_source (dst, src)); + } + } + + if (sub->snapshot && sub->snapshot->type == CAIRO_SURFACE_TYPE_XLIB) { + src = (cairo_xlib_surface_t *) cairo_surface_reference (sub->snapshot); + source = &src->embedded_source; + } else { + src = (cairo_xlib_surface_t *) + _cairo_surface_create_scratch (&dst->base, + sub->base.content, + sub->extents.width, + sub->extents.height, + NULL); + if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + _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 (&src->base, + CAIRO_OPERATOR_SOURCE, + &local_pattern.base, + NULL); + _cairo_pattern_fini (&local_pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (status); + } + + _cairo_xlib_surface_ensure_picture (src); + _cairo_surface_subsurface_set_snapshot (&sub->base, &src->base); + + source = &src->embedded_source; + source->has_component_alpha = 0; + source->has_matrix = 0; + source->filter = CAIRO_FILTER_NEAREST; + source->extend = CAIRO_EXTEND_NONE; + } + + status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix, + pattern->base.filter, + extents->x + extents->width / 2, + extents->y + extents->height / 2, + (pixman_transform_t *)&xtransform, + src_x, src_y); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + if (source->has_matrix) { + source->has_matrix = 0; + memcpy (&xtransform, &identity, sizeof (identity)); + status = CAIRO_INT_STATUS_SUCCESS; + } + } else + source->has_matrix = 1; + if (status == CAIRO_INT_STATUS_SUCCESS) + XRenderSetPictureTransform (dpy, src->picture, &xtransform); + + if (source->filter != pattern->base.filter) { + picture_set_filter (dpy, src->picture, pattern->base.filter); + source->filter = pattern->base.filter; + } + + if (source->has_component_alpha != pattern->base.has_component_alpha) { + pa.component_alpha = pattern->base.has_component_alpha; + mask |= CPComponentAlpha; + source->has_component_alpha = pattern->base.has_component_alpha; + } + + if (source->extend != pattern->base.extend) { + pa.repeat = extend_to_repeat (pattern->base.extend); + mask |= CPRepeat; + source->extend = pattern->base.extend; + } + + if (mask) + XRenderChangePicture (dpy, src->picture, mask, &pa); + + return &src->base; +} + +static cairo_surface_t * +native_source (cairo_xlib_surface_t *dst, + const cairo_surface_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_xlib_surface_t *src; + cairo_int_status_t status; + + if (_cairo_surface_is_subsurface (pattern->surface)) + return subsurface_source (dst, pattern, is_mask, + extents, sample, + src_x, src_y); + + src = unwrap_source (pattern); + status = _cairo_surface_flush (&src->base, 0); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (pattern->base.filter == CAIRO_FILTER_NEAREST && + sample->x >= 0 && sample->y >= 0 && + sample->x + sample->width <= src->width && + sample->y + sample->height <= src->height && + _cairo_matrix_is_translation (&pattern->base.matrix)) + { + *src_x += pattern->base.matrix.x0; + *src_y += pattern->base.matrix.y0; + _cairo_xlib_surface_ensure_picture (src); + return cairo_surface_reference (&src->base); + } + + return embedded_source (dst, pattern, extents, src_x, src_y, + init_source (dst, src)); +} + +static cairo_surface_t * +recording_pattern_get_surface (const cairo_pattern_t *pattern) +{ + cairo_surface_t *surface; + + surface = ((const cairo_surface_pattern_t *) pattern)->surface; + + if (_cairo_surface_is_paginated (surface)) + return cairo_surface_reference (_cairo_paginated_surface_get_recording (surface)); + + if (_cairo_surface_is_snapshot (surface)) + return _cairo_surface_snapshot_get_target (surface); + + return cairo_surface_reference (surface); +} + +static cairo_surface_t * +record_source (cairo_xlib_surface_t *dst, + const cairo_surface_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_xlib_surface_t *src; + cairo_surface_t *recording; + cairo_matrix_t matrix, m; + cairo_status_t status; + cairo_rectangle_int_t upload, limit; + + upload = *sample; + if (_cairo_surface_get_extents (pattern->surface, &limit) && + ! _cairo_rectangle_intersect (&upload, &limit)) + { + if (pattern->base.extend == CAIRO_EXTEND_NONE) + return alpha_source (dst, 0); + + upload = limit; + } + + src = (cairo_xlib_surface_t *) + _cairo_surface_create_scratch (&dst->base, + pattern->surface->content, + upload.width, + upload.height, + NULL); + if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + cairo_matrix_init_translate (&matrix, upload.x, upload.y); + recording = recording_pattern_get_surface (&pattern->base), + status = _cairo_recording_surface_replay_with_clip (recording, + &matrix, &src->base, + NULL); + cairo_surface_destroy (recording); + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (status); + } + + matrix = pattern->base.matrix; + if (upload.x | upload.y) { + cairo_matrix_init_translate (&m, -upload.x, -upload.y); + cairo_matrix_multiply (&matrix, &matrix, &m); + } + + _cairo_xlib_surface_ensure_picture (src); + if (! picture_set_properties (src->display, src->picture, + &pattern->base, &matrix, extents, + src_x, src_y)) + { + cairo_surface_destroy (&src->base); + return render_pattern (dst, &pattern->base, is_mask, + extents, src_x, src_y); + } + + return &src->base; +} + +static cairo_surface_t * +surface_source (cairo_xlib_surface_t *dst, + const cairo_surface_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_surface_t *src; + cairo_xlib_surface_t *xsrc; + cairo_surface_pattern_t local_pattern; + cairo_status_t status; + cairo_rectangle_int_t upload, limit; + + src = pattern->surface; + if (src->type == CAIRO_SURFACE_TYPE_IMAGE && + src->device == dst->base.device && + _cairo_xlib_shm_surface_get_pixmap (src)) { + cairo_xlib_proxy_t *proxy; + + proxy = _cairo_malloc (sizeof(*proxy)); + if (unlikely (proxy == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_surface_init (&proxy->source.base, + &cairo_xlib_proxy_backend, + dst->base.device, + src->content, + src->is_vector); + + proxy->source.dpy = dst->display->display; + proxy->source.picture = XRenderCreatePicture (proxy->source.dpy, + _cairo_xlib_shm_surface_get_pixmap (src), + _cairo_xlib_shm_surface_get_xrender_format (src), + 0, NULL); + proxy->source.pixmap = None; + + proxy->source.has_component_alpha = 0; + proxy->source.has_matrix = 0; + proxy->source.filter = CAIRO_FILTER_NEAREST; + proxy->source.extend = CAIRO_EXTEND_NONE; + proxy->owner = cairo_surface_reference (src); + + return embedded_source (dst, pattern, extents, src_x, src_y, + &proxy->source); + } + + upload = *sample; + if (_cairo_surface_get_extents (pattern->surface, &limit)) { + if (pattern->base.extend == CAIRO_EXTEND_NONE) { + if (! _cairo_rectangle_intersect (&upload, &limit)) + return alpha_source (dst, 0); + } else if (pattern->base.extend == CAIRO_EXTEND_PAD) { + if (! _cairo_rectangle_intersect (&upload, &limit)) + upload = limit; + } else { + if (upload.x < limit.x || + upload.x + upload.width > limit.x + limit.width || + upload.y < limit.y || + upload.y + upload.height > limit.y + limit.height) + { + upload = limit; + } + } + } + + xsrc = (cairo_xlib_surface_t *) + _cairo_surface_create_scratch (&dst->base, + src->content, + upload.width, + upload.height, + NULL); + if (xsrc->base.type != CAIRO_SURFACE_TYPE_XLIB) { + cairo_surface_destroy (src); + cairo_surface_destroy (&xsrc->base); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + if (_cairo_surface_is_image (src)) { + status = _cairo_xlib_surface_draw_image (xsrc, (cairo_image_surface_t *)src, + upload.x, upload.y, + upload.width, upload.height, + 0, 0); + } else { + cairo_image_surface_t *image; + cairo_rectangle_int_t map_extents = { 0,0, upload.width,upload.height }; + + image = _cairo_surface_map_to_image (&xsrc->base, &map_extents); + + _cairo_pattern_init_for_surface (&local_pattern, pattern->surface); + cairo_matrix_init_translate (&local_pattern.base.matrix, + upload.x, upload.y); + + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + &local_pattern.base, + NULL); + _cairo_pattern_fini (&local_pattern.base); + + status = _cairo_surface_unmap_image (&xsrc->base, image); + if (unlikely (status)) { + cairo_surface_destroy (&xsrc->base); + return _cairo_surface_create_in_error (status); + } + + status = _cairo_xlib_surface_put_shm (xsrc); + if (unlikely (status)) { + cairo_surface_destroy (&xsrc->base); + return _cairo_surface_create_in_error (status); + } + } + + _cairo_pattern_init_static_copy (&local_pattern.base, &pattern->base); + if (upload.x | upload.y) { + cairo_matrix_t m; + cairo_matrix_init_translate (&m, -upload.x, -upload.y); + cairo_matrix_multiply (&local_pattern.base.matrix, + &local_pattern.base.matrix, + &m); + } + + *src_x = *src_y = 0; + _cairo_xlib_surface_ensure_picture (xsrc); + if (! picture_set_properties (xsrc->display, + xsrc->picture, + &local_pattern.base, + &local_pattern.base.matrix, + extents, + src_x, src_y)) + { + cairo_surface_destroy (&xsrc->base); + return render_pattern (dst, &pattern->base, + is_mask, extents, + src_x, src_y); + } + + return &xsrc->base; +} + +static cairo_bool_t +pattern_is_supported (cairo_xlib_display_t *display, + const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_MESH) + return FALSE; + + if (display->buggy_pad_reflect) { + if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_PAD) + return FALSE; + } + + if (display->buggy_gradients) { + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + return FALSE; + } + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display) || + _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL); + case CAIRO_FILTER_GOOD: + return CAIRO_RENDER_HAS_FILTER_GOOD (display); + case CAIRO_FILTER_BEST: + return CAIRO_RENDER_HAS_FILTER_BEST (display); + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + default: + return CAIRO_RENDER_HAS_FILTERS (display); + } +} + +cairo_surface_t * +_cairo_xlib_source_create_for_pattern (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_xlib_surface_t *dst = (cairo_xlib_surface_t *)_dst; + + *src_x = *src_y = 0; + + if (pattern == NULL || pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + if (pattern == NULL) + pattern = &_cairo_pattern_white.base; + + return solid_source (dst, &((cairo_solid_pattern_t *)pattern)->color); + } + + if (pattern_is_supported (dst->display, pattern)) { + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *)pattern; + if (spattern->surface->type == CAIRO_SURFACE_TYPE_XLIB && + _cairo_xlib_surface_same_screen (dst, + unwrap_source (spattern))) + return native_source (dst, spattern, is_mask, + extents, sample, + src_x, src_y); + + if (spattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return record_source (dst, spattern, is_mask, + extents, sample, + src_x, src_y); + + return surface_source (dst, spattern, is_mask, + extents, sample, + src_x, src_y); + } + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || + pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + { + cairo_gradient_pattern_t *gpattern = (cairo_gradient_pattern_t *)pattern; + return gradient_source (dst, gpattern, is_mask, extents, src_x, src_y); + } + } + + return render_pattern (dst, pattern, is_mask, extents, src_x, src_y); +} + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface-private.h b/gfx/cairo/cairo/src/cairo-xlib-surface-private.h index 34732b4f660f..83d9b63cc4c7 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-surface-private.h +++ b/gfx/cairo/cairo/src/cairo-xlib-surface-private.h @@ -33,80 +33,12 @@ #ifndef CAIRO_XLIB_SURFACE_PRIVATE_H #define CAIRO_XLIB_SURFACE_PRIVATE_H +#include "cairo-xlib-xrender-private.h" #include "cairo-xlib.h" #include "cairo-xlib-private.h" -#include "cairo-xlib-xrender-private.h" #include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" -typedef struct _cairo_xlib_surface cairo_xlib_surface_t; - -struct _cairo_xlib_surface { - cairo_surface_t base; - - cairo_xlib_screen_t *screen; - cairo_xlib_hook_t close_display_hook; - - Drawable drawable; - cairo_bool_t owns_pixmap; - Visual *visual; - - int use_pixmap; - - int render_major; - int render_minor; - - /* TRUE if the server has a bug with repeating pictures - * - * https://bugs.freedesktop.org/show_bug.cgi?id=3566 - * - * We can't test for this because it depends on whether the - * picture is in video memory or not. - * - * We also use this variable as a guard against a second - * independent bug with transformed repeating pictures: - * - * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html - * - * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so - * we can reuse the test for now. - */ - unsigned int buggy_gradients : 1; - unsigned int buggy_pad_reflect : 1; - unsigned int buggy_repeat : 1; -#define CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS 1 -#define CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT 1 -#define CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT 1 - - int width; - int height; - int depth; - - Picture dst_picture, src_picture; - - unsigned int clip_dirty; - XRectangle embedded_clip_rects[8]; - XRectangle *clip_rects; - int num_clip_rects; - cairo_region_t *clip_region; - - XRenderPictFormat *xrender_format; - cairo_filter_t filter; - cairo_extend_t extend; - cairo_bool_t has_component_alpha; - int precision; - XTransform xtransform; - - uint32_t a_mask; - uint32_t r_mask; - uint32_t g_mask; - uint32_t b_mask; -}; - -enum { - CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC = 0x01, - CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE = 0x02, - CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL = 0x03 -}; #endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface-shm.c b/gfx/cairo/cairo/src/cairo-xlib-surface-shm.c new file mode 100644 index 000000000000..ccdaf4cbcf09 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-surface-shm.c @@ -0,0 +1,1463 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2012 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib-private.h" +#include "cairo-xlib-surface-private.h" + +#if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H) +void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {} + +cairo_surface_t * +_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, + cairo_bool_t overwrite) +{ + return NULL; +} + +cairo_int_status_t +_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) +{ + assert (!surface->fallback); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_surface_t * +_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, + pixman_format_code_t format, + int width, int height) +{ + return NULL; +} + +cairo_surface_t * +_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, + pixman_format_code_t format, + int width, int height) +{ + return NULL; +} + +cairo_surface_t * +_cairo_xlib_surface_create_similar_shm (void *other, + cairo_format_t format, + int width, int height) +{ + return cairo_image_surface_create (format, width, height); +} + +void +_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm) +{ + ASSERT_NOT_REACHED; +} + +void +_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, + XImage *ximage) +{ + ASSERT_NOT_REACHED; +} + +void * +_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return NULL; +} + +Pixmap +_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return 0; +} + +XRenderPictFormat * +_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return NULL; +} + +cairo_bool_t +_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return FALSE; +} + +cairo_bool_t +_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface) +{ + ASSERT_NOT_REACHED; + return TRUE; +} + +void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {} + +#else + +#include "cairo-damage-private.h" +#include "cairo-default-context-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-mempool-private.h" + +#include +#include +#include +#if HAVE_X11_EXTENSIONS_SHMPROTO_H +#include +#elif HAVE_X11_EXTENSIONS_SHMSTR_H +#include +#endif +#include +#include + +#define MIN_PIXMAP_SIZE 4096 + +#define MIN_BITS 8 +#define MIN_SIZE (1<<(MIN_BITS-1)) + +typedef struct _cairo_xlib_shm cairo_xlib_shm_t; +typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t; +typedef struct _cairo_xlib_shm_surface cairo_xlib_shm_surface_t; + +struct _cairo_xlib_shm { + cairo_mempool_t mem; + + XShmSegmentInfo shm; + unsigned long attached; + cairo_list_t link; +}; + +struct _cairo_xlib_shm_info { + unsigned long last_request; + void *mem; + size_t size; + cairo_xlib_shm_t *pool; +}; + +struct _cairo_xlib_shm_surface { + cairo_image_surface_t image; + + cairo_list_t link; + cairo_xlib_shm_info_t *info; + Pixmap pixmap; + unsigned long active; + int idle; +}; + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i) >> 1) +#define PQ_FIRST_ENTRY 1 + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1) + +#define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY]) + +struct pqueue { + int size, max_size; + cairo_xlib_shm_info_t **elements; +}; + +struct _cairo_xlib_shm_display { + int has_pixmaps; + int opcode; + int event; + + Window window; + unsigned long last_request; + unsigned long last_event; + + cairo_list_t surfaces; + + cairo_list_t pool; + struct pqueue info; +}; + +static inline cairo_bool_t +seqno_passed (unsigned long a, unsigned long b) +{ + return (long)(b - a) >= 0; +} + +static inline cairo_bool_t +seqno_before (unsigned long a, unsigned long b) +{ + return (long)(b - a) > 0; +} + +static inline cairo_bool_t +seqno_after (unsigned long a, unsigned long b) +{ + return (long)(a - b) > 0; +} + +static inline cairo_status_t +_pqueue_init (struct pqueue *pq) +{ + pq->max_size = 32; + pq->size = 0; + + pq->elements = _cairo_malloc_ab (pq->max_size, + sizeof (cairo_xlib_shm_info_t *)); + if (unlikely (pq->elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + PQ_TOP(pq) = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_fini (struct pqueue *pq) +{ + free (pq->elements); +} + +static cairo_status_t +_pqueue_grow (struct pqueue *pq) +{ + cairo_xlib_shm_info_t **new_elements; + + new_elements = _cairo_realloc_ab (pq->elements, + 2 * pq->max_size, + sizeof (cairo_xlib_shm_info_t *)); + if (unlikely (new_elements == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pq->elements = new_elements; + pq->max_size *= 2; + return CAIRO_STATUS_SUCCESS; +} + +static void +_pqueue_shrink (struct pqueue *pq, int min_size) +{ + cairo_xlib_shm_info_t **new_elements; + + if (min_size > pq->max_size) + return; + + new_elements = _cairo_realloc_ab (pq->elements, + min_size, + sizeof (cairo_xlib_shm_info_t *)); + if (unlikely (new_elements == NULL)) + return; + + pq->elements = new_elements; + pq->max_size = min_size; +} + +static inline cairo_status_t +_pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info) +{ + cairo_xlib_shm_info_t **elements; + int i, parent; + + if (unlikely (pq->size + 1 == pq->max_size)) { + cairo_status_t status; + + status = _pqueue_grow (pq); + if (unlikely (status)) + return status; + } + + elements = pq->elements; + + for (i = ++pq->size; + i != PQ_FIRST_ENTRY && + info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = info; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +_pqueue_pop (struct pqueue *pq) +{ + cairo_xlib_shm_info_t **elements = pq->elements; + cairo_xlib_shm_info_t *tail; + int child, i; + + tail = elements[pq->size--]; + if (pq->size == 0) { + elements[PQ_FIRST_ENTRY] = NULL; + _pqueue_shrink (pq, 32); + return; + } + + for (i = PQ_FIRST_ENTRY; + (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size; + i = child) + { + if (child != pq->size && + elements[child+1]->last_request < elements[child]->last_request) + { + child++; + } + + if (elements[child]->last_request >= tail->last_request) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static cairo_bool_t _x_error_occurred; + +static int +_check_error_handler (Display *display, + XErrorEvent *event) +{ + _x_error_occurred = TRUE; + return False; /* ignored */ +} + +static cairo_bool_t +can_use_shm (Display *dpy, int *has_pixmap) +{ + XShmSegmentInfo shm; + int (*old_handler) (Display *display, XErrorEvent *event); + Status success; + int major, minor; + + if (! XShmQueryExtension (dpy)) + return FALSE; + + XShmQueryVersion (dpy, &major, &minor, has_pixmap); + + shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); + if (shm.shmid == -1) + return FALSE; + + shm.readOnly = FALSE; + shm.shmaddr = shmat (shm.shmid, NULL, 0); + if (shm.shmaddr == (char *) -1) { + shmctl (shm.shmid, IPC_RMID, NULL); + return FALSE; + } + + assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex)); + _x_error_occurred = FALSE; + + XLockDisplay (dpy); + XSync (dpy, False); + old_handler = XSetErrorHandler (_check_error_handler); + + success = XShmAttach (dpy, &shm); + if (success) + XShmDetach (dpy, &shm); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XUnlockDisplay (dpy); + + shmctl (shm.shmid, IPC_RMID, NULL); + shmdt (shm.shmaddr); + + return success && ! _x_error_occurred; +} + +static inline Display * +peek_display (cairo_device_t *device) +{ + return ((cairo_xlib_display_t *)device)->display; +} + +static inline unsigned long +peek_processed (cairo_device_t *device) +{ + return LastKnownRequestProcessed (peek_display(device)); +} + +static void +_cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display, + cairo_xlib_shm_t *pool) +{ + shmdt (pool->shm.shmaddr); + if (display->display) /* may be called after CloseDisplay */ + XShmDetach (display->display, &pool->shm); + + _cairo_mempool_fini (&pool->mem); + + cairo_list_del (&pool->link); + free (pool); +} + +static void send_event(cairo_xlib_display_t *display, + cairo_xlib_shm_info_t *info, + unsigned long seqno) +{ + XShmCompletionEvent ev; + + if (! seqno_after (seqno, display->shm->last_event)) + return; + + ev.type = display->shm->event; + ev.send_event = 1; /* XXX or lie? */ + ev.serial = XNextRequest (display->display); + ev.drawable = display->shm->window; + ev.major_code = display->shm->opcode; + ev.minor_code = X_ShmPutImage; + ev.shmseg = info->pool->shm.shmid; + ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr; + + XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev); + + display->shm->last_event = ev.serial; +} + +static void _cairo_xlib_display_sync (cairo_xlib_display_t *display) +{ + cairo_xlib_shm_info_t *info; + struct pqueue *pq = &display->shm->info; + + XSync (display->display, False); + + while ((info = PQ_TOP(pq))) { + _cairo_mempool_free (&info->pool->mem, info->mem); + _pqueue_pop (&display->shm->info); + free (info); + } +} + +static void +_cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display) +{ + cairo_xlib_shm_info_t *info; + Display *dpy = display->display; + struct pqueue *pq = &display->shm->info; + unsigned long processed; + + if (PQ_TOP(pq) == NULL) + return; + + XEventsQueued (dpy, QueuedAfterReading); + processed = LastKnownRequestProcessed (dpy); + + info = PQ_TOP(pq); + do { + if (! seqno_passed (info->last_request, processed)) { + send_event (display, info, display->shm->last_request); + return; + } + + _cairo_mempool_free (&info->pool->mem, info->mem); + _pqueue_pop (&display->shm->info); + free (info); + } while ((info = PQ_TOP(pq))); +} + +static cairo_xlib_shm_t * +_cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size, + void **ptr, unsigned long *last_request) +{ + cairo_xlib_shm_info_t *info; + struct pqueue *pq = &display->shm->info; + + if (PQ_TOP(pq) == NULL) + return NULL; + + info = PQ_TOP(pq); + do { + cairo_xlib_shm_t *pool = info->pool; + + *last_request = info->last_request; + + _pqueue_pop (&display->shm->info); + _cairo_mempool_free (&pool->mem, info->mem); + free (info); + + if (pool->mem.free_bytes >= size) { + void *mem = _cairo_mempool_alloc (&pool->mem, size); + if (mem != NULL) { + *ptr = mem; + return pool; + } + } + } while ((info = PQ_TOP(pq))); + + return NULL; +} + +static cairo_xlib_shm_t * +_cairo_xlib_shm_pool_find (cairo_xlib_display_t *display, + size_t size, + void **ptr) +{ + cairo_xlib_shm_t *pool; + + cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) { + if (pool->mem.free_bytes >= size) { + void *mem = _cairo_mempool_alloc (&pool->mem, size); + if (mem != NULL) { + *ptr = mem; + return pool; + } + } + } + + return NULL; +} + +static void +_cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display) +{ + cairo_xlib_shm_t *pool, *next; + unsigned long processed; + + processed = LastKnownRequestProcessed (display->display); + + cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t, + &display->shm->pool, link) { + if (! seqno_passed (pool->attached, processed)) + break; + + if (pool->mem.free_bytes == pool->mem.max_bytes) + _cairo_xlib_display_shm_pool_destroy (display, pool); + } +} + +static cairo_xlib_shm_t * +_cairo_xlib_shm_pool_create(cairo_xlib_display_t *display, + size_t size, void **ptr) +{ + Display *dpy = display->display; + cairo_xlib_shm_t *pool; + size_t bytes, maxbits = 16, minbits = MIN_BITS; + Status success; + + pool = _cairo_malloc (sizeof (cairo_xlib_shm_t)); + if (pool == NULL) + return NULL; + + bytes = 1 << maxbits; + while (bytes <= size) + bytes <<= 1, maxbits++; + bytes <<= 3; + + minbits += (maxbits - 16) / 2; + + pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); + while (pool->shm.shmid == -1 && bytes >= 2*size) { + bytes >>= 1; + pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600); + } + if (pool->shm.shmid == -1) + goto cleanup; + + pool->shm.readOnly = FALSE; + pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0); + if (pool->shm.shmaddr == (char *) -1) { + shmctl (pool->shm.shmid, IPC_RMID, NULL); + goto cleanup; + } + + pool->attached = XNextRequest (dpy); + success = XShmAttach (dpy, &pool->shm); +#if !IPC_RMID_DEFERRED_RELEASE + XSync (dpy, FALSE); +#endif + shmctl (pool->shm.shmid, IPC_RMID, NULL); + + if (! success) + goto cleanup_shm; + + if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes, + minbits, maxbits - minbits + 1)) + goto cleanup_detach; + + cairo_list_add (&pool->link, &display->shm->pool); + + *ptr = _cairo_mempool_alloc (&pool->mem, size); + assert (*ptr != NULL); + return pool; + +cleanup_detach: + XShmDetach (dpy, &pool->shm); +cleanup_shm: + shmdt (pool->shm.shmaddr); +cleanup: + free (pool); + return NULL; +} + +static cairo_xlib_shm_info_t * +_cairo_xlib_shm_info_create (cairo_xlib_display_t *display, + size_t size, cairo_bool_t will_sync) +{ + cairo_xlib_shm_info_t *info; + cairo_xlib_shm_t *pool; + unsigned long last_request = 0; + void *mem = NULL; + + _cairo_xlib_shm_info_cleanup (display); + pool = _cairo_xlib_shm_pool_find (display, size, &mem); + _cairo_xlib_shm_pool_cleanup (display); + + if (pool == NULL && will_sync) + pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request); + if (pool == NULL) + pool = _cairo_xlib_shm_pool_create (display, size, &mem); + if (pool == NULL) + return NULL; + + assert (mem != NULL); + + info = _cairo_malloc (sizeof (*info)); + if (info == NULL) { + _cairo_mempool_free (&pool->mem, mem); + return NULL; + } + + info->pool = pool; + info->mem = mem; + info->size = size; + info->last_request = last_request; + + return info; +} + +static cairo_status_t +_cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags) +{ + cairo_xlib_shm_surface_t *shm = abstract_surface; + cairo_xlib_display_t *display; + Display *dpy; + cairo_status_t status; + + if (shm->active == 0) + return CAIRO_STATUS_SUCCESS; + + if (shm->image.base._finishing) + return CAIRO_STATUS_SUCCESS; + + if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) { + shm->active = 0; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_xlib_display_acquire (shm->image.base.device, &display); + if (unlikely (status)) + return status; + + send_event (display, shm->info, shm->active); + + dpy = display->display; + XEventsQueued (dpy, QueuedAfterReading); + while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) { + LockDisplay(dpy); + _XReadEvents(dpy); + UnlockDisplay(dpy); + } + + cairo_device_release (&display->base); + shm->active = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static inline cairo_bool_t +active (cairo_xlib_shm_surface_t *shm, Display *dpy) +{ + return (shm->active && + ! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))); +} + +static cairo_status_t +_cairo_xlib_shm_surface_finish (void *abstract_surface) +{ + cairo_xlib_shm_surface_t *shm = abstract_surface; + cairo_xlib_display_t *display; + cairo_status_t status; + + if (shm->image.base.damage) { + _cairo_damage_destroy (shm->image.base.damage); + shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + } + + status = _cairo_xlib_display_acquire (shm->image.base.device, &display); + if (unlikely (status)) + return status; + + if (shm->pixmap) + XFreePixmap (display->display, shm->pixmap); + + if (active (shm, display->display)) { + shm->info->last_request = shm->active; + _pqueue_push (&display->shm->info, shm->info); + if (seqno_before (display->shm->last_request, shm->active)) + display->shm->last_request = shm->active; + } else { + _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem); + free (shm->info); + + _cairo_xlib_shm_pool_cleanup (display); + } + + cairo_list_del (&shm->link); + + cairo_device_release (&display->base); + return _cairo_image_surface_finish (abstract_surface); +} + +static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_xlib_shm_surface_finish, + + _cairo_default_context_create, + + _cairo_image_surface_create_similar, + NULL, /* create similar image */ + _cairo_image_surface_map_to_image, + _cairo_image_surface_unmap_image, + + _cairo_image_surface_source, + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + _cairo_image_surface_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_image_surface_get_extents, + _cairo_image_surface_get_font_options, + + _cairo_xlib_shm_surface_flush, + NULL, + + _cairo_image_surface_paint, + _cairo_image_surface_mask, + _cairo_image_surface_stroke, + _cairo_image_surface_fill, + NULL, /* fill-stroke */ + _cairo_image_surface_glyphs, +}; + +static cairo_bool_t +has_shm (cairo_xlib_surface_t *surface) +{ + cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device; + return display->shm != NULL; +} + +static int +has_shm_pixmaps (cairo_xlib_surface_t *surface) +{ + cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device; + if (!display->shm) + return 0; + + return display->shm->has_pixmaps; +} + +static cairo_xlib_shm_surface_t * +_cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other, + pixman_format_code_t format, + int width, int height, + cairo_bool_t will_sync, + int create_pixmap) +{ + cairo_xlib_shm_surface_t *shm; + cairo_xlib_display_t *display; + pixman_image_t *image; + int stride, size; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return NULL; + + stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format)); + size = stride * height; + if (size < MIN_SIZE) + return NULL; + + shm = _cairo_malloc (sizeof (*shm)); + if (unlikely (shm == NULL)) + return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_surface_init (&shm->image.base, + &cairo_xlib_shm_surface_backend, + other->base.device, + _cairo_content_from_pixman_format (format), + FALSE); /* is_vector */ + + if (_cairo_xlib_display_acquire (other->base.device, &display)) + goto cleanup_shm; + + shm->info = _cairo_xlib_shm_info_create (display, size, will_sync); + if (shm->info == NULL) + goto cleanup_display; + + image = pixman_image_create_bits (format, width, height, + (uint32_t *) shm->info->mem, stride); + if (image == NULL) + goto cleanup_info; + + _cairo_image_surface_init (&shm->image, image, format); + + shm->pixmap = 0; + if (create_pixmap && size >= create_pixmap) { + shm->pixmap = XShmCreatePixmap (display->display, + other->drawable, + shm->info->mem, + &shm->info->pool->shm, + shm->image.width, + shm->image.height, + shm->image.depth); + } + shm->active = shm->info->last_request; + shm->idle = -5; + + assert (shm->active == 0 || will_sync); + + cairo_list_add (&shm->link, &display->shm->surfaces); + + cairo_device_release (&display->base); + + return shm; + +cleanup_info: + _cairo_mempool_free (&shm->info->pool->mem, shm->info->mem); + free(shm->info); +cleanup_display: + cairo_device_release (&display->base); +cleanup_shm: + free (shm); + return NULL; +} + +static void +_cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm; + cairo_xlib_display_t *display; + cairo_damage_t *damage; + GC gc; + + damage = _cairo_damage_reduce (surface->base.damage); + surface->base.damage = _cairo_damage_create(); + + if (_cairo_xlib_display_acquire (surface->base.device, &display)) + goto cleanup_damage; + + if (_cairo_xlib_surface_get_gc (display, surface, &gc)) + goto cleanup_display; + + if (! surface->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + } + + if (damage->region) { + XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *rects = stack_rects; + cairo_rectangle_int_t r; + int n_rects, i; + + n_rects = cairo_region_num_rectangles (damage->region); + if (n_rects == 0) { + } else if (n_rects == 1) { + cairo_region_get_rectangle (damage->region, 0, &r); + XCopyArea (display->display, + surface->drawable, shm->pixmap, gc, + r.x, r.y, + r.width, r.height, + r.x, r.y); + } else { + if (n_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); + if (unlikely (rects == NULL)) { + rects = stack_rects; + n_rects = ARRAY_LENGTH (stack_rects); + } + } + for (i = 0; i < n_rects; i++) { + cairo_region_get_rectangle (damage->region, i, &r); + + rects[i].x = r.x; + rects[i].y = r.y; + rects[i].width = r.width; + rects[i].height = r.height; + } + XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded); + + XCopyArea (display->display, + surface->drawable, shm->pixmap, gc, + 0, 0, + shm->image.width, shm->image.height, + 0, 0); + + if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) + XSetClipMask (display->display, gc, None); + } + } else { + XCopyArea (display->display, + surface->drawable, shm->pixmap, gc, + 0, 0, + shm->image.width, shm->image.height, + 0, 0); + } + + if (! surface->owns_pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + } + + _cairo_xlib_display_sync (display); + shm->active = 0; + shm->idle--; + + _cairo_xlib_surface_put_gc (display, surface, gc); +cleanup_display: + cairo_device_release (&display->base); +cleanup_damage: + _cairo_damage_destroy (damage); +} + +static void +_cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm; + + assert (shm->active == 0); + + _cairo_damage_destroy (surface->base.damage); + surface->base.damage = _cairo_damage_create(); + + memset (shm->image.data, 0, shm->image.stride * shm->image.height); + shm->image.base.is_clear = TRUE; +} + +static void inc_idle (cairo_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface; + shm->idle++; +} + +static void dec_idle (cairo_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface; + shm->idle--; +} + +cairo_surface_t * +_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface, + cairo_bool_t overwrite) +{ + if (surface->fallback) { + assert (surface->base.damage); + assert (surface->shm); + assert (surface->shm->damage); + goto done; + } + + if (surface->shm == NULL) { + pixman_format_code_t pixman_format; + cairo_bool_t will_sync; + + if (! has_shm_pixmaps (surface)) + return NULL; + + if ((surface->width | surface->height) < 32) + return NULL; + + pixman_format = _pixman_format_for_xlib_surface (surface); + if (pixman_format == 0) + return NULL; + + will_sync = !surface->base.is_clear && !overwrite; + + surface->shm = + &_cairo_xlib_shm_surface_create (surface, pixman_format, + surface->width, surface->height, + will_sync, 1)->image.base; + if (surface->shm == NULL) + return NULL; + + assert (surface->base.damage == NULL); + if (surface->base.serial || !surface->owns_pixmap) { + cairo_rectangle_int_t rect; + + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + + surface->base.damage = + _cairo_damage_add_rectangle (NULL, &rect); + } else + surface->base.damage = _cairo_damage_create (); + + surface->shm->damage = _cairo_damage_create (); + } + + if (overwrite) { + _cairo_damage_destroy (surface->base.damage); + surface->base.damage = _cairo_damage_create (); + } + + if (!surface->base.is_clear && surface->base.damage->dirty) + _cairo_xlib_surface_update_shm (surface); + + _cairo_xlib_shm_surface_flush (surface->shm, 1); + + if (surface->base.is_clear && surface->base.damage->dirty) + _cairo_xlib_surface_clear_shm (surface); + +done: + dec_idle(surface->shm); + return surface->shm; +} + +cairo_int_status_t +_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + if (!surface->fallback) { + if (surface->shm) + inc_idle (surface->shm); + return CAIRO_INT_STATUS_SUCCESS; + } + + if (surface->shm->damage->dirty) { + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm; + cairo_xlib_display_t *display; + cairo_damage_t *damage; + GC gc; + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return status; + + damage = _cairo_damage_reduce (shm->image.base.damage); + shm->image.base.damage = _cairo_damage_create (); + + TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__, + damage->region ? cairo_region_num_rectangles (damage->region) : 0)); + if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) { + XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; + XRectangle *rects = stack_rects; + cairo_rectangle_int_t r; + int n_rects, i; + + n_rects = cairo_region_num_rectangles (damage->region); + if (n_rects == 0) + goto out; + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + goto out; + + if (n_rects == 1) { + cairo_region_get_rectangle (damage->region, 0, &r); + _cairo_xlib_shm_surface_mark_active (surface->shm); + XCopyArea (display->display, + shm->pixmap, surface->drawable, gc, + r.x, r.y, + r.width, r.height, + r.x, r.y); + } else { + if (n_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); + if (unlikely (rects == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + _cairo_xlib_surface_put_gc (display, surface, gc); + goto out; + } + } + for (i = 0; i < n_rects; i++) { + cairo_region_get_rectangle (damage->region, i, &r); + + rects[i].x = r.x; + rects[i].y = r.y; + rects[i].width = r.width; + rects[i].height = r.height; + } + XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded); + + _cairo_xlib_shm_surface_mark_active (surface->shm); + XCopyArea (display->display, + shm->pixmap, surface->drawable, gc, + 0, 0, + shm->image.width, shm->image.height, + 0, 0); + + if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) + XSetClipMask (display->display, gc, None); + } + + _cairo_xlib_surface_put_gc (display, surface, gc); + } + +out: + _cairo_damage_destroy (damage); + cairo_device_release (&display->base); + } + + return status; +} + +cairo_surface_t * +_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other, + pixman_format_code_t format, + int width, int height) +{ + cairo_surface_t *surface; + + surface = NULL; + if (has_shm (other)) + surface = &_cairo_xlib_shm_surface_create (other, format, width, height, + FALSE, has_shm_pixmaps (other))->image.base; + + return surface; +} + +cairo_surface_t * +_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface, + pixman_format_code_t format, + int width, int height) +{ + if (! has_shm(surface)) + return NULL; + + return &_cairo_xlib_shm_surface_create (surface, format, width, height, + FALSE, 0)->image.base; +} + +cairo_surface_t * +_cairo_xlib_surface_create_similar_shm (void *other, + cairo_format_t format, + int width, int height) +{ + cairo_surface_t *surface; + + surface = _cairo_xlib_surface_create_shm (other, + _cairo_format_to_pixman_format_code (format), + width, height); + if (surface) { + if (! surface->is_clear) { + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; + assert (shm->active == 0); + memset (shm->image.data, 0, shm->image.stride * shm->image.height); + shm->image.base.is_clear = TRUE; + } + } else + surface = cairo_image_surface_create (format, width, height); + + return surface; +} + +void +_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm) +{ + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm; + cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device; + + shm->active = XNextRequest (display->display); +} + +void +_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface, + XImage *ximage) +{ + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; + int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; + cairo_format_masks_t image_masks; + int ret; + + ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks); + assert (ret); + + ximage->width = shm->image.width; + ximage->height = shm->image.height; + ximage->format = ZPixmap; + ximage->data = (char *) shm->image.data; + ximage->obdata = (char *)&shm->info->pool->shm; + ximage->byte_order = native_byte_order; + ximage->bitmap_unit = 32; /* always for libpixman */ + ximage->bitmap_bit_order = native_byte_order; + ximage->bitmap_pad = 32; /* always for libpixman */ + ximage->depth = shm->image.depth; + ximage->bytes_per_line = shm->image.stride; + ximage->bits_per_pixel = image_masks.bpp; + ximage->red_mask = image_masks.red_mask; + ximage->green_mask = image_masks.green_mask; + ximage->blue_mask = image_masks.blue_mask; + ximage->xoffset = 0; + + ret = XInitImage (ximage); + assert (ret != 0); +} + +void * +_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface) +{ + cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device; + cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface; + + display->shm->last_event = shm->active = XNextRequest (display->display); + return &shm->info->pool->shm; +} + +Pixmap +_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm; + + shm = (cairo_xlib_shm_surface_t *) surface; + return shm->pixmap; +} + +XRenderPictFormat * +_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm; + + shm = (cairo_xlib_shm_surface_t *) surface; + if (shm->image.format != CAIRO_FORMAT_INVALID) + return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device, + shm->image.format); + + return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device, + shm->image.pixman_format); +} + +cairo_bool_t +_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm; + + shm = (cairo_xlib_shm_surface_t *) surface; + if (shm->active == 0) + return FALSE; + + if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) { + shm->active = 0; + return FALSE; + } + + return TRUE; +} + +cairo_bool_t +_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface) +{ + cairo_xlib_shm_surface_t *shm; + + shm = (cairo_xlib_shm_surface_t *) surface; + return shm->idle > 0; +} + +#define XORG_VERSION_ENCODE(major,minor,patch,snap) \ + (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap) + +static cairo_bool_t +has_broken_send_shm_event (cairo_xlib_display_t *display, + cairo_xlib_shm_display_t *shm) +{ + Display *dpy = display->display; + int (*old_handler) (Display *display, XErrorEvent *event); + XShmCompletionEvent ev; + XShmSegmentInfo info; + + info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); + if (info.shmid == -1) + return TRUE; + + info.readOnly = FALSE; + info.shmaddr = shmat (info.shmid, NULL, 0); + if (info.shmaddr == (char *) -1) { + shmctl (info.shmid, IPC_RMID, NULL); + return TRUE; + } + + ev.type = shm->event; + ev.send_event = 1; + ev.serial = 1; + ev.drawable = shm->window; + ev.major_code = shm->opcode; + ev.minor_code = X_ShmPutImage; + + ev.shmseg = info.shmid; + ev.offset = 0; + + assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex)); + _x_error_occurred = FALSE; + + XLockDisplay (dpy); + XSync (dpy, False); + old_handler = XSetErrorHandler (_check_error_handler); + + XShmAttach (dpy, &info); + XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev); + XShmDetach (dpy, &info); + + XSync (dpy, False); + XSetErrorHandler (old_handler); + XUnlockDisplay (dpy); + + shmctl (info.shmid, IPC_RMID, NULL); + shmdt (info.shmaddr); + + return _x_error_occurred; +} + +static cairo_bool_t +xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display, + cairo_xlib_shm_display_t *shm) +{ + Display *dpy = display->display; + + /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent, + * the Xserver may crash if it does not take care when processing + * the event type. For instance versions of Xorg prior to 1.11.1 + * exhibited this bug, and was fixed by: + * + * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39 + * Author: Sam Spilsbury + * Date: Wed Sep 14 09:58:34 2011 +0800 + * + * Remove the SendEvent bit (0x80) before doing range checks on event type. + */ + if (_cairo_xlib_vendor_is_xorg (dpy) && + VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1)) + return TRUE; + + /* For everyone else check that no error is generated */ + return has_broken_send_shm_event (display, shm); +} + +void +_cairo_xlib_display_init_shm (cairo_xlib_display_t *display) +{ + cairo_xlib_shm_display_t *shm; + XSetWindowAttributes attr; + XExtCodes *codes; + int has_pixmap, scr; + + display->shm = NULL; + + if (!can_use_shm (display->display, &has_pixmap)) + return; + + shm = _cairo_malloc (sizeof (*shm)); + if (unlikely (shm == NULL)) + return; + + codes = XInitExtension (display->display, SHMNAME); + if (codes == NULL) { + free (shm); + return; + } + + shm->opcode = codes ->major_opcode; + shm->event = codes->first_event; + + if (unlikely (_pqueue_init (&shm->info))) { + free (shm); + return; + } + + scr = DefaultScreen (display->display); + attr.override_redirect = 1; + shm->window = XCreateWindow (display->display, + DefaultRootWindow (display->display), -1, -1, + 1, 1, 0, + DefaultDepth (display->display, scr), + InputOutput, + DefaultVisual (display->display, scr), + CWOverrideRedirect, &attr); + shm->last_event = 0; + shm->last_request = 0; + + if (xorg_has_buggy_send_shm_completion_event(display, shm)) + has_pixmap = 0; + + shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0; + cairo_list_init (&shm->pool); + + cairo_list_init (&shm->surfaces); + + display->shm = shm; +} + +void +_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) +{ + cairo_xlib_shm_display_t *shm = display->shm; + + if (shm == NULL) + return; + + while (!cairo_list_is_empty (&shm->surfaces)) + cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces, + cairo_xlib_shm_surface_t, + link)->image.base); + + _pqueue_fini (&shm->info); + + while (!cairo_list_is_empty (&shm->pool)) { + cairo_xlib_shm_t *pool; + + pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link); + _cairo_xlib_display_shm_pool_destroy (display, pool); + } + + if (display->display) + XDestroyWindow (display->display, shm->window); + + free (shm); + display->shm = NULL; +} +#endif +#endif diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c index e24c9627a66c..b37b21badba6 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-surface.c +++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c @@ -47,20 +47,30 @@ #include "cairoint.h" +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" + +#include "cairo-compositor-private.h" #include "cairo-clip-private.h" +#include "cairo-damage-private.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-list-inline.h" +#include "cairo-pattern-private.h" +#include "cairo-pixman-private.h" +#include "cairo-region-private.h" #include "cairo-scaled-font-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" -#include "cairo-region-private.h" -#include "cairo-xlib-xrender-private.h" #include /* for XDestroyImage */ -#include /* for access to XDisplay's innards */ -#define XLIB_COORD_MAX 32767 +#include +#include +#include #define DEBUG 0 @@ -75,6 +85,7 @@ #endif #if DEBUG +#include static void CAIRO_PRINTF_FORMAT (2, 3) _x_bread_crumb (Display *dpy, const char *fmt, @@ -119,18 +130,20 @@ _x_bread_crumb (Display *dpy, * * Note that the XLib surface automatically takes advantage of X render extension * if it is available. - */ + **/ /** * CAIRO_HAS_XLIB_SURFACE: * * Defined if the Xlib surface backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.0 + **/ /** * SECTION:cairo-xlib-xrender - * @Title: XLib/XRender Backend + * @Title: XLib-XRender Backend * @Short_Description: X Window System rendering using XLib and the X Render extension * @See_Also: #cairo_surface_t * @@ -139,14 +152,16 @@ _x_bread_crumb (Display *dpy, * * Note that the XLib surface automatically takes advantage of X Render extension * if it is available. - */ + **/ /** * CAIRO_HAS_XLIB_XRENDER_SURFACE: * * Defined if the XLib/XRender surface functions are available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.6 + **/ /* Xlib doesn't define a typedef, so define one ourselves */ typedef int (*cairo_xlib_error_func_t) (Display *display, @@ -164,19 +179,6 @@ _cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, static cairo_bool_t _cairo_surface_is_xlib (cairo_surface_t *surface); -static cairo_bool_t -_native_byte_order_lsb (void); - -static cairo_int_status_t -_cairo_xlib_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - /* * Instead of taking two round trips for each blending request, * assume that if a particular drawable fails GetImage that it will @@ -185,43 +187,6 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst, #define CAIRO_ASSUME_PIXMAP 20 -static const XTransform identity = { { - { 1 << 16, 0x00000, 0x00000 }, - { 0x00000, 1 << 16, 0x00000 }, - { 0x00000, 0x00000, 1 << 16 }, -} }; - -#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ - (((surface)->render_major > major) || \ - (((surface)->render_major == major) && ((surface)->render_minor >= minor))) - -#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) - -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) - -#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) -#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) - -#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) - -#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) - -#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) -#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) - -#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11) - -#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ - ((op) <= CAIRO_OPERATOR_SATURATE || \ - (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \ - (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) - static Visual * _visual_for_xrender_format(Screen *screen, XRenderPictFormat *xrender_format) @@ -272,85 +237,25 @@ _visual_for_xrender_format(Screen *screen, return NULL; } -static cairo_status_t -_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, - cairo_region_t *region) -{ - cairo_bool_t had_clip_rects = surface->clip_region != NULL; - - if (had_clip_rects == FALSE && region == NULL) - return CAIRO_STATUS_SUCCESS; - - if (surface->clip_region == region) - return CAIRO_STATUS_SUCCESS; - - if (cairo_region_equal (surface->clip_region, region)) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - if (surface->clip_rects != surface->embedded_clip_rects) { - free (surface->clip_rects); - surface->clip_rects = surface->embedded_clip_rects; - } - surface->num_clip_rects = 0; - - if (region != NULL) { - XRectangle *rects = NULL; - int n_rects, i; - - n_rects = cairo_region_num_rectangles (region); - if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { - rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - rects = surface->embedded_clip_rects; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; - } - - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; - } - - surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; - return CAIRO_STATUS_SUCCESS; -} - static cairo_content_t _xrender_format_to_content (XRenderPictFormat *xrender_format) { - cairo_bool_t xrender_format_has_alpha; - cairo_bool_t xrender_format_has_color; + cairo_content_t content; /* This only happens when using a non-Render server. Let's punt * and say there's no alpha here. */ if (xrender_format == NULL) return CAIRO_CONTENT_COLOR; - xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0); - xrender_format_has_color = (xrender_format->direct.redMask != 0 || - xrender_format->direct.greenMask != 0 || - xrender_format->direct.blueMask != 0); + content = 0; + if (xrender_format->direct.alphaMask) + content |= CAIRO_CONTENT_ALPHA; + if (xrender_format->direct.redMask | + xrender_format->direct.greenMask | + xrender_format->direct.blueMask) + content |= CAIRO_CONTENT_COLOR; - if (xrender_format_has_alpha) - if (xrender_format_has_color) - return CAIRO_CONTENT_COLOR_ALPHA; - else - return CAIRO_CONTENT_ALPHA; - else - return CAIRO_CONTENT_COLOR; + return content; } static cairo_surface_t * @@ -368,7 +273,7 @@ _cairo_xlib_surface_create_similar (void *abstract_src, if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) return NULL; - if (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src)) + if (width == 0 || height == 0) return NULL; if (_cairo_xlib_display_acquire (src->base.device, &display)) @@ -379,21 +284,25 @@ _cairo_xlib_surface_create_similar (void *abstract_src, * constructing a cairo_format_t instead, (which will fairly * arbitrarily pick a visual/depth for the similar surface. */ - xrender_format = src->xrender_format; - if ((xrender_format != NULL && - _xrender_format_to_content (xrender_format) == content) || - (xrender_format = - _cairo_xlib_display_get_xrender_format (display, - _cairo_format_from_content (content)))) + xrender_format = NULL; + if (src->xrender_format && + _xrender_format_to_content (src->xrender_format) == content) { + xrender_format = src->xrender_format; + } + if (xrender_format == NULL) { + xrender_format = + _cairo_xlib_display_get_xrender_format (display, + _cairo_format_from_content (content)); + } + if (xrender_format) { Visual *visual; /* We've got a compatible XRenderFormat now, which means the * similar surface will match the existing surface as closely in * visual/depth etc. as possible. */ pix = XCreatePixmap (display->display, src->drawable, - width <= 0 ? 1 : width, height <= 0 ? 1 : height, - xrender_format->depth); + width, height, xrender_format->depth); if (xrender_format == src->xrender_format) visual = src->visual; @@ -402,19 +311,17 @@ _cairo_xlib_surface_create_similar (void *abstract_src, xrender_format); surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (src->screen, pix, - visual, + _cairo_xlib_surface_create_internal (src->screen, pix, visual, xrender_format, width, height, xrender_format->depth); } else { -#ifdef DEBUG_FORCE_FALLBACKS Screen *screen = src->screen->screen; int depth; - /* No compatabile XRenderFormat, see if we can make an ordinary pixmap, + /* No compatible XRenderFormat, see if we can make an ordinary pixmap, * so that we can still accelerate blits with XCopyArea(). */ if (content != CAIRO_CONTENT_COLOR) { cairo_device_release (&display->base); @@ -432,26 +339,38 @@ _cairo_xlib_surface_create_similar (void *abstract_src, DefaultVisualOfScreen (screen), NULL, width, height, depth); -#else - /* No compatabile XRenderFormat, just say no. */ - cairo_device_release (&display->base); - return NULL; -#endif } - if (unlikely (surface->base.status)) { + if (likely (surface->base.status == CAIRO_STATUS_SUCCESS)) + surface->owns_pixmap = TRUE; + else XFreePixmap (display->display, pix); - cairo_device_release (&display->base); - return &surface->base; - } - - surface->owns_pixmap = TRUE; cairo_device_release (&display->base); return &surface->base; } +static void +_cairo_xlib_surface_discard_shm (cairo_xlib_surface_t *surface) +{ + if (surface->shm == NULL) + return; + + /* Force the flush for an external surface */ + if (!surface->owns_pixmap) + cairo_surface_flush (surface->shm); + + cairo_surface_finish (surface->shm); + cairo_surface_destroy (surface->shm); + surface->shm = NULL; + + _cairo_damage_destroy (surface->base.damage); + surface->base.damage = NULL; + + surface->fallback = 0; +} + static cairo_status_t _cairo_xlib_surface_finish (void *abstract_surface) { @@ -459,59 +378,30 @@ _cairo_xlib_surface_finish (void *abstract_surface) cairo_status_t status; cairo_xlib_display_t *display; - X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable)); + cairo_list_del (&surface->link); status = _cairo_xlib_display_acquire (surface->base.device, &display); if (unlikely (status)) return status; - if (surface->owns_pixmap) { - cairo_status_t status2; + X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable)); - if (surface->dst_picture != None) { - status2 = _cairo_xlib_display_queue_resource (display, - XRenderFreePicture, - surface->dst_picture); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } + if (surface->embedded_source.picture) + XRenderFreePicture (display->display, surface->embedded_source.picture); + if (surface->picture) + XRenderFreePicture (display->display, surface->picture); - if (surface->src_picture != None) { - status2 = _cairo_xlib_display_queue_resource (display, - XRenderFreePicture, - surface->src_picture); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } + _cairo_xlib_surface_discard_shm (surface); - status2 = _cairo_xlib_display_queue_resource (display, - (cairo_xlib_notify_resource_func) XFreePixmap, - surface->drawable); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } else { - if (surface->dst_picture != None) - XRenderFreePicture (display->display, surface->dst_picture); - - if (surface->src_picture != None) - XRenderFreePicture (display->display, surface->src_picture); - } - - if (surface->clip_rects != surface->embedded_clip_rects) - free (surface->clip_rects); - - if (display->display != NULL) - _cairo_xlib_remove_close_display_hook (display, - &surface->close_display_hook); + if (surface->owns_pixmap) + XFreePixmap (display->display, surface->drawable); cairo_device_release (&display->base); - cairo_region_destroy (surface->clip_region); - return status; } -static cairo_status_t +cairo_status_t _cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, GC *gc) @@ -526,17 +416,6 @@ _cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, return CAIRO_STATUS_SUCCESS; } -static void -_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - GC gc) -{ - _cairo_xlib_screen_put_gc (display, - surface->screen, - surface->depth, - gc); -} - static int _noop_error_handler (Display *display, XErrorEvent *event) @@ -644,7 +523,7 @@ static void _swap_ximage_to_native (XImage *ximage) { int unit_bytes = 0; - int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; + int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; if (ximage->bits_per_pixel == 1 && ximage->bitmap_bit_order != native_byte_order) @@ -707,7 +586,6 @@ _characterize_field (uint32_t mask, int *width, int *shift) *shift = _cairo_popcount ((mask - 1) & ~mask) & 31; } - /* Convert a field of 'width' bits to 'new_width' bits with correct * rounding. */ static inline uint32_t @@ -799,7 +677,6 @@ _pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, (b ); } - /* should range from -128 to 127 */ #define X 16 static const int8_t dither_pattern[4][4] = { @@ -810,52 +687,133 @@ static const int8_t dither_pattern[4][4] = { }; #undef X +static int bits_per_pixel(cairo_xlib_surface_t *surface) +{ + if (surface->depth > 16) + return 32; + else if (surface->depth > 8) + return 16; + else if (surface->depth > 1) + return 8; + else + return 1; +} -static cairo_status_t +pixman_format_code_t +_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface) +{ + cairo_format_masks_t masks; + pixman_format_code_t format; + + masks.bpp = bits_per_pixel (surface); + masks.alpha_mask = surface->a_mask; + masks.red_mask = surface->r_mask; + masks.green_mask = surface->g_mask; + masks.blue_mask = surface->b_mask; + if (! _pixman_format_from_masks (&masks, &format)) + return 0; + + return format; +} + +static cairo_surface_t * _get_image_surface (cairo_xlib_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect) + const cairo_rectangle_int_t *extents, + int try_shm) { cairo_int_status_t status; cairo_image_surface_t *image = NULL; XImage *ximage; - cairo_rectangle_int_t extents; pixman_format_code_t pixman_format; - cairo_format_masks_t xlib_masks; cairo_xlib_display_t *display; - extents.x = 0; - extents.y = 0; - extents.width = surface->width; - extents.height = surface->height; + assert (extents->x >= 0); + assert (extents->y >= 0); + assert (extents->x + extents->width <= surface->width); + assert (extents->y + extents->height <= surface->height); - if (interest_rect) { - if (! _cairo_rectangle_intersect (&extents, interest_rect)) { - *image_out = NULL; - return CAIRO_STATUS_SUCCESS; + if (surface->base.is_clear || + (surface->base.serial == 0 && surface->owns_pixmap)) + { + pixman_format = _pixman_format_for_xlib_surface (surface); + if (pixman_format) + { + return _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + extents->width, + extents->height, + 0); } } + if (surface->shm) { + cairo_image_surface_t *src = (cairo_image_surface_t *) surface->shm; + cairo_surface_t *dst; + cairo_surface_pattern_t pattern; + + dst = cairo_image_surface_create (src->format, + extents->width, extents->height); + if (unlikely (dst->status)) + return dst; + + _cairo_pattern_init_for_surface (&pattern, &src->base); + cairo_matrix_init_translate (&pattern.base.matrix, + extents->x, extents->y); + status = _cairo_surface_paint (dst, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) { + cairo_surface_destroy (dst); + dst = _cairo_surface_create_in_error (status); + } + + return dst; + } + status = _cairo_xlib_display_acquire (surface->base.device, &display); if (status) - return status; + return _cairo_surface_create_in_error (status); - if (image_rect) - *image_rect = extents; + pixman_format = _pixman_format_for_xlib_surface (surface); + if (try_shm && pixman_format) { + image = (cairo_image_surface_t *) + _cairo_xlib_surface_create_shm__image (surface, pixman_format, + extents->width, extents->height); + if (image && image->base.status == CAIRO_STATUS_SUCCESS) { + cairo_xlib_error_func_t old_handler; + XImage shm_image; + Bool success; - /* XXX: This should try to use the XShm extension if available */ + _cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image); - if (surface->use_pixmap == 0) - { + XSync (display->display, False); + old_handler = XSetErrorHandler (_noop_error_handler); + success = XShmGetImage (display->display, + surface->drawable, + &shm_image, + extents->x, extents->y, + AllPlanes); + XSetErrorHandler (old_handler); + + if (success) { + cairo_device_release (&display->base); + return &image->base; + } + + cairo_surface_destroy (&image->base); + image = NULL; + } + } + + if (surface->use_pixmap == 0) { cairo_xlib_error_func_t old_handler; + XSync (display->display, False); old_handler = XSetErrorHandler (_noop_error_handler); ximage = XGetImage (display->display, surface->drawable, - extents.x, extents.y, - extents.width, extents.height, + extents->x, extents->y, + extents->width, extents->height, AllPlanes, ZPixmap); XSetErrorHandler (old_handler); @@ -865,9 +823,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, */ if (!ximage) surface->use_pixmap = CAIRO_ASSUME_PIXMAP; - } - else - { + } else { surface->use_pixmap--; ximage = NULL; } @@ -888,20 +844,26 @@ _get_image_surface (cairo_xlib_surface_t *surface, pixmap = XCreatePixmap (display->display, surface->drawable, - extents.width <= 0 ? 1 : extents.width, - extents.height <= 0 ? 1 : extents.height, + extents->width, extents->height, surface->depth); if (pixmap) { + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + XCopyArea (display->display, surface->drawable, pixmap, gc, - extents.x, extents.y, - extents.width, extents.height, + extents->x, extents->y, + extents->width, extents->height, 0, 0); + gcv.subwindow_mode = ClipByChildren; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + ximage = XGetImage (display->display, pixmap, 0, 0, - extents.width <= 0 ? 1 : extents.width, - extents.height <= 0 ? 1 : extents.height, + extents->width, extents->height, AllPlanes, ZPixmap); XFreePixmap (display->display, pixmap); @@ -917,19 +879,13 @@ _get_image_surface (cairo_xlib_surface_t *surface, _swap_ximage_to_native (ximage); - xlib_masks.bpp = ximage->bits_per_pixel; - xlib_masks.alpha_mask = surface->a_mask; - xlib_masks.red_mask = surface->r_mask; - xlib_masks.green_mask = surface->g_mask; - xlib_masks.blue_mask = surface->b_mask; - /* We can't use pixman to simply write to image if: * (a) the pixels are not appropriately aligned, * (b) pixman does not the pixel format, or * (c) if the image is palettized and we need to convert. */ - if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && - _pixman_format_from_masks (&xlib_masks, &pixman_format) && + if (pixman_format && + ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && (surface->visual == NULL || surface->visual->class == TrueColor)) { image = (cairo_image_surface_t*) @@ -1014,8 +970,8 @@ _get_image_surface (cairo_xlib_surface_t *surface, data = cairo_image_surface_get_data (&image->base); rowstride = cairo_image_surface_get_stride (&image->base) >> 2; row = (uint32_t *) data; - x0 = extents.x + surface->base.device_transform.x0; - y0 = extents.y + surface->base.device_transform.y0; + x0 = extents->x + surface->base.device_transform.x0; + y0 = extents->y + surface->base.device_transform.y0; for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); y < ximage->height; y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { @@ -1028,7 +984,7 @@ _get_image_surface (cairo_xlib_surface_t *surface, in_pixel = XGetPixel (ximage, x, y); if (visual_info == NULL) { out_pixel = ( - _field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | + (uint32_t)_field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 | _field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 | _field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 | _field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment)); @@ -1050,116 +1006,92 @@ _get_image_surface (cairo_xlib_surface_t *surface, cairo_device_release (&display->base); if (unlikely (status)) { - if (image) { + if (image) cairo_surface_destroy (&image->base); - image = NULL; - } - } - *image_out = image; - return status; -} - -static void -_cairo_xlib_surface_ensure_src_picture (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (!surface->src_picture) { - XRenderPictureAttributes pa; - int mask = 0; - - pa.subwindow_mode = IncludeInferiors; - mask |= CPSubwindowMode; - - surface->src_picture = XRenderCreatePicture (display->display, - surface->drawable, - surface->xrender_format, - mask, &pa); - } -} - -static void -_cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (surface->clip_region != NULL) { - XRenderSetPictureClipRectangles (display->display, surface->dst_picture, - 0, 0, - surface->clip_rects, - surface->num_clip_rects); - } else { - XRenderPictureAttributes pa; - pa.clip_mask = None; - XRenderChangePicture (display->display, surface->dst_picture, - CPClipMask, &pa); + return _cairo_surface_create_in_error (status); } - surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE; + return &image->base; } -static void -_cairo_xlib_surface_set_precision (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, +void +_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface, cairo_antialias_t antialias) { + cairo_xlib_display_t *display = surface->display; int precision; - switch (antialias) { + if (display->force_precision != -1) + precision = display->force_precision; + else switch (antialias) { + default: case CAIRO_ANTIALIAS_DEFAULT: case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_NONE: + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GOOD: precision = PolyModeImprecise; break; - case CAIRO_ANTIALIAS_NONE: + case CAIRO_ANTIALIAS_BEST: case CAIRO_ANTIALIAS_SUBPIXEL: precision = PolyModePrecise; break; } - /* NVidia's driver version 190.42 is much slower when using PolyModeInprecise */ - precision = PolyModePrecise; - if (surface->precision != precision) { XRenderPictureAttributes pa; pa.poly_mode = precision; - XRenderChangePicture (display->display, surface->dst_picture, + XRenderChangePicture (display->display, surface->picture, CPPolyMode, &pa); surface->precision = precision; } } -static void -_cairo_xlib_surface_ensure_dst_picture (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) +void +_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface) { - if (!surface->dst_picture) { - surface->dst_picture = XRenderCreatePicture (display->display, - surface->drawable, - surface->xrender_format, - 0, NULL); - } + cairo_xlib_display_t *display = surface->display; + XRenderPictureAttributes pa; + int mask = 0; - if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) - _cairo_xlib_surface_set_picture_clip_rects (display, surface); + if (surface->picture) + return; + + if (display->force_precision != -1) + pa.poly_mode = display->force_precision; + else + pa.poly_mode = PolyModeImprecise; + if (pa.poly_mode) + mask |= CPPolyMode; + + surface->precision = pa.poly_mode; + surface->picture = XRenderCreatePicture (display->display, + surface->drawable, + surface->xrender_format, + mask, &pa); } -static cairo_status_t -_draw_image_surface (cairo_xlib_surface_t *surface, - cairo_image_surface_t *image, - int src_x, - int src_y, - int width, - int height, - int dst_x, - int dst_y) +cairo_status_t +_cairo_xlib_surface_draw_image (cairo_xlib_surface_t *surface, + cairo_image_surface_t *image, + int src_x, + int src_y, + int width, + int height, + int dst_x, + int dst_y) { cairo_xlib_display_t *display; XImage ximage; cairo_format_masks_t image_masks; - int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst; + int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; + cairo_surface_t *shm_image = NULL; pixman_image_t *pixman_image = NULL; cairo_status_t status; - cairo_bool_t own_data; + cairo_bool_t own_data = FALSE; + cairo_bool_t is_rgb_image; GC gc; ximage.width = image->width; @@ -1174,12 +1106,63 @@ _draw_image_surface (cairo_xlib_surface_t *surface, ximage.green_mask = surface->g_mask; ximage.blue_mask = surface->b_mask; ximage.xoffset = 0; + ximage.obdata = NULL; status = _cairo_xlib_display_acquire (surface->base.device, &display); if (unlikely (status)) return status; - if (!_pixman_format_to_masks (image->pixman_format, &image_masks)) + is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks); + + if (is_rgb_image && + (image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && + (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && + (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && + (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) + { + int ret; + + ximage.bits_per_pixel = image_masks.bpp; + ximage.bytes_per_line = image->stride; + ximage.data = (char *)image->data; + if (image->base.device != surface->base.device) { + /* If PutImage will break the image up into chunks, prefer to + * send it all in one pass with ShmPutImage. For larger images, + * it is further advantageous to reduce the number of copies, + * albeit at the expense of more SHM bookkeeping. + */ + int max_request_size = XExtendedMaxRequestSize (display->display); + if (max_request_size == 0) + max_request_size = XMaxRequestSize (display->display); + if (max_request_size > 8192) + max_request_size = 8192; + if (width * height * 4 > max_request_size) { + shm_image = _cairo_xlib_surface_create_shm__image (surface, + image->pixman_format, + width, height); + if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) { + cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image; + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, clone->pixman_image, + src_x, src_y, + 0, 0, + 0, 0, + width, height); + ximage.obdata = _cairo_xlib_shm_surface_get_obdata (shm_image); + ximage.data = (char *)clone->data; + ximage.bytes_per_line = clone->stride; + ximage.width = width; + ximage.height = height; + src_x = src_y = 0; + } + } + } else + ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&image->base); + + ret = XInitImage (&ximage); + assert (ret != 0); + } + else if (surface->visual == NULL || surface->visual->class == TrueColor) { pixman_format_code_t intermediate_format; int ret; @@ -1188,52 +1171,57 @@ _draw_image_surface (cairo_xlib_surface_t *surface, image_masks.red_mask = surface->r_mask; image_masks.green_mask = surface->g_mask; image_masks.blue_mask = surface->b_mask; - image_masks.bpp = surface->depth; + image_masks.bpp = bits_per_pixel (surface); ret = _pixman_format_from_masks (&image_masks, &intermediate_format); assert (ret); - own_data = FALSE; + shm_image = _cairo_xlib_surface_create_shm__image (surface, + intermediate_format, + width, height); + if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) { + cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image; - pixman_image = pixman_image_create_bits (intermediate_format, - image->width, - image->height, - NULL, - 0); - if (pixman_image == NULL) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, + NULL, + clone->pixman_image, + src_x, src_y, + 0, 0, + 0, 0, + width, height); - pixman_image_composite32 (PIXMAN_OP_SRC, - image->pixman_image, - NULL, - pixman_image, - 0, 0, - 0, 0, - 0, 0, - image->width, image->height); + ximage.data = (char *) clone->data; + ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&clone->base); + ximage.bytes_per_line = clone->stride; + } else { + pixman_image = pixman_image_create_bits (intermediate_format, + width, height, NULL, 0); + if (pixman_image == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, + NULL, + pixman_image, + src_x, src_y, + 0, 0, + 0, 0, + width, height); + + ximage.data = (char *) pixman_image_get_data (pixman_image); + ximage.bytes_per_line = pixman_image_get_stride (pixman_image); + } + + ximage.width = width; + ximage.height = height; ximage.bits_per_pixel = image_masks.bpp; - ximage.data = (char *) pixman_image_get_data (pixman_image); - ximage.bytes_per_line = pixman_image_get_stride (pixman_image); ret = XInitImage (&ximage); assert (ret != 0); - } - else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) && - (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) && - (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) && - (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0)) - { - int ret; - ximage.bits_per_pixel = image_masks.bpp; - ximage.bytes_per_line = image->stride; - ximage.data = (char *)image->data; - own_data = FALSE; - - ret = XInitImage (&ximage); - assert (ret != 0); + src_x = src_y = 0; } else { @@ -1248,20 +1236,12 @@ _draw_image_surface (cairo_xlib_surface_t *surface, cairo_bool_t true_color; int ret; - if (surface->depth > 16) - ximage.bits_per_pixel = 32; - else if (surface->depth > 8) - ximage.bits_per_pixel = 16; - else if (surface->depth > 1) - ximage.bits_per_pixel = 8; - else - ximage.bits_per_pixel = 1; + ximage.bits_per_pixel = bits_per_pixel(surface); stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width, ximage.bits_per_pixel); ximage.bytes_per_line = stride; ximage.data = _cairo_malloc_ab (stride, ximage.height); if (unlikely (ximage.data == NULL)) { - own_data = FALSE; status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } @@ -1360,1670 +1340,169 @@ _draw_image_surface (cairo_xlib_surface_t *surface, if (unlikely (status)) goto BAIL; - XPutImage (display->display, surface->drawable, gc, - &ximage, src_x, src_y, dst_x, dst_y, - width, height); + if (ximage.obdata) + XShmPutImage (display->display, surface->drawable, gc, &ximage, + src_x, src_y, dst_x, dst_y, width, height, True); + else + XPutImage (display->display, surface->drawable, gc, &ximage, + src_x, src_y, dst_x, dst_y, width, height); _cairo_xlib_surface_put_gc (display, surface, gc); BAIL: - cairo_device_release (&display->base); if (own_data) free (ximage.data); + if (shm_image) + cairo_surface_destroy (shm_image); if (pixman_image) pixman_image_unref (pixman_image); return CAIRO_STATUS_SUCCESS; } +static cairo_surface_t * +_cairo_xlib_surface_source(void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xlib_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_xlib_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; + cairo_rectangle_int_t extents; - status = _get_image_surface (surface, NULL, &image, NULL); - if (unlikely (status)) - return status; - - *image_out = image; *image_extra = NULL; + *image_out = (cairo_image_surface_t *) + _cairo_xlib_surface_get_shm (abstract_surface, FALSE); + if (*image_out) + return (*image_out)->base.status; - return CAIRO_STATUS_SUCCESS; + extents.x = extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + + *image_out = (cairo_image_surface_t*) + _get_image_surface (surface, &extents, TRUE); + return (*image_out)->base.status; } static cairo_surface_t * _cairo_xlib_surface_snapshot (void *abstract_surface) { cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; + cairo_rectangle_int_t extents; - status = _get_image_surface (surface, NULL, &image, NULL); - if (unlikely (status)) - return _cairo_surface_create_in_error (status); + extents.x = extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; - return &image->base; + return _get_image_surface (surface, &extents, FALSE); } static void _cairo_xlib_surface_release_source_image (void *abstract_surface, cairo_image_surface_t *image, void *image_extra) -{ - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) { cairo_xlib_surface_t *surface = abstract_surface; - cairo_image_surface_t *image; - cairo_status_t status; - status = _get_image_surface (surface, interest_rect, &image, image_rect_out); - if (unlikely (status)) - return status; - - *image_out = image; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_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_xlib_surface_t *surface = abstract_surface; - cairo_status_t status; - - status = _draw_image_surface (surface, image, - 0, 0, image->width, image->height, - image_rect->x, image_rect->y); - status = _cairo_surface_set_error (&surface->base, status); + if (&image->base == surface->shm) + return; cairo_surface_destroy (&image->base); } -/* - * Return whether two xlib surfaces share the same - * screen. Both core and Render drawing require this - * when using multiple drawables in an operation. - */ -static inline cairo_bool_t -_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst, - cairo_xlib_surface_t *src) -{ - return dst->screen == src->screen; -} - -static cairo_status_t -_cairo_xlib_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) +static cairo_image_surface_t * +_cairo_xlib_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) { cairo_xlib_surface_t *surface = abstract_surface; - cairo_xlib_surface_t *clone; - cairo_status_t status; + cairo_surface_t *image; - if (src->backend == surface->base.backend ) { - cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; - - if (_cairo_xlib_surface_same_screen (surface, xlib_src)) { - *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; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return UNSUPPORTED ("roi too large for xlib"); - - clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (surface, - image_src->base.content, - width, height); - if (clone == NULL) - return UNSUPPORTED ("unhandled image format, no similar surface"); - - if (unlikely (clone->base.status)) - return clone->base.status; - - status = _draw_image_surface (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (unlikely (status)) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &clone->base; - - return CAIRO_STATUS_SUCCESS; + image = _cairo_xlib_surface_get_shm (abstract_surface, FALSE); + if (image) { + assert (surface->base.damage); + surface->fallback++; + return _cairo_image_surface_map_to_image (image, extents); } - return CAIRO_INT_STATUS_UNSUPPORTED; -} + image = _get_image_surface (abstract_surface, extents, TRUE); + cairo_surface_set_device_offset (image, -extents->x, -extents->y); -static cairo_surface_t * -_cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - /* This function's only responsibility is to create a proper surface - * for when XRender is not available. The proper surface is a xlib - * surface (as opposed to image surface which is what create_similar - * returns in those cases) and the size of the dithering pattern, not - * 1x1. This surface can then be used in - * _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid" - * fills using core protocol */ - - cairo_xlib_surface_t *other = abstract_surface; - cairo_image_surface_t *image; - cairo_xlib_surface_t *surface = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xlib_display_t *display; - - int width = ARRAY_LENGTH (dither_pattern[0]); - int height = ARRAY_LENGTH (dither_pattern); - - Pixmap pixmap = None; - - if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) - return NULL; - - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_content (_cairo_color_get_content (&solid_pattern->color), - width, height); - status = image->base.status; - if (unlikely (status)) - goto BAIL; - - status = _cairo_xlib_display_acquire (other->base.device, &display); - if (unlikely (status)) - goto BAIL; - - pixmap = XCreatePixmap (display->display, - other->drawable, - width, height, - other->depth); - cairo_device_release (&display->base); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (other->screen, - pixmap, - other->visual, - other->xrender_format, - width, height, - other->depth); - status = surface->base.status; - if (unlikely (status)) - goto BAIL; - - status = _cairo_surface_paint (&image->base, - CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, - NULL); - if (unlikely (status)) - goto BAIL; - - status = _draw_image_surface (surface, image, - 0, 0, - width, height, - 0, 0); - if (unlikely (status)) - goto BAIL; - - BAIL: - cairo_surface_destroy (&image->base); - - if (status) { - if (pixmap != None) { - if (!_cairo_xlib_display_acquire (other->base.device, &display)) { - XFreePixmap (display->display, pixmap); - cairo_device_release (&display->base); - } - } - cairo_surface_destroy (&surface->base); - - return _cairo_surface_create_in_error (status); - } - - surface->owns_pixmap = TRUE; - return &surface->base; -} - -static cairo_bool_t -_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - cairo_xlib_surface_t *other = abstract_surface; - return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); -} - -static cairo_status_t -_cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - const cairo_matrix_t *matrix, - double xc, - double yc) -{ - XTransform xtransform; - - /* Casting between pixman_transform_t and XTransform is safe because - * they happen to be the exact same type. - */ - _cairo_matrix_to_pixman_matrix (matrix, - (pixman_transform_t *) &xtransform, - xc, yc); - - if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) - return CAIRO_STATUS_SUCCESS; - - if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - return UNSUPPORTED ("XRender does not support picture transforms"); - - XRenderSetPictureTransform (display->display, surface->src_picture, &xtransform); - surface->xtransform = xtransform; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_filter (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - cairo_filter_t filter) -{ - const char *render_filter; - - if (surface->filter == filter) - return CAIRO_STATUS_SUCCESS; - - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - return CAIRO_STATUS_SUCCESS; - - return UNSUPPORTED ("XRender does not support filter"); - } - - switch (filter) { - case CAIRO_FILTER_FAST: - render_filter = FilterFast; - break; - case CAIRO_FILTER_GOOD: - render_filter = FilterGood; - break; - case CAIRO_FILTER_BEST: - render_filter = FilterBest; - break; - case CAIRO_FILTER_NEAREST: - render_filter = FilterNearest; - break; - case CAIRO_FILTER_BILINEAR: - render_filter = FilterBilinear; - break; - case CAIRO_FILTER_GAUSSIAN: - /* XXX: The GAUSSIAN value has no implementation in cairo - * whatsoever, so it was really a mistake to have it in the - * API. We could fix this by officially deprecating it, or - * else inventing semantics and providing an actual - * implementation for it. */ - default: - render_filter = FilterBest; - break; - } - - XRenderSetPictureFilter (display->display, surface->src_picture, - (char *) render_filter, NULL, 0); - surface->filter = filter; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, - cairo_extend_t extend, - unsigned long *mask, - XRenderPictureAttributes *pa) -{ - int repeat; - - if (surface->extend == extend) - return CAIRO_STATUS_SUCCESS; - - switch (extend) { - case CAIRO_EXTEND_NONE: - repeat = RepeatNone; - break; - case CAIRO_EXTEND_REPEAT: - repeat = RepeatNormal; - break; - case CAIRO_EXTEND_REFLECT: - if (surface->buggy_pad_reflect) - return UNSUPPORTED ("buggy reflect"); - - repeat = RepeatReflect; - break; - case CAIRO_EXTEND_PAD: - if (surface->buggy_pad_reflect) - return UNSUPPORTED ("buggy pad"); - - repeat = RepeatPad; - break; - default: - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - *mask |= CPRepeat; - pa->repeat = repeat; - - surface->extend = extend; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface, - cairo_bool_t ca, - unsigned long *mask, - XRenderPictureAttributes *pa) -{ - if (surface->has_component_alpha == ca) - return CAIRO_STATUS_SUCCESS; - - *mask |= CPComponentAlpha; - pa->component_alpha = ca; - - surface->has_component_alpha = ca; - return CAIRO_STATUS_SUCCESS; + return (cairo_image_surface_t *) image; } static cairo_int_status_t -_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - const cairo_surface_attributes_t *attributes, - double xc, - double yc) +_cairo_xlib_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) { + cairo_xlib_surface_t *surface = abstract_surface; cairo_int_status_t status; - XRenderPictureAttributes pa; - unsigned long mask = 0; - _cairo_xlib_surface_ensure_src_picture (display, surface); + if (surface->shm) { + cairo_rectangle_int_t r; - status = _cairo_xlib_surface_set_matrix (display, surface, - &attributes->matrix, xc, yc); - if (unlikely (status)) - return status; + assert (surface->fallback); + assert (surface->base.damage); - status = _cairo_xlib_surface_set_repeat (surface, attributes->extend, - &mask, &pa); - if (unlikely (status)) - return status; + r.x = image->base.device_transform_inverse.x0; + r.y = image->base.device_transform_inverse.y0; + r.width = image->width; + r.height = image->height; - status = _cairo_xlib_surface_set_component_alpha (surface, - attributes->has_component_alpha, - &mask, &pa); - if (unlikely (status)) - return status; + TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n", + __FUNCTION__, r.x, r.y, r.width, r.height)); + surface->shm->damage = + _cairo_damage_add_rectangle (surface->shm->damage, &r); - status = _cairo_xlib_surface_set_filter (display, surface, attributes->filter); - if (unlikely (status)) - return status; - - if (mask) - XRenderChangePicture (display->display, surface->src_picture, mask, &pa); - - return CAIRO_STATUS_SUCCESS; -} - -/* Checks whether we can can directly draw from src to dst with - * the core protocol: either with CopyArea or using src as a - * a tile in a GC. - */ -static cairo_bool_t -_surfaces_compatible (cairo_xlib_surface_t *dst, - cairo_xlib_surface_t *src) -{ - /* same screen */ - if (! _cairo_xlib_surface_same_screen (dst, src)) - return FALSE; - - /* same depth (for core) */ - if (src->depth != dst->depth) - return FALSE; - - /* if Render is supported, match picture formats */ - if (src->xrender_format != dst->xrender_format) - return FALSE; - else if (src->xrender_format != NULL) - return TRUE; - - /* Without Render, match visuals instead */ - if (src->visual == dst->visual) - return TRUE; - - return FALSE; -} - -static cairo_bool_t -_surface_has_alpha (cairo_xlib_surface_t *surface) -{ - if (surface->xrender_format) { - if (surface->xrender_format->type == PictTypeDirect && - surface->xrender_format->direct.alphaMask != 0) - return TRUE; - else - return FALSE; - } else { - /* In the no-render case, we never have alpha */ - return FALSE; - } -} - -/* Returns true if the given operator and alpha combination requires alpha - * compositing to complete on source and destination surfaces with the same - * format. i.e. if a simple bitwise copy is not appropriate. - */ -static cairo_bool_t -_operator_needs_alpha_composite (cairo_operator_t op, - cairo_bool_t surfaces_have_alpha) -{ - if (op == CAIRO_OPERATOR_SOURCE) - return FALSE; - - if (op == CAIRO_OPERATOR_OVER || - op == CAIRO_OPERATOR_IN || - op == CAIRO_OPERATOR_ATOP) - return surfaces_have_alpha; - - return TRUE; -} - -/* There is a bug in most older X servers with compositing using a - * untransformed repeating source pattern when the source is in off-screen - * video memory, and another with repeated transformed images using a - * general transform matrix. When these bugs could be triggered, we need a - * fallback: in the common case where we have no transformation and the - * source and destination have the same format/visual, we can do the - * operation using the core protocol for the first bug, otherwise, we need - * a software fallback. - * - * We can also often optimize a compositing operation by calling XCopyArea - * for some common cases where there is no alpha compositing to be done. - * We figure that out here as well. - */ -typedef enum { - DO_RENDER, /* use render */ - DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ - DO_XTILE, /* core protocol XSetTile optimization/fallback */ - DO_UNSUPPORTED /* software fallback */ -} composite_operation_t; - -/* Initial check for the render bugs; we need to recheck for the - * offscreen-memory bug after we turn patterns into surfaces, since that - * may introduce a repeating pattern for gradient patterns. We don't need - * to check for the repeat+transform bug because gradient surfaces aren't - * transformed. - * - * All we do here is reject cases where we *know* are going to - * hit the bug and won't be able to use a core protocol fallback. - */ -static composite_operation_t -_categorize_composite_operation (cairo_xlib_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_bool_t have_mask) - -{ - if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op)) - return DO_UNSUPPORTED; - - if (! dst->buggy_repeat) - return DO_RENDER; - - if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID && - src_pattern->extend == CAIRO_EXTEND_REPEAT) - { - /* Check for the bug with repeat patterns nad general transforms. */ - if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix, - NULL, NULL)) - { - return DO_UNSUPPORTED; - } - - if (have_mask || - !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) - { - return DO_UNSUPPORTED; - } - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern; - - /* This is the case where we have the bug involving - * untransformed repeating source patterns with off-screen - * video memory; reject some cases where a core protocol - * fallback is impossible. - */ - if (_cairo_surface_is_xlib (surface_pattern->surface)) { - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface; - - if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) - return DO_UNSUPPORTED; - - /* If these are on the same screen but otherwise incompatible, - * make a copy as core drawing can't cross depths and doesn't - * work right across visuals of the same depth - */ - if (_cairo_xlib_surface_same_screen (dst, src) && - !_surfaces_compatible (dst, src)) - { - return DO_UNSUPPORTED; - } - } - } + return _cairo_image_surface_unmap_image (surface->shm, image); } - return DO_RENDER; -} - -/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces - * If we end up returning DO_UNSUPPORTED here, we're throwing away work we - * did to turn gradients into a pattern, but most of the time we can handle - * that case with core protocol fallback. - * - * Also check here if we can just use XCopyArea, instead of going through - * Render. - */ -static composite_operation_t -_recategorize_composite_operation (cairo_xlib_surface_t *dst, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *src_attr, - cairo_bool_t have_mask) -{ - /* Can we use the core protocol? */ - if (! have_mask && - _surfaces_compatible (src, dst) && - _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - ! _operator_needs_alpha_composite (op, _surface_has_alpha (dst))) - { - if (src_attr->extend == CAIRO_EXTEND_NONE) - return DO_XCOPYAREA; - - if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) - return DO_XTILE; - } - - if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT && - (src->width != 1 || src->height != 1)) - return DO_UNSUPPORTED; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) - return DO_UNSUPPORTED; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return DO_UNSUPPORTED; - - return DO_RENDER; -} - -static int -_render_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return PictOpClear; - - case CAIRO_OPERATOR_SOURCE: - return PictOpSrc; - case CAIRO_OPERATOR_OVER: - return PictOpOver; - case CAIRO_OPERATOR_IN: - return PictOpIn; - case CAIRO_OPERATOR_OUT: - return PictOpOut; - case CAIRO_OPERATOR_ATOP: - return PictOpAtop; - - case CAIRO_OPERATOR_DEST: - return PictOpDst; - case CAIRO_OPERATOR_DEST_OVER: - return PictOpOverReverse; - case CAIRO_OPERATOR_DEST_IN: - return PictOpInReverse; - case CAIRO_OPERATOR_DEST_OUT: - return PictOpOutReverse; - case CAIRO_OPERATOR_DEST_ATOP: - return PictOpAtopReverse; - - case CAIRO_OPERATOR_XOR: - return PictOpXor; - case CAIRO_OPERATOR_ADD: - return PictOpAdd; - case CAIRO_OPERATOR_SATURATE: - return PictOpSaturate; - - case CAIRO_OPERATOR_MULTIPLY: - return PictOpMultiply; - case CAIRO_OPERATOR_SCREEN: - return PictOpScreen; - case CAIRO_OPERATOR_OVERLAY: - return PictOpOverlay; - case CAIRO_OPERATOR_DARKEN: - return PictOpDarken; - case CAIRO_OPERATOR_LIGHTEN: - return PictOpLighten; - case CAIRO_OPERATOR_COLOR_DODGE: - return PictOpColorDodge; - case CAIRO_OPERATOR_COLOR_BURN: - return PictOpColorBurn; - case CAIRO_OPERATOR_HARD_LIGHT: - return PictOpHardLight; - case CAIRO_OPERATOR_SOFT_LIGHT: - return PictOpSoftLight; - case CAIRO_OPERATOR_DIFFERENCE: - return PictOpDifference; - case CAIRO_OPERATOR_EXCLUSION: - return PictOpExclusion; - case CAIRO_OPERATOR_HSL_HUE: - return PictOpHSLHue; - case CAIRO_OPERATOR_HSL_SATURATION: - return PictOpHSLSaturation; - case CAIRO_OPERATOR_HSL_COLOR: - return PictOpHSLColor; - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return PictOpHSLLuminosity; - - default: - ASSERT_NOT_REACHED; - return PictOpOver; - } -} - -static cairo_int_status_t -_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - const cairo_pattern_t *pattern, - int x, int y, - int width, int height, - cairo_xlib_surface_t **surface_out, - cairo_surface_attributes_t *attributes) -{ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - cairo_matrix_t matrix = pattern->matrix; - cairo_xlib_surface_t *surface; - char buf[CAIRO_STACK_BUFFER_SIZE]; - XFixed *stops; - XRenderColor *colors; - XRenderPictFormat *format; - Picture picture; - unsigned int i; - - if (dst->buggy_gradients) - break; - - if (gradient->n_stops < 2) /* becomes a solid */ - break; - - if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) - { - stops = (XFixed *) buf; - } - else - { - stops = - _cairo_malloc_ab (gradient->n_stops, - sizeof (XFixed) + sizeof (XRenderColor)); - if (unlikely (stops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - colors = (XRenderColor *) (stops + gradient->n_stops); - for (i = 0; i < gradient->n_stops; i++) { - stops[i] = - _cairo_fixed_16_16_from_double (gradient->stops[i].offset); - - colors[i].red = gradient->stops[i].color.red_short; - colors[i].green = gradient->stops[i].color.green_short; - colors[i].blue = gradient->stops[i].color.blue_short; - colors[i].alpha = gradient->stops[i].color.alpha_short; - } - -#if 0 - /* For some weird reason the X server is sometimes getting - * CreateGradient requests with bad length. So far I've only seen - * XRenderCreateLinearGradient request with 4 stops sometime end up - * with length field matching 0 stops at the server side. I've - * looked at the libXrender code and I can't see anything that - * could cause this behavior. However, for some reason having a - * XSync call here seems to avoid the issue so I'll keep it here - * until it's solved. - */ - XSync (display->display, False); -#endif - - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; - XLinearGradient grad; - - cairo_fixed_t xdim, ydim; - - xdim = linear->p2.x - linear->p1.x; - ydim = linear->p2.y - linear->p1.y; - - /* - * Transform the matrix to avoid overflow when converting between - * cairo_fixed_t and pixman_fixed_t (without incurring performance - * loss when the transformation is unnecessary). - * - * XXX: Consider converting out-of-range co-ordinates and transforms. - * Having a function to compute the required transformation to - * "normalize" a given bounding box would be generally useful - - * cf linear patterns, gradient patterns, surface patterns... - */ -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ - if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT || - _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT) - { - double sf; - - if (xdim > ydim) - sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim); - else - sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim); - - grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf); - grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf); - grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf); - grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf); - - cairo_matrix_scale (&matrix, sf, sf); - } - else - { - grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x); - grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y); - grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x); - grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y); - } - - picture = XRenderCreateLinearGradient (display->display, &grad, - stops, colors, - gradient->n_stops); - } else { - cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; - XRadialGradient grad; - - grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x); - grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y); - grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1); - - grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x); - grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y); - grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2); - - picture = XRenderCreateRadialGradient (display->display, &grad, - stops, colors, - gradient->n_stops); - - } - - if (stops != (XFixed *) buf) - free (stops); - - if (unlikely (picture == None)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* Wrap the remote Picture in an xlib surface. */ - format = _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_ARGB32); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (dst->screen, None, - NULL, format, - /* what could possibly go wrong? */ - XLIB_COORD_MAX, XLIB_COORD_MAX, 32); - if (unlikely (surface->base.status)) { - XRenderFreePicture (display->display, picture); - return surface->base.status; - } - - surface->src_picture = picture; - - attributes->matrix = matrix; - attributes->extend = pattern->extend; - attributes->filter = CAIRO_FILTER_NEAREST; - attributes->x_offset = 0; - attributes->y_offset = 0; - attributes->has_component_alpha = FALSE; - - *surface_out = surface; - return CAIRO_STATUS_SUCCESS; - } - default: - ASSERT_NOT_REACHED; - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_SURFACE: - break; - } - - return _cairo_pattern_acquire_surface (pattern, &dst->base, - x, y, width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) surface_out, - attributes); -} - -static cairo_int_status_t -_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_xlib_surface_t **src_out, - cairo_xlib_surface_t **mask_out, - cairo_surface_attributes_t *src_attr, - cairo_surface_attributes_t *mask_attr) -{ - if (! dst->buggy_gradients && - (src->type == CAIRO_PATTERN_TYPE_LINEAR || - src->type == CAIRO_PATTERN_TYPE_RADIAL || - (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR || - mask->type == CAIRO_PATTERN_TYPE_RADIAL)))) - { - cairo_int_status_t status; - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, src, - src_x, src_y, - width, height, - src_out, - src_attr); - if (unlikely (status)) - return status; - - if (mask) { - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, mask, - mask_x, - mask_y, - width, - height, - mask_out, - mask_attr); - if (unlikely (status)) { - _cairo_pattern_release_surface (src, &(*src_out)->base, - src_attr); - return status; - } - } else { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pattern_acquire_surfaces (src, mask, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) src_out, - (cairo_surface_t **) mask_out, - src_attr, mask_attr); -} - -static cairo_int_status_t -_cairo_xlib_surface_upload(cairo_xlib_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int src_x, int src_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *image; - cairo_rectangle_int_t extents; - cairo_status_t status; - int tx, ty; - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - image = (cairo_image_surface_t *) ((cairo_surface_pattern_t *) pattern)->surface; - if (image->base.type != CAIRO_SURFACE_TYPE_IMAGE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && - (image->base.content & CAIRO_CONTENT_ALPHA) == 0))) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (image->base.backend->type != CAIRO_SURFACE_TYPE_IMAGE) { - if (image->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { - image = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) image)->target; - extents.x = extents.y = 0; - extents.width = image->width; - extents.height = image->height; - } else if (image->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) image; - image = (cairo_image_surface_t *) sub->target; - src_x += sub->extents.x; - src_y += sub->extents.y; - extents = sub->extents; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - extents.x = extents.y = 0; - extents.width = image->width; - extents.height = image->height; - } - - if (image->format == CAIRO_FORMAT_INVALID) - return CAIRO_INT_STATUS_UNSUPPORTED; - if (image->depth != surface->depth) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! _cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - src_x += tx; - src_y += ty; - - /* XXX for EXTEND_NONE perform unbounded fixups? */ - if (src_x < extents.x || - src_y < extents.y || - src_x + width > (unsigned) extents.width || - src_y + height > (unsigned) extents.height) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = cairo_device_acquire (surface->base.device); - if (unlikely (status)) - return status; - - if (clip_region != NULL) { - int n, num_rect; - - src_x -= dst_x; - src_y -= dst_y; - - num_rect = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rect; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - status = _draw_image_surface (surface, image, - rect.x + src_x, rect.y + src_y, - rect.width, rect.height, - rect.x, rect.y); - if (unlikely (status)) - break; - } - } else { - status = _draw_image_surface (surface, image, - src_x, src_y, - width, height, - dst_x, dst_y); - } - - cairo_device_release (surface->base.device); - - return status; -} - -static cairo_int_status_t -_cairo_xlib_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_surface_attributes_t src_attr, mask_attr; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_surface_t *mask; - cairo_xlib_display_t *display; - cairo_int_status_t status; - composite_operation_t operation; - int itx, ity; - cairo_bool_t is_integer_translation; - GC gc; - - if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return UNSUPPORTED ("no support for masks"); - - operation = _categorize_composite_operation (dst, op, src_pattern, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported operation"); - - X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); - - if (mask_pattern == NULL) { - /* Can we do a simple upload in-place? */ - status = _cairo_xlib_surface_upload(dst, op, src_pattern, - src_x, src_y, - dst_x, dst_y, - width, height, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_xlib_display_acquire (dst-> base.device, &display); - if (unlikely (status)) - return status; - - status = - _cairo_xlib_surface_acquire_pattern_surfaces (display, dst, - src_pattern, mask_pattern, - src_x, src_y, - mask_x, mask_y, - width, height, - &src, &mask, - &src_attr, &mask_attr); - if (unlikely (status)) - goto BAIL0; - - /* check for fallback surfaces that we cannot handle ... */ - assert (_cairo_surface_is_xlib (&src->base)); - assert (mask == NULL || _cairo_surface_is_xlib (&mask->base)); - - if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) { - status = UNSUPPORTED ("unsupported mask"); - goto BAIL; - } - - operation = _recategorize_composite_operation (dst, op, src, &src_attr, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported operation"); - goto BAIL; - } - - switch (operation) - { - case DO_RENDER: - status = _cairo_xlib_surface_set_attributes (display, - src, &src_attr, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto BAIL; - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - if (mask) { - status = _cairo_xlib_surface_set_attributes (display, - mask, &mask_attr, - dst_x + width / 2., - dst_y + height/ 2.); - if (unlikely (status)) - goto BAIL; - - XRenderComposite (display->display, - _render_operator (op), - src->src_picture, - mask->src_picture, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } else { - XRenderComposite (display->display, - _render_operator (op), - src->src_picture, - 0, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - - break; - - case DO_XCOPYAREA: - status = _cairo_xlib_surface_get_gc (display, dst, &gc); - if (unlikely (status)) - goto BAIL; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - /* This is a pre-condition for DO_XCOPYAREA. */ - assert (is_integer_translation); - - if (clip_region == NULL) { - XCopyArea (display->display, src->drawable, dst->drawable, gc, - src_x + src_attr.x_offset + itx, - src_y + src_attr.y_offset + ity, - width, height, - dst_x, dst_y); - } else { - int n, num_rects, x, y; - - x = src_x + src_attr.x_offset + itx - dst_x; - y = src_y + src_attr.y_offset + ity - dst_y; - - num_rects = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - XCopyArea (display->display, src->drawable, dst->drawable, gc, - rect.x + x, rect.y + y, - rect.width, rect.height, - rect.x, rect.y); - } - } - - _cairo_xlib_surface_put_gc (display, dst, gc); - break; - - case DO_XTILE: - /* This case is only used for bug fallbacks, though we also use it for - * the case where we don't have the RENDER extension, by forcing - * buggy_repeat to TRUE. - * - * We've checked that we have a repeating unscaled source in - * _recategorize_composite_operation. - */ - - status = _cairo_xlib_surface_get_gc (display, dst, &gc); - if (unlikely (status)) - goto BAIL; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - /* This is a pre-condition for DO_XTILE. */ - assert (is_integer_translation); - - XSetTSOrigin (display->display, gc, - - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); - XSetTile (display->display, gc, src->drawable); - - if (clip_region == NULL) { - XFillRectangle (display->display, dst->drawable, gc, - dst_x, dst_y, width, height); - } else { - int n, num_rects; - - num_rects = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - XFillRectangle (display->display, dst->drawable, gc, - rect.x, rect.y, rect.width, rect.height); - } - } - - _cairo_xlib_surface_put_gc (display, dst, gc); - break; - - case DO_UNSUPPORTED: - default: - ASSERT_NOT_REACHED; - } - - if (!_cairo_operator_bounded_by_source (op)) - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr, src->width, src->height, - mask ? &mask_attr : NULL, - mask ? mask->width : 0, - mask ? mask->height : 0, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); - - BAIL: - if (mask) - _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); - - BAIL0: - cairo_device_release (&display->base); - - return status; -} - -/* XXX move this out of core and into acquire_pattern_surface() above. */ -static cairo_int_status_t -_cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_status_t status; - cairo_solid_pattern_t solid; - cairo_surface_t *solid_surface = NULL; - cairo_surface_attributes_t attrs; - cairo_xlib_display_t *display; - GC gc; - int i; - - _cairo_pattern_init_solid (&solid, color); - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - status = _cairo_xlib_surface_get_gc (display, surface, &gc); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); - - status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, + status = _cairo_xlib_surface_draw_image (abstract_surface, image, 0, 0, - ARRAY_LENGTH (dither_pattern[0]), - ARRAY_LENGTH (dither_pattern), - CAIRO_PATTERN_ACQUIRE_NONE, - &solid_surface, - &attrs); - if (unlikely (status)) { - _cairo_xlib_surface_put_gc (display, surface, gc); - cairo_device_release (&display->base); - return status; - } + image->width, image->height, + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0); - assert (_cairo_surface_is_xlib (solid_surface)); + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); - XSetTSOrigin (display->display, gc, - - (surface->base.device_transform.x0 + attrs.x_offset), - - (surface->base.device_transform.y0 + attrs.y_offset)); - XSetTile (display->display, gc, - ((cairo_xlib_surface_t *) solid_surface)->drawable); - - for (i = 0; i < num_rects; i++) { - XFillRectangle (display->display, surface->drawable, gc, - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - } - - _cairo_xlib_surface_put_gc (display, surface, gc); - - _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); - - cairo_device_release (&display->base); - - return CAIRO_STATUS_SUCCESS; + return status; } -static cairo_int_status_t -_cairo_xlib_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) +static cairo_status_t +_cairo_xlib_surface_flush (void *abstract_surface, + unsigned flags) { cairo_xlib_surface_t *surface = abstract_surface; - cairo_xlib_display_t *display; - XRenderColor render_color; - cairo_status_t status; - int i; + cairo_int_status_t status; - if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (flags) + return CAIRO_STATUS_SUCCESS; - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { - if (op == CAIRO_OPERATOR_CLEAR || - ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) && - CAIRO_COLOR_IS_OPAQUE (color))) - { - return _cairo_xlib_surface_solid_fill_rectangles (surface, color, - rects, num_rects); - } - - return UNSUPPORTED ("no support for FillRectangles with this op"); - } - - status = _cairo_xlib_display_acquire (surface->base.device, &display); + status = _cairo_xlib_surface_put_shm (surface); if (unlikely (status)) - return status; + return status; - X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); + surface->fallback >>= 1; + if (surface->shm && _cairo_xlib_shm_surface_is_idle (surface->shm)) + _cairo_xlib_surface_discard_shm (surface); - render_color.red = color->red_short; - render_color.green = color->green_short; - render_color.blue = color->blue_short; - render_color.alpha = color->alpha_short; - - status = _cairo_xlib_surface_set_clip_region (surface, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_xlib_surface_ensure_dst_picture (display, surface); - if (num_rects == 1) { - /* Take advantage of the protocol compaction that libXrender performs - * to amalgamate sequences of XRenderFillRectangle(). - */ - XRenderFillRectangle (display->display, - _render_operator (op), - surface->dst_picture, - &render_color, - rects->x, - rects->y, - rects->width, - rects->height); - } else { - XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; - XRectangle *xrects = static_xrects; - - if (num_rects > ARRAY_LENGTH (static_xrects)) { - xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); - if (unlikely (xrects == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_rects; i++) { - xrects[i].x = rects[i].x; - xrects[i].y = rects[i].y; - xrects[i].width = rects[i].width; - xrects[i].height = rects[i].height; - } - - XRenderFillRectangles (display->display, - _render_operator (op), - surface->dst_picture, - &render_color, xrects, num_rects); - - if (xrects != static_xrects) - free (xrects); - } - -BAIL: - cairo_device_release (&display->base); - return status; -} - -#define CAIRO_FIXED_16_16_MIN -32768 -#define CAIRO_FIXED_16_16_MAX 32767 - -static cairo_bool_t -_line_exceeds_16_16 (const cairo_line_t *line) -{ - return - line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); -} - -static void -_project_line_x_onto_16_16 (const cairo_line_t *line, - cairo_fixed_t top, - cairo_fixed_t bottom, - XLineFixed *out) -{ - cairo_point_double_t p1, p2; - double m; - - p1.x = _cairo_fixed_to_double (line->p1.x); - p1.y = _cairo_fixed_to_double (line->p1.y); - - p2.x = _cairo_fixed_to_double (line->p2.x); - p2.y = _cairo_fixed_to_double (line->p2.y); - - m = (p2.x - p1.x) / (p2.y - p1.y); - out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); - out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); -} - -static cairo_int_status_t -_cairo_xlib_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_surface_attributes_t attributes; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_display_t *display; - cairo_int_status_t status; - composite_operation_t operation; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - XRenderPictFormat *pict_format; - XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; - XTrapezoid *xtraps = xtraps_stack; - int i; - - if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return UNSUPPORTED ("XRender does not support CompositeTrapezoids"); - - operation = _categorize_composite_operation (dst, op, pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported operation"); - - status = _cairo_xlib_display_acquire (dst->base.device, &display); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, - pattern, - src_x, src_y, - width, height, - &src, &attributes); - if (unlikely (status)) - goto BAIL0; - - operation = _recategorize_composite_operation (dst, op, src, - &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported operation"); - goto BAIL; - } - - switch (antialias) { - case CAIRO_ANTIALIAS_NONE: - pict_format = - _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_A1); - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_DEFAULT: - default: - pict_format = - _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_A8); - break; - } - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - _cairo_xlib_surface_set_precision (display, dst, antialias); - - status = _cairo_xlib_surface_set_attributes (display, - src, &attributes, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto BAIL; - - if (num_traps > ARRAY_LENGTH (xtraps_stack)) { - xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); - if (unlikely (xtraps == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_traps; i++) { - /* top/bottom will be clamped to surface bounds */ - xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); - xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - - /* However, all the other coordinates will have been left untouched so - * as not to introduce numerical error. Recompute them if they - * exceed the 16.16 limits. - */ - if (unlikely (_line_exceeds_16_16 (&traps[i].left))) { - _project_line_x_onto_16_16 (&traps[i].left, - traps[i].top, - traps[i].bottom, - &xtraps[i].left); - xtraps[i].left.p1.y = xtraps[i].top; - xtraps[i].left.p2.y = xtraps[i].bottom; - } else { - xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); - xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); - xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); - xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); - } - - if (unlikely (_line_exceeds_16_16 (&traps[i].right))) { - _project_line_x_onto_16_16 (&traps[i].right, - traps[i].top, - traps[i].bottom, - &xtraps[i].right); - xtraps[i].right.p1.y = xtraps[i].top; - xtraps[i].right.p2.y = xtraps[i].bottom; - } else { - xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); - xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); - xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); - xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); - } - } - - if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { - render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); - render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); - render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - XRenderCompositeTrapezoids (display->display, - _render_operator (op), - src->src_picture, dst->dst_picture, - pict_format, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - xtraps, num_traps); - - if (xtraps != xtraps_stack) - free (xtraps); - - if (! _cairo_operator_bounded_by_mask (op)) { - cairo_traps_t _traps; - cairo_box_t box; - cairo_rectangle_int_t extents; - - /* XRenderCompositeTrapezoids() creates a mask only large enough for the - * trapezoids themselves, but if the operator is unbounded, then we need - * to actually composite all the way out to the bounds. - */ - /* XXX: update the interface to pass composite rects */ - _traps.traps = traps; - _traps.num_traps = num_traps; - _cairo_traps_extents (&_traps, &box); - _cairo_box_round_to_rectangle (&box, &extents); - - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, - src->width, src->height, - extents.width, extents.height, - src_x, src_y, - -extents.x + dst_x, -extents.y + dst_y, - dst_x, dst_y, - width, height, - clip_region); - } - - BAIL: - _cairo_pattern_release_surface (pattern, &src->base, &attributes); - BAIL0: - cairo_device_release (&display->base); - - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_bool_t @@ -3050,57 +1529,176 @@ _cairo_xlib_surface_get_font_options (void *abstract_surface, *options = *_cairo_xlib_screen_get_font_options (surface->screen); } -static void -_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -static void -_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - -static cairo_bool_t -_cairo_xlib_surface_is_similar (void *surface_a, - void *surface_b) +static inline cairo_int_status_t +get_compositor (cairo_xlib_surface_t **surface, + const cairo_compositor_t **compositor) { - return _cairo_xlib_surface_same_screen (surface_a, surface_b); + cairo_xlib_surface_t *s = *surface; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;; + + if (s->fallback) { + assert (s->base.damage != NULL); + assert (s->shm != NULL); + assert (s->shm->damage != NULL); + if (! _cairo_xlib_shm_surface_is_active (s->shm)) { + *surface = (cairo_xlib_surface_t *) s->shm; + *compositor = ((cairo_image_surface_t *) s->shm)->compositor; + s->fallback++; + } else { + status = _cairo_xlib_surface_put_shm (s); + s->fallback = 0; + *compositor = s->compositor; + } + } else + *compositor = s->compositor; + + return status; +} + +static cairo_int_status_t +_cairo_xlib_surface_paint (void *_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_xlib_surface_t *surface = _surface; + const cairo_compositor_t *compositor; + cairo_int_status_t status; + + status = get_compositor (&surface, &compositor); + if (unlikely (status)) + return status; + + return _cairo_compositor_paint (compositor, &surface->base, + op, source, + clip); +} + +static cairo_int_status_t +_cairo_xlib_surface_mask (void *_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_xlib_surface_t *surface = _surface; + const cairo_compositor_t *compositor; + cairo_int_status_t status; + + status = get_compositor (&surface, &compositor); + if (unlikely (status)) + return status; + + return _cairo_compositor_mask (compositor, &surface->base, + op, source, mask, + clip); +} + +static cairo_int_status_t +_cairo_xlib_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) +{ + cairo_xlib_surface_t *surface = _surface; + const cairo_compositor_t *compositor; + cairo_int_status_t status; + + status = get_compositor (&surface, &compositor); + if (unlikely (status)) + return status; + + return _cairo_compositor_stroke (compositor, &surface->base, + op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_xlib_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) +{ + cairo_xlib_surface_t *surface = _surface; + const cairo_compositor_t *compositor; + cairo_int_status_t status; + + status = get_compositor (&surface, &compositor); + if (unlikely (status)) + return status; + + return _cairo_compositor_fill (compositor, &surface->base, + op, source, + path, fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_xlib_surface_glyphs (void *_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_xlib_surface_t *surface = _surface; + const cairo_compositor_t *compositor; + cairo_int_status_t status; + + status = get_compositor (&surface, &compositor); + if (unlikely (status)) + return status; + + return _cairo_compositor_glyphs (compositor, &surface->base, + op, source, + glyphs, num_glyphs, scaled_font, + clip); } static const cairo_surface_backend_t cairo_xlib_surface_backend = { CAIRO_SURFACE_TYPE_XLIB, - _cairo_xlib_surface_create_similar, _cairo_xlib_surface_finish, + + _cairo_default_context_create, + + _cairo_xlib_surface_create_similar, + _cairo_xlib_surface_create_similar_shm, + _cairo_xlib_surface_map_to_image, + _cairo_xlib_surface_unmap_image, + + _cairo_xlib_surface_source, _cairo_xlib_surface_acquire_source_image, _cairo_xlib_surface_release_source_image, - _cairo_xlib_surface_acquire_dest_image, - _cairo_xlib_surface_release_dest_image, - _cairo_xlib_surface_clone_similar, - _cairo_xlib_surface_composite, - _cairo_xlib_surface_fill_rectangles, - _cairo_xlib_surface_composite_trapezoids, - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + _cairo_xlib_surface_snapshot, + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_xlib_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_xlib_surface_get_font_options, - NULL, /* flush */ + + _cairo_xlib_surface_flush, NULL, /* mark_dirty_rectangle */ - _cairo_xlib_surface_scaled_font_fini, - _cairo_xlib_surface_scaled_glyph_fini, - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - _cairo_xlib_surface_show_glyphs, - - _cairo_xlib_surface_snapshot, - _cairo_xlib_surface_is_similar, - - NULL, /* fill_stroke */ - - _cairo_xlib_surface_create_solid_pattern_surface, - _cairo_xlib_surface_can_repaint_solid_pattern_surface + _cairo_xlib_surface_paint, + _cairo_xlib_surface_mask, + _cairo_xlib_surface_stroke, + _cairo_xlib_surface_fill, + NULL, /* fill-stroke */ + _cairo_xlib_surface_glyphs, }; /** @@ -3117,36 +1715,6 @@ _cairo_surface_is_xlib (cairo_surface_t *surface) return surface->backend == &cairo_xlib_surface_backend; } -/* callback from CloseDisplay */ -static void -_cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) -{ - cairo_xlib_surface_t *surface = cairo_container_of (data, - cairo_xlib_surface_t, - close_display_hook); - Display *dpy; - - dpy = display->display; - - X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable)); - - if (surface->dst_picture != None) { - XRenderFreePicture (dpy, surface->dst_picture); - surface->dst_picture = None; - } - - if (surface->src_picture != None) { - XRenderFreePicture (dpy, surface->src_picture); - surface->src_picture = None; - } - - if (surface->owns_pixmap) { - XFreePixmap (dpy, surface->drawable); - surface->drawable = None; - surface->owns_pixmap = FALSE; - } -} - static cairo_surface_t * _cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, Drawable drawable, @@ -3196,7 +1764,7 @@ found: ; } - surface = malloc (sizeof (cairo_xlib_surface_t)); + surface = _cairo_malloc (sizeof (cairo_xlib_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -3206,10 +1774,8 @@ found: return _cairo_surface_create_in_error (_cairo_error (status)); } - _cairo_xlib_display_get_xrender_version (display, - &surface->render_major, - &surface->render_minor); - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { + surface->display = display; + if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) { if (!xrender_format) { if (visual) { xrender_format = XRenderFindVisualFormat (display->display, visual); @@ -3219,25 +1785,20 @@ found: CAIRO_FORMAT_A1); } } - } else { - /* we cannot use XRender for this surface, so ensure we don't try */ - surface->render_major = -1; - surface->render_minor = -1; } - /* initialize and hook into the CloseDisplay callback */ - surface->close_display_hook.func = _cairo_xlib_surface_detach_display; - _cairo_xlib_add_close_display_hook (display, - &surface->close_display_hook); - cairo_device_release (&display->base); _cairo_surface_init (&surface->base, &cairo_xlib_surface_backend, screen->device, - _xrender_format_to_content (xrender_format)); + _xrender_format_to_content (xrender_format), + FALSE); /* is_vector */ surface->screen = screen; + surface->compositor = display->compositor; + surface->shm = NULL; + surface->fallback = 0; surface->drawable = drawable; surface->owns_pixmap = FALSE; @@ -3245,36 +1806,14 @@ found: surface->width = width; surface->height = height; - surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { - /* so we can use the XTile fallback */ - surface->buggy_repeat = TRUE; - } + surface->picture = None; + surface->precision = PolyModePrecise; - surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) - surface->buggy_pad_reflect = TRUE; - - surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) - surface->buggy_gradients = TRUE; - - surface->dst_picture = None; - surface->src_picture = None; + surface->embedded_source.picture = None; surface->visual = visual; surface->xrender_format = xrender_format; surface->depth = depth; - surface->filter = CAIRO_FILTER_NEAREST; - surface->extend = CAIRO_EXTEND_NONE; - surface->has_component_alpha = FALSE; - surface->precision = PolyModePrecise; - surface->xtransform = identity; - - surface->clip_region = NULL; - surface->clip_rects = surface->embedded_clip_rects; - surface->num_clip_rects = 0; - surface->clip_dirty = 0; /* * Compute the pixel format masks from either a XrenderFormat or @@ -3310,6 +1849,8 @@ found: surface->b_mask = 0; } + cairo_list_add (&surface->link, &screen->surfaces); + return &surface->base; } @@ -3338,6 +1879,18 @@ _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) return NULL; } +static cairo_bool_t valid_size (int width, int height) +{ + /* Note: the minimum surface size allowed in the X protocol is 1x1. + * However, as we historically did not check the minimum size we + * allowed applications to lie and set the correct size later (one hopes). + * To preserve compatibility we must allow applications to use + * 0x0 surfaces. + */ + return (width >= 0 && width <= XLIB_COORD_MAX && + height >= 0 && height <= XLIB_COORD_MAX); +} + /** * cairo_xlib_surface_create: * @dpy: an X Display @@ -3362,6 +1915,8 @@ _cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) * children will be included. * * Return value: the newly created surface + * + * Since: 1.0 **/ cairo_surface_t * cairo_xlib_surface_create (Display *dpy, @@ -3374,9 +1929,9 @@ cairo_xlib_surface_create (Display *dpy, cairo_xlib_screen_t *screen; cairo_status_t status; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + if (! valid_size (width, height)) { /* you're lying, and you know it! */ - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); } scr = _cairo_xlib_screen_from_visual (dpy, visual); @@ -3406,6 +1961,8 @@ cairo_xlib_surface_create (Display *dpy, * This will be drawn to as a %CAIRO_FORMAT_A1 object. * * Return value: the newly created surface + * + * Since: 1.0 **/ cairo_surface_t * cairo_xlib_surface_create_for_bitmap (Display *dpy, @@ -3417,8 +1974,8 @@ cairo_xlib_surface_create_for_bitmap (Display *dpy, cairo_xlib_screen_t *screen; cairo_status_t status; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + if (! valid_size (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); status = _cairo_xlib_screen_get (dpy, scr, &screen); if (unlikely (status)) @@ -3451,6 +2008,8 @@ cairo_xlib_surface_create_for_bitmap (Display *dpy, * window changes. * * Return value: the newly created surface + * + * Since: 1.0 **/ cairo_surface_t * cairo_xlib_surface_create_with_xrender_format (Display *dpy, @@ -3463,8 +2022,8 @@ cairo_xlib_surface_create_with_xrender_format (Display *dpy, cairo_xlib_screen_t *screen; cairo_status_t status; - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); + if (! valid_size (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); status = _cairo_xlib_screen_get (dpy, scr, &screen); if (unlikely (status)) @@ -3522,6 +2081,8 @@ cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) * * A Pixmap can never change size, so it is never necessary to call * this function on a surface created for a Pixmap. + * + * Since: 1.0 **/ void cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, @@ -3534,26 +2095,38 @@ cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (! _cairo_surface_is_xlib (abstract_surface)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + if (surface->width == width && surface->height == height) + return; + + if (! valid_size (width, height)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } + status = _cairo_surface_flush (abstract_surface, 0); + if (unlikely (status)) { + _cairo_surface_set_error (abstract_surface, status); + return; + } + + _cairo_xlib_surface_discard_shm (surface); + surface->width = width; surface->height = height; } + /** * cairo_xlib_surface_set_drawable: * @surface: a #cairo_surface_t for the XLib backend @@ -3567,6 +2140,8 @@ cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, * will get X protocol errors and will probably terminate. * No checks are done by this function to ensure this * compatibility. + * + * Since: 1.0 **/ void cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, @@ -3591,7 +2166,7 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, return; } - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) { + if (! valid_size (width, height)) { status = _cairo_surface_set_error (abstract_surface, _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; @@ -3601,6 +2176,12 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, if (surface->owns_pixmap) return; + status = _cairo_surface_flush (abstract_surface, 0); + if (unlikely (status)) { + _cairo_surface_set_error (abstract_surface, status); + return; + } + if (surface->drawable != drawable) { cairo_xlib_display_t *display; @@ -3610,38 +2191,27 @@ cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); - if (surface->dst_picture != None) { - status = _cairo_xlib_display_queue_resource ( - display, - XRenderFreePicture, - surface->dst_picture); + if (surface->picture != None) { + XRenderFreePicture (display->display, surface->picture); if (unlikely (status)) { status = _cairo_surface_set_error (&surface->base, status); return; } - surface->dst_picture = None; - } - - if (surface->src_picture != None) { - status = _cairo_xlib_display_queue_resource ( - display, - XRenderFreePicture, - surface->src_picture); - if (unlikely (status)) { - status = _cairo_surface_set_error (&surface->base, status); - return; - } - - surface->src_picture = None; + surface->picture = None; } cairo_device_release (&display->base); surface->drawable = drawable; } - surface->width = width; - surface->height = height; + + if (surface->width != width || surface->height != height) { + _cairo_xlib_surface_discard_shm (surface); + + surface->width = width; + surface->height = height; + } } /** @@ -3807,1127 +2377,4 @@ cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) return surface->height; } -enum { - GLYPHSET_INDEX_ARGB32, - GLYPHSET_INDEX_A8, - GLYPHSET_INDEX_A1, - NUM_GLYPHSETS -}; - -typedef struct _cairo_xlib_font_glyphset_free_glyphs { - GlyphSet glyphset; - int glyph_count; - unsigned long glyph_indices[128]; -} cairo_xlib_font_glyphset_free_glyphs_t; - -typedef struct _cairo_xlib_font_glyphset_info { - GlyphSet glyphset; - cairo_format_t format; - XRenderPictFormat *xrender_format; - cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs; -} cairo_xlib_font_glyphset_info_t; - -typedef struct _cairo_xlib_surface_font_private { - cairo_scaled_font_t *scaled_font; - cairo_scaled_font_t *grayscale_font; - cairo_xlib_hook_t close_display_hook; - cairo_device_t *device; - cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; -} cairo_xlib_surface_font_private_t; - -/* callback from CloseDisplay */ -static void -_cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, - void *data) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_scaled_font_t *scaled_font; - - font_private = cairo_container_of (data, - cairo_xlib_surface_font_private_t, - close_display_hook); - scaled_font = font_private->scaled_font; - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - font_private = scaled_font->surface_private; - scaled_font->surface_private = NULL; - - _cairo_scaled_font_reset_cache (scaled_font); - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (font_private != NULL) { - int i; - - if (font_private->grayscale_font) { - cairo_scaled_font_destroy (font_private->grayscale_font); - } - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyphset_info = &font_private->glyphset_info[i]; - if (glyphset_info->glyphset) - XRenderFreeGlyphSet (display->display, glyphset_info->glyphset); - - if (glyphset_info->pending_free_glyphs != NULL) - free (glyphset_info->pending_free_glyphs); - } - - cairo_device_destroy (font_private->device); - free (font_private); - } -} - -static cairo_status_t -_cairo_xlib_surface_font_init (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - int i; - - font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); - if (unlikely (font_private == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->scaled_font = scaled_font; - font_private->grayscale_font = NULL; - font_private->device = cairo_device_reference (&display->base); - - /* initialize and hook into the CloseDisplay callback */ - font_private->close_display_hook.func = - _cairo_xlib_surface_remove_scaled_font; - _cairo_xlib_add_close_display_hook (display, - &font_private->close_display_hook); - - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; - switch (i) { - case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; - case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; - case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; - default: ASSERT_NOT_REACHED; break; - } - glyphset_info->xrender_format = NULL; - glyphset_info->glyphset = None; - glyphset_info->pending_free_glyphs = NULL; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &cairo_xlib_surface_backend; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_status_t status; - - font_private = scaled_font->surface_private; - if (font_private != NULL) { - cairo_xlib_display_t *display; - int i; - - if (font_private->grayscale_font) { - cairo_scaled_font_destroy (font_private->grayscale_font); - } - status = _cairo_xlib_display_acquire (font_private->device, &display); - if (status) - goto BAIL; - - _cairo_xlib_remove_close_display_hook (display, - &font_private->close_display_hook); - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyphset_info = &font_private->glyphset_info[i]; - - if (glyphset_info->pending_free_glyphs != NULL) - free (glyphset_info->pending_free_glyphs); - - if (glyphset_info->glyphset) { - status = _cairo_xlib_display_queue_resource (display, - XRenderFreeGlyphSet, - glyphset_info->glyphset); - (void) status; /* XXX cannot propagate failure */ - } - } - - cairo_device_release (&display->base); -BAIL: - cairo_device_destroy (&display->base); - free (font_private); - } -} - -static void -_cairo_xlib_render_free_glyphs (Display *dpy, - cairo_xlib_font_glyphset_free_glyphs_t *to_free) -{ - XRenderFreeGlyphs (dpy, - to_free->glyphset, - to_free->glyph_indices, - to_free->glyph_count); -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) -{ - return scaled_glyph->surface_private; -} - -static void -_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, - cairo_xlib_font_glyphset_info_t *glyphset_info) -{ - scaled_glyph->surface_private = glyphset_info; -} - -static void -_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_xlib_font_glyphset_info_t *glyphset_info; - - if (scaled_font->finished) - return; - - font_private = scaled_font->surface_private; - glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); - if (font_private != NULL && glyphset_info != NULL) { - cairo_xlib_font_glyphset_free_glyphs_t *to_free; - cairo_status_t status; - - to_free = glyphset_info->pending_free_glyphs; - if (to_free != NULL && - to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) - { - cairo_xlib_display_t *display; - - status = _cairo_xlib_display_acquire (font_private->device, &display); - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_xlib_display_queue_work (display, - (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs, - to_free, - free); - cairo_device_release (&display->base); - } - /* XXX cannot propagate failure */ - if (unlikely (status)) - free (to_free); - - to_free = glyphset_info->pending_free_glyphs = NULL; - } - - if (to_free == NULL) { - to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t)); - if (unlikely (to_free == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return; /* XXX cannot propagate failure */ - } - - to_free->glyphset = glyphset_info->glyphset; - to_free->glyph_count = 0; - glyphset_info->pending_free_glyphs = to_free; - } - - to_free->glyph_indices[to_free->glyph_count++] = - _cairo_scaled_glyph_index (scaled_glyph); - } -} - -static cairo_bool_t -_native_byte_order_lsb (void) -{ - int x = 1; - - return *((char *) &x) == 1; -} - -static int -_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) -{ - if (format == CAIRO_FORMAT_A8) - return GLYPHSET_INDEX_A8; - if (format == CAIRO_FORMAT_A1) - return GLYPHSET_INDEX_A1; - - assert (format == CAIRO_FORMAT_ARGB32); - return GLYPHSET_INDEX_ARGB32; -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, - cairo_format_t format) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_xlib_font_glyphset_info_t *glyphset_info; - int glyphset_index; - - glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); - font_private = scaled_font->surface_private; - glyphset_info = &font_private->glyphset_info[glyphset_index]; - if (glyphset_info->glyphset == None) { - cairo_xlib_display_t *display; - - if (_cairo_xlib_display_acquire (font_private->device, &display)) - return NULL; - - glyphset_info->xrender_format = - _cairo_xlib_display_get_xrender_format (display, - glyphset_info->format); - glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, - glyphset_info->xrender_format); - - cairo_device_release (&display->base); - } - - return glyphset_info; -} - -static cairo_bool_t -_cairo_xlib_glyphset_info_has_pending_free_glyph ( - cairo_xlib_font_glyphset_info_t *glyphset_info, - unsigned long glyph_index) -{ - if (glyphset_info->pending_free_glyphs != NULL) { - cairo_xlib_font_glyphset_free_glyphs_t *to_free; - int i; - - to_free = glyphset_info->pending_free_glyphs; - for (i = 0; i < to_free->glyph_count; i++) { - if (to_free->glyph_indices[i] == glyph_index) { - to_free->glyph_count--; - memmove (&to_free->glyph_indices[i], - &to_free->glyph_indices[i+1], - (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); - return TRUE; - } - } - } - - return FALSE; -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph ( - cairo_scaled_font_t *scaled_font, - unsigned long glyph_index, - cairo_image_surface_t *surface) -{ - cairo_xlib_surface_font_private_t *font_private; - int i; - - font_private = scaled_font->surface_private; - if (font_private == NULL) - return NULL; - - if (surface != NULL) { - i = _cairo_xlib_get_glyphset_index_for_format (surface->format); - if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], - glyph_index)) - { - return &font_private->glyphset_info[i]; - } - } else { - for (i = 0; i < NUM_GLYPHSETS; i++) { - if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], - glyph_index)) - { - return &font_private->glyphset_info[i]; - } - } - } - - return NULL; -} - -static cairo_status_t -_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t **pscaled_glyph) -{ - XGlyphInfo glyph_info; - unsigned long glyph_index; - unsigned char *data; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph; - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_bool_t already_had_glyph_surface; - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyph_index = _cairo_scaled_glyph_index (scaled_glyph); - - /* check to see if we have a pending XRenderFreeGlyph for this glyph */ - glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); - if (glyphset_info != NULL) { - _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - return CAIRO_STATUS_SUCCESS; - } - - if (!glyph_surface) { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_SURFACE, - pscaled_glyph); - if (unlikely (status)) - return status; - - scaled_glyph = *pscaled_glyph; - glyph_surface = scaled_glyph->surface; - already_had_glyph_surface = FALSE; - } else { - already_had_glyph_surface = TRUE; - } - - if (scaled_font->surface_private == NULL) { - status = _cairo_xlib_surface_font_init (display, scaled_font); - if (unlikely (status)) - return status; - } - - glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font, - glyph_surface->format); - - /* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */ - { - int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height; - int max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) - : XMaxRequestSize (display->display)) * 4 - - sz_xRenderAddGlyphsReq - - sz_xGlyphInfo - - 8; - if (len >= max_request_size) - return UNSUPPORTED ("glyph too large for XRequest"); - } - - /* If the glyph surface has zero height or width, we create - * a clear 1x1 surface, to avoid various X server bugs. - */ - if (glyph_surface->width == 0 || glyph_surface->height == 0) { - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - } - - /* If the glyph format does not match the font format, then we - * create a temporary surface for the glyph image with the font's - * format. - */ - if (glyph_surface->format != glyphset_info->format) { - cairo_surface_pattern_t pattern; - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (glyphset_info->format, - glyph_surface->width, - glyph_surface->height); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); - status = _cairo_surface_paint (tmp_surface, - CAIRO_OPERATOR_SOURCE, &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - - if (unlikely (status)) - goto BAIL; - } - - /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ - glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); - glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); - glyph_info.width = glyph_surface->width; - glyph_info.height = glyph_surface->height; - glyph_info.xOff = scaled_glyph->x_advance; - glyph_info.yOff = scaled_glyph->y_advance; - - data = glyph_surface->data; - - /* flip formats around */ - switch (_cairo_xlib_get_glyphset_index_for_format (scaled_glyph->surface->format)) { - case GLYPHSET_INDEX_A1: - /* local bitmaps are always stored with bit == byte */ - if (_native_byte_order_lsb() != (BitmapBitOrder (display->display) == LSBFirst)) { - int c = glyph_surface->stride * glyph_surface->height; - unsigned char *d; - unsigned char *new, *n; - - new = malloc (c); - if (!new) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - n = new; - d = data; - do { - char b = *d++; - b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); - b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); - b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); - *n++ = b; - } while (--c); - data = new; - } - break; - case GLYPHSET_INDEX_A8: - break; - case GLYPHSET_INDEX_ARGB32: - if (_native_byte_order_lsb() != (ImageByteOrder (display->display) == LSBFirst)) { - unsigned int c = glyph_surface->stride * glyph_surface->height / 4; - const uint32_t *d; - uint32_t *new, *n; - - new = malloc (4 * c); - if (unlikely (new == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - n = new; - d = (uint32_t *) data; - do { - *n++ = bswap_32 (*d); - d++; - } while (--c); - data = (uint8_t *) new; - } - break; - default: - ASSERT_NOT_REACHED; - break; - } - /* XXX assume X server wants pixman padding. Xft assumes this as well */ - - struct _XDisplay *dpy = (struct _XDisplay *) display->display; - int req_length = sz_xRenderAddGlyphsReq + 4; - if (req_length & 3) - req_length += 4 - (req_length & 3); - if (dpy->bufptr + req_length > dpy->bufmax) - XFlush (display->display); - - XRenderAddGlyphs (display->display, glyphset_info->glyphset, - &glyph_index, &glyph_info, 1, - (char *) data, - glyph_surface->stride * glyph_surface->height); - - if (data != glyph_surface->data) - free (data); - - _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - - BAIL: - if (glyph_surface != scaled_glyph->surface) - cairo_surface_destroy (&glyph_surface->base); - - /* if the scaled glyph didn't already have a surface attached - * to it, release the created surface now that we have it - * uploaded to the X server. If the surface has already been - * there (eg. because image backend requested it), leave it in - * the cache - */ - if (!already_had_glyph_surface) - _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL); - - return status; -} - -typedef void (*cairo_xrender_composite_text_func_t) - (Display *dpy, - int op, - Picture src, - Picture dst, - _Xconst XRenderPictFormat *maskFormat, - int xSrc, - int ySrc, - int xDst, - int yDst, - _Xconst XGlyphElt8 *elts, - int nelt); - -/* Build a struct of the same size of #cairo_glyph_t that can be used both as - * an input glyph with double coordinates, and as "working" glyph with - * integer from-current-point offsets. */ -typedef union { - cairo_glyph_t d; - unsigned long index; - struct { - unsigned long index; - int x; - int y; - } i; -} cairo_xlib_glyph_t; - -/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */ -COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t)); - -/* Start a new element for the first glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs - * (Xrender limits each element to 252 glyphs, we limit them to 128) - * - * These same conditions need to be mirrored between - * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks - */ -#define _start_new_glyph_elt(count, glyph) \ - (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) - -static cairo_status_t -_emit_glyphs_chunk (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - cairo_xlib_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes, - /* info for this chunk */ - int num_elts, - int width, - cairo_xlib_font_glyphset_info_t *glyphset_info) -{ - /* Which XRenderCompositeText function to use */ - cairo_xrender_composite_text_func_t composite_text_func; - int size; - - /* Element buffer stuff */ - XGlyphElt8 *elts; - XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)]; - - /* Reuse the input glyph array for output char generation */ - char *char8 = (char *) glyphs; - unsigned short *char16 = (unsigned short *) glyphs; - unsigned int *char32 = (unsigned int *) glyphs; - - int i; - int nelt; /* Element index */ - int n; /* Num output glyphs in current element */ - int j; /* Num output glyphs so far */ - - switch (width) { - case 1: - /* don't cast the 8-variant, to catch possible mismatches */ - composite_text_func = XRenderCompositeText8; - size = sizeof (char); - break; - case 2: - composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16; - size = sizeof (unsigned short); - break; - default: - case 4: - composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32; - size = sizeof (unsigned int); - } - - /* Allocate element array */ - if (num_elts <= ARRAY_LENGTH (stack_elts)) { - elts = stack_elts; - } else { - elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8)); - if (unlikely (elts == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - /* Fill them in */ - nelt = 0; - n = 0; - j = 0; - for (i = 0; i < num_glyphs; i++) { - - /* Start a new element for first output glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs. - * - * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() - */ - if (_start_new_glyph_elt (j, &glyphs[i])) { - if (j) { - elts[nelt].nchars = n; - nelt++; - n = 0; - } - elts[nelt].chars = char8 + size * j; - elts[nelt].glyphset = glyphset_info->glyphset; - elts[nelt].xOff = glyphs[i].i.x; - elts[nelt].yOff = glyphs[i].i.y; - } - - switch (width) { - case 1: char8 [j] = (char) glyphs[i].index; break; - case 2: char16[j] = (unsigned short) glyphs[i].index; break; - default: - case 4: char32[j] = (unsigned int) glyphs[i].index; break; - } - - n++; - j++; - } - - if (n) { - elts[nelt].nchars = n; - nelt++; - } - - /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the - * expected number of xGlyphElts. */ - assert (nelt == num_elts); - - composite_text_func (display->display, - _render_operator (op), - src->src_picture, - dst->dst_picture, - glyphset_info->xrender_format, - attributes->x_offset + elts[0].xOff, - attributes->y_offset + elts[0].yOff, - elts[0].xOff, elts[0].yOff, - (XGlyphElt8 *) elts, nelt); - - if (elts != stack_elts) - free (elts); - - return CAIRO_STATUS_SUCCESS; -} - - -/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have - * enough room for padding */ -#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) - -static cairo_status_t -_cairo_xlib_surface_emit_glyphs (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - cairo_xlib_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes, - int *remaining_glyphs) -{ - int i; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph; - cairo_fixed_t x = 0, y = 0; - cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; - - unsigned long max_index = 0; - int width = 1; - int num_elts = 0; - int num_out_glyphs = 0; - - int max_request_size = XMaxRequestSize (display->display) * 4 - - MAX (sz_xRenderCompositeGlyphs8Req, - MAX(sz_xRenderCompositeGlyphs16Req, - sz_xRenderCompositeGlyphs32Req)); - int request_size = 0; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - - for (i = 0; i < num_glyphs; i++) { - int this_x, this_y; - int old_width; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - return status; - - this_x = _cairo_lround (glyphs[i].d.x); - this_y = _cairo_lround (glyphs[i].d.y); - - /* Glyph skipping: - * - * We skip any glyphs that have troublesome coordinates. We want - * to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in - * a signed 16bit integer, otherwise it will overflow in the render - * protocol. - * To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in - * a signed 15bit integer. The trivial option would be to allow - * coordinates -8192..8192, but that's kinda dull. It probably will - * take a decade or so to get monitors 8192x4096 or something. A - * negative value of -8192 on the other hand, is absolutely useless. - * Note that we do want to allow some negative positions. The glyph - * may start off the screen but part of it make it to the screen. - * Anyway, we will allow positions in the range -4096..122887. That - * will buy us a few more years before this stops working. - * - * Update: upon seeing weird glyphs, we just return and let fallback - * code do the job. - */ - if (((this_x+4096)|(this_y+4096))&~0x3fffu) - break; - - /* Send unsent glyphs to the server */ - if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { - status = _cairo_xlib_surface_add_glyph (display, - scaled_font, - &scaled_glyph); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - /* Break so we flush glyphs so far and let fallback code - * handle the rest */ - break; - - return status; - } - } - - this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); - if (!glyphset_info) - glyphset_info = this_glyphset_info; - - /* The invariant here is that we can always flush the glyphs - * accumulated before this one, using old_width, and they - * would fit in the request. - */ - old_width = width; - - /* Update max glyph index */ - if (glyphs[i].index > max_index) { - max_index = glyphs[i].index; - if (max_index >= 65536) - width = 4; - else if (max_index >= 256) - width = 2; - if (width != old_width) - request_size += (width - old_width) * num_out_glyphs; - } - - /* If we will pass the max request size by adding this glyph, - * flush current glyphs. Note that we account for a - * possible element being added below. - * - * Also flush if changing glyphsets, as Xrender limits one mask - * format per request, so we can either break up, or use a - * wide-enough mask format. We do the former. One reason to - * prefer the latter is the fact that Xserver ADDs all glyphs - * to the mask first, and then composes that to final surface, - * though it's not a big deal. - */ - if (request_size + width > max_request_size - _cairo_sz_xGlyphElt || - (this_glyphset_info != glyphset_info)) { - status = _emit_glyphs_chunk (display, dst, glyphs, i, - scaled_font, op, src, attributes, - num_elts, old_width, glyphset_info); - if (unlikely (status)) - return status; - - glyphs += i; - num_glyphs -= i; - i = 0; - max_index = glyphs[i].index; - width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; - request_size = 0; - num_elts = 0; - num_out_glyphs = 0; - x = y = 0; - glyphset_info = this_glyphset_info; - } - - /* Convert absolute glyph position to relative-to-current-point - * position */ - glyphs[i].i.x = this_x - x; - glyphs[i].i.y = this_y - y; - - /* Start a new element for the first glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs. - * - * These same conditions are mirrored in _emit_glyphs_chunk(). - */ - if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { - num_elts++; - request_size += _cairo_sz_xGlyphElt; - } - - /* adjust current-position */ - x = this_x + scaled_glyph->x_advance; - y = this_y + scaled_glyph->y_advance; - - num_out_glyphs++; - request_size += width; - } - - if (num_elts) { - status = _emit_glyphs_chunk (display, dst, glyphs, i, - scaled_font, op, src, attributes, - num_elts, width, glyphset_info); - } - - *remaining_glyphs = num_glyphs - i; - if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS) - status = CAIRO_INT_STATUS_UNSUPPORTED; - - return status; -} - -static cairo_bool_t -_cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &cairo_xlib_surface_backend) || - (font_private != NULL && font_private->device != dst->base.device)) - { - return FALSE; - } - - return TRUE; -} - -/* Gets a grayscale version of scaled_font. The grayscale version is cached - * in our surface_private data. - */ -static cairo_scaled_font_t * -_cairo_xlib_get_grayscale_font (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private; - cairo_bool_t needs_font; - - if (font_private == NULL) { - cairo_status_t status = _cairo_xlib_surface_font_init (display, scaled_font); - if (unlikely (status)) - return _cairo_scaled_font_create_in_error (status); - font_private = scaled_font->surface_private; - } - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - needs_font = !font_private->grayscale_font; - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (needs_font) { - cairo_font_options_t options; - cairo_scaled_font_t *new_font; - - options = scaled_font->options; - options.antialias = CAIRO_ANTIALIAS_GRAY; - new_font = cairo_scaled_font_create (scaled_font->font_face, - &scaled_font->font_matrix, - &scaled_font->ctm, &options); - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - if (!font_private->grayscale_font) { - font_private->grayscale_font = new_font; - new_font = NULL; - } - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (new_font) { - cairo_scaled_font_destroy (new_font); - } - } - - return font_private->grayscale_font; -} - -static cairo_int_status_t -_cairo_xlib_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; - composite_operation_t operation; - cairo_surface_attributes_t attributes; - cairo_xlib_surface_t *src = NULL; - cairo_region_t *clip_region = NULL; - cairo_xlib_display_t *display; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst)) - return UNSUPPORTED ("XRender does not support CompositeText"); - - /* Just let unbounded operators go through the fallback code - * instead of trying to do the fixups here */ - if (! _cairo_operator_bounded_by_mask (op)) - return UNSUPPORTED ("unsupported unbounded op"); - - /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- - * the solid source seems to be multiplied by the glyph mask, and - * then the entire thing is copied to the destination surface, - * including the fully transparent "background" of the rectangular - * glyph surface. */ - if (op == CAIRO_OPERATOR_SOURCE && - ! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11)) - { - return UNSUPPORTED ("known bug in Render"); - } - - /* We can only use our code if we either have no clip or - * have a real native clip region set. If we're using - * fallback clip masking, we have to go through the full - * fallback path. - */ - if (clip != NULL) { - status = _cairo_clip_get_region (clip, &clip_region); - assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO); - if (status) - return status; - } - - operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported op"); - - if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) - return UNSUPPORTED ("unowned font"); - - - status = _cairo_xlib_display_acquire (dst->base.device, &display); - if (unlikely (status)) - return status; - - if (!dst->base.permit_subpixel_antialiasing && - scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { - scaled_font = _cairo_xlib_get_grayscale_font (display, scaled_font); - } - - X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); - - if (clip_region != NULL && - cairo_region_num_rectangles (clip_region) == 1) - { - cairo_rectangle_int_t glyph_extents; - cairo_rectangle_int_t clip_extents; - - /* Can we do without the clip? - * Around 50% of the time the clip is redundant (firefox). - */ - _cairo_scaled_font_glyph_approximate_extents (scaled_font, - glyphs, num_glyphs, - &glyph_extents); - - cairo_region_get_extents(clip_region, &clip_extents); - if (clip_extents.x <= glyph_extents.x && - clip_extents.y <= glyph_extents.y && - clip_extents.x + clip_extents.width >= glyph_extents.x + glyph_extents.width && - clip_extents.y + clip_extents.height >= glyph_extents.y + glyph_extents.height) - { - clip_region = NULL; - } - } - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL0; - - /* After passing all those tests, we're now committed to rendering - * these glyphs or to fail trying. We first upload any glyphs to - * the X server that it doesn't have already, then we draw - * them. - */ - - /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore - * the mask (the glyphs). This code below was executed as a side effect - * of going through the _clip_and_composite fallback code for old_show_glyphs, - * so PictOpClear was never used with CompositeText before. - */ - if (op == CAIRO_OPERATOR_CLEAR) { - src_pattern = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - 0, 0, 1, 1, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); - if (unlikely (status)) - goto BAIL0; - } else { - cairo_rectangle_int_t glyph_extents; - - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - goto BAIL0; - - if (clip != NULL) { - if (! _cairo_rectangle_intersect (&glyph_extents, - _cairo_clip_get_extents (clip))) - { - goto BAIL0; - } - } - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, src_pattern, - glyph_extents.x, - glyph_extents.y, - glyph_extents.width, - glyph_extents.height, - &src, &attributes); - if (unlikely (status)) - goto BAIL0; - } - - operation = _recategorize_composite_operation (dst, op, src, - &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported op"); - goto BAIL1; - } - - status = _cairo_xlib_surface_set_attributes (display, src, &attributes, 0, 0); - if (unlikely (status)) - goto BAIL1; - - _cairo_scaled_font_freeze_cache (scaled_font); - if (_cairo_xlib_surface_owns_font (dst, scaled_font)) { - status = _cairo_xlib_surface_emit_glyphs (display, - dst, - (cairo_xlib_glyph_t *) glyphs, - num_glyphs, - scaled_font, - op, - src, - &attributes, - remaining_glyphs); - } else { - status = UNSUPPORTED ("unowned font"); - } - _cairo_scaled_font_thaw_cache (scaled_font); - - BAIL1: - if (src) - _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); - BAIL0: - cairo_device_release (&display->base); - - return status; -} +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-visual.c b/gfx/cairo/cairo/src/cairo-xlib-visual.c index e076ed01e45e..979cd5b36b40 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-visual.c +++ b/gfx/cairo/cairo/src/cairo-xlib-visual.c @@ -35,9 +35,12 @@ #include "cairoint.h" +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + #include "cairo-xlib-private.h" #include "cairo-error-private.h" +#include "cairo-list-inline.h" /* A perceptual distance metric between two colors. No sqrt needed * since the square of the distance is still a valid metric. */ @@ -79,10 +82,11 @@ _cairo_xlib_visual_info_create (Display *dpy, for (i = 0; i < RAMP_SIZE; i++) ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1); - info = malloc (sizeof (cairo_xlib_visual_info_t)); + info = _cairo_malloc (sizeof (cairo_xlib_visual_info_t)); if (unlikely (info == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_list_init (&info->link); info->visualid = visualid; /* Allocate a gray ramp and a color cube. @@ -183,5 +187,8 @@ void _cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info) { /* No need for XFreeColors() whilst using DefaultColormap */ + _cairo_list_del (&info->link); free (info); } + +#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c b/gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c new file mode 100644 index 000000000000..4fa9f2883283 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c @@ -0,0 +1,848 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2009 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib.h" +#include "cairo-xcb.h" + +#include "cairo-xcb-private.h" +#include "cairo-xlib-xrender-private.h" + +#include "cairo-default-context-private.h" +#include "cairo-list-inline.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" + +#include +#include /* For XESetCloseDisplay */ + +struct cairo_xlib_xcb_display_t { + cairo_device_t base; + + Display *dpy; + cairo_device_t *xcb_device; + XExtCodes *codes; + + cairo_list_t link; +}; +typedef struct cairo_xlib_xcb_display_t cairo_xlib_xcb_display_t; + +/* List of all #cairo_xlib_xcb_display_t alive, + * protected by _cairo_xlib_display_mutex */ +static cairo_list_t displays; + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create (void *dpy, + void *scr, + void *visual, + void *format, + cairo_surface_t *xcb); + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *other = abstract_other; + cairo_surface_t *xcb; + + xcb = other->xcb->base.backend->create_similar (other->xcb, content, width, height); + if (unlikely (xcb == NULL || xcb->status)) + return xcb; + + return _cairo_xlib_xcb_surface_create (other->display, other->screen, NULL, NULL, xcb); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_finish (void *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + cairo_surface_finish (&surface->xcb->base); + status = surface->xcb->base.status; + cairo_surface_destroy (&surface->xcb->base); + surface->xcb = NULL; + + return status; +} + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create_similar_image (void *abstract_other, + cairo_format_t format, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *surface = abstract_other; + return cairo_surface_create_similar_image (&surface->xcb->base, format, width, height); +} + +static cairo_image_surface_t * +_cairo_xlib_xcb_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_map_to_image (&surface->xcb->base, extents); +} + +static cairo_int_status_t +_cairo_xlib_xcb_surface_unmap (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_unmap_image (&surface->xcb->base, image); +} + +static cairo_surface_t * +_cairo_xlib_xcb_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_get_source (&surface->xcb->base, extents); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_acquire_source_image (&surface->xcb->base, + image_out, image_extra); +} + +static void +_cairo_xlib_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image_out, + void *image_extra) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + _cairo_surface_release_source_image (&surface->xcb->base, image_out, image_extra); +} + +static cairo_bool_t +_cairo_xlib_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_get_extents (&surface->xcb->base, extents); +} + +static void +_cairo_xlib_xcb_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + cairo_surface_get_font_options (&surface->xcb->base, options); +} + +static cairo_int_status_t +_cairo_xlib_xcb_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_paint (&surface->xcb->base, op, source, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_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_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_mask (&surface->xcb->base, op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_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_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_stroke (&surface->xcb->base, + op, source, path, style, ctm, ctm_inverse, + tolerance, antialias, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_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_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_fill (&surface->xcb->base, + op, source, path, + fill_rule, tolerance, + antialias, clip); +} + +static cairo_int_status_t +_cairo_xlib_xcb_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_xlib_xcb_surface_t *surface = abstract_surface; + return _cairo_surface_show_text_glyphs (&surface->xcb->base, op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, clip); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_flush (void *abstract_surface, unsigned flags) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + /* We have to call cairo_surface_flush() to make sure snapshots are detached */ + return _cairo_surface_flush (&surface->xcb->base, flags); +} + +static cairo_status_t +_cairo_xlib_xcb_surface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_xlib_xcb_surface_t *surface = abstract_surface; + cairo_surface_mark_dirty_rectangle (&surface->xcb->base, x, y, width, height); + return cairo_surface_status (&surface->xcb->base); +} + +static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = { + CAIRO_SURFACE_TYPE_XLIB, + _cairo_xlib_xcb_surface_finish, + + _cairo_default_context_create, /* XXX */ + + _cairo_xlib_xcb_surface_create_similar, + _cairo_xlib_xcb_surface_create_similar_image, + _cairo_xlib_xcb_surface_map_to_image, + _cairo_xlib_xcb_surface_unmap, + + _cairo_xlib_xcb_surface_source, + _cairo_xlib_xcb_surface_acquire_source_image, + _cairo_xlib_xcb_surface_release_source_image, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_xlib_xcb_surface_get_extents, + _cairo_xlib_xcb_surface_get_font_options, + + _cairo_xlib_xcb_surface_flush, + _cairo_xlib_xcb_surface_mark_dirty, + + _cairo_xlib_xcb_surface_paint, + _cairo_xlib_xcb_surface_mask, + _cairo_xlib_xcb_surface_stroke, + _cairo_xlib_xcb_surface_fill, + NULL, /* fill_stroke */ + _cairo_xlib_xcb_surface_glyphs, +}; + +static void +_cairo_xlib_xcb_display_finish (void *abstract_display) +{ + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) abstract_display; + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + cairo_list_del (&display->link); + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + cairo_device_destroy (display->xcb_device); + display->xcb_device = NULL; + + XESetCloseDisplay (display->dpy, display->codes->extension, NULL); + /* Drop the reference from _cairo_xlib_xcb_device_create */ + cairo_device_destroy (&display->base); +} + +static int +_cairo_xlib_xcb_close_display(Display *dpy, XExtCodes *codes) +{ + cairo_xlib_xcb_display_t *display; + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + cairo_list_foreach_entry (display, + cairo_xlib_xcb_display_t, + &displays, + link) + { + if (display->dpy == dpy) + { + /* _cairo_xlib_xcb_display_finish will lock the mutex again + * -> deadlock (This mutex isn't recursive) */ + cairo_device_reference (&display->base); + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + /* Make sure the xcb and xlib-xcb devices are finished */ + cairo_device_finish (display->xcb_device); + cairo_device_finish (&display->base); + + cairo_device_destroy (&display->base); + return 0; + } + } + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + return 0; +} + +static const cairo_device_backend_t _cairo_xlib_xcb_device_backend = { + CAIRO_DEVICE_TYPE_XLIB, + + NULL, + NULL, + + NULL, /* flush */ + _cairo_xlib_xcb_display_finish, + free, /* destroy */ +}; + +static cairo_device_t * +_cairo_xlib_xcb_device_create (Display *dpy, cairo_device_t *xcb_device) +{ + cairo_xlib_xcb_display_t *display = NULL; + cairo_device_t *device; + + if (xcb_device == NULL) + return NULL; + + CAIRO_MUTEX_INITIALIZE (); + + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + if (displays.next == NULL) { + cairo_list_init (&displays); + } + + cairo_list_foreach_entry (display, + cairo_xlib_xcb_display_t, + &displays, + link) + { + if (display->dpy == dpy) { + /* Maintain MRU order. */ + if (displays.next != &display->link) + cairo_list_move (&display->link, &displays); + + /* Grab a reference for our caller */ + device = cairo_device_reference (&display->base); + assert (display->xcb_device == xcb_device); + goto unlock; + } + } + + display = _cairo_malloc (sizeof (cairo_xlib_xcb_display_t)); + if (unlikely (display == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + goto unlock; + } + + display->codes = XAddExtension (dpy); + if (unlikely (display->codes == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + free (display); + goto unlock; + } + + _cairo_device_init (&display->base, &_cairo_xlib_xcb_device_backend); + + XESetCloseDisplay (dpy, display->codes->extension, _cairo_xlib_xcb_close_display); + /* Add a reference for _cairo_xlib_xcb_display_finish. This basically means + * that the device's reference count never drops to zero + * as long as our Display* is alive. We need this because there is no + * "XDelExtension" to undo XAddExtension and having lots of registered + * extensions slows down libX11. */ + cairo_device_reference (&display->base); + + display->dpy = dpy; + display->xcb_device = cairo_device_reference(xcb_device); + + cairo_list_add (&display->link, &displays); + device = &display->base; + +unlock: + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + return device; +} + +static cairo_surface_t * +_cairo_xlib_xcb_surface_create (void *dpy, + void *scr, + void *visual, + void *format, + cairo_surface_t *xcb) +{ + cairo_xlib_xcb_surface_t *surface; + + if (unlikely (xcb->status)) + return xcb; + + surface = _cairo_malloc (sizeof (*surface)); + if (unlikely (surface == NULL)) { + cairo_surface_destroy (xcb); + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_surface_init (&surface->base, + &_cairo_xlib_xcb_surface_backend, + _cairo_xlib_xcb_device_create (dpy, xcb->device), + xcb->content, + FALSE); /* is_vector */ + + /* _cairo_surface_init() got another reference to the device, drop ours */ + cairo_device_destroy (surface->base.device); + + surface->display = dpy; + surface->screen = scr; + surface->visual = visual; + surface->format = format; + surface->xcb = (cairo_xcb_surface_t *) xcb; + + return &surface->base; +} + +static Screen * +_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) +{ + int s, d, v; + + for (s = 0; s < ScreenCount (dpy); s++) { + Screen *screen; + + screen = ScreenOfDisplay (dpy, s); + if (visual == DefaultVisualOfScreen (screen)) + return screen; + + for (d = 0; d < screen->ndepths; d++) { + Depth *depth; + + depth = &screen->depths[d]; + for (v = 0; v < depth->nvisuals; v++) + if (visual == &depth->visuals[v]) + return screen; + } + } + + return NULL; +} + +cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) +{ + Screen *scr; + xcb_visualtype_t xcb_visual; + + scr = _cairo_xlib_screen_from_visual (dpy, visual); + if (scr == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + + xcb_visual.visual_id = visual->visualid; +#if defined(__cplusplus) || defined(c_plusplus) + xcb_visual._class = visual->c_class; +#else + xcb_visual._class = visual->class; +#endif + xcb_visual.bits_per_rgb_value = visual->bits_per_rgb; + xcb_visual.colormap_entries = visual->map_entries; + xcb_visual.red_mask = visual->red_mask; + xcb_visual.green_mask = visual->green_mask; + xcb_visual.blue_mask = visual->blue_mask; + + return _cairo_xlib_xcb_surface_create (dpy, scr, visual, NULL, + cairo_xcb_surface_create (XGetXCBConnection (dpy), + drawable, + &xcb_visual, + width, height)); +} + +static xcb_screen_t * +_cairo_xcb_screen_from_root (xcb_connection_t *connection, + xcb_window_t id) +{ + xcb_screen_iterator_t s; + + s = xcb_setup_roots_iterator (xcb_get_setup (connection)); + for (; s.rem; xcb_screen_next (&s)) { + if (s.data->root == id) + return s.data; + } + + return NULL; +} + +cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + Screen *scr, + int width, + int height) +{ + xcb_connection_t *connection = XGetXCBConnection (dpy); + xcb_screen_t *screen = _cairo_xcb_screen_from_root (connection, (xcb_window_t) scr->root); + return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, NULL, + cairo_xcb_surface_create_for_bitmap (connection, + screen, + bitmap, + width, height)); +} + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *scr, + XRenderPictFormat *format, + int width, + int height) +{ + xcb_render_pictforminfo_t xcb_format; + xcb_connection_t *connection; + xcb_screen_t *screen; + + xcb_format.id = format->id; + xcb_format.type = format->type; + xcb_format.depth = format->depth; + xcb_format.direct.red_shift = format->direct.red; + xcb_format.direct.red_mask = format->direct.redMask; + xcb_format.direct.green_shift = format->direct.green; + xcb_format.direct.green_mask = format->direct.greenMask; + xcb_format.direct.blue_shift = format->direct.blue; + xcb_format.direct.blue_mask = format->direct.blueMask; + xcb_format.direct.alpha_shift = format->direct.alpha; + xcb_format.direct.alpha_mask = format->direct.alphaMask; + xcb_format.colormap = format->colormap; + + connection = XGetXCBConnection (dpy); + screen = _cairo_xcb_screen_from_root (connection, (xcb_window_t) scr->root); + + return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, format, + cairo_xcb_surface_create_with_xrender_format (connection, screen, + drawable, + &xcb_format, + width, height)); +} + +XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) +{ + cairo_xlib_xcb_surface_t *xlib_surface = (cairo_xlib_xcb_surface_t *) surface; + + /* Throw an error for a non-xlib surface */ + if (surface->type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return xlib_surface->format; +} +#endif + +void +cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + cairo_xcb_surface_set_size (&surface->xcb->base, width, height); + if (unlikely (surface->xcb->base.status)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (surface->xcb->base.status)); + } +} + +void +cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, + Drawable drawable, + int width, + int height) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *)abstract_surface; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + cairo_xcb_surface_set_drawable (&surface->xcb->base, drawable, width, height); + if (unlikely (surface->xcb->base.status)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (surface->xcb->base.status)); + } +} + +Display * +cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->display; +} + +Drawable +cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (unlikely (abstract_surface->finished)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); + return 0; + } + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + /* This can happen when e.g. create_similar falls back to an image surface + * because we don't have the RENDER extension. */ + if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->drawable; +} + +Screen * +cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->screen; +} + +Visual * +cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->visual; +} + +int +cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (unlikely (abstract_surface->finished)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); + return 0; + } + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + /* This can happen when e.g. create_similar falls back to an image surface + * because we don't have the RENDER extension. */ + if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->depth; +} + +int +cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (unlikely (abstract_surface->finished)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); + return 0; + } + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + /* This can happen when e.g. create_similar falls back to an image surface + * because we don't have the RENDER extension. */ + if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->width; +} + +int +cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface; + + if (unlikely (abstract_surface->finished)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED); + return 0; + } + if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + /* This can happen when e.g. create_similar falls back to an image surface + * because we don't have the RENDER extension. */ + if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->xcb->height; +} + +void +cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, + int major, int minor) +{ + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; + + if (device == NULL || device->status) + return; + + if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) + return; + + cairo_xcb_device_debug_cap_xrender_version (display->xcb_device, + major, minor); +} + +void +cairo_xlib_device_debug_set_precision (cairo_device_t *device, + int precision) +{ + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; + + if (device == NULL || device->status) + return; + if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return; + } + + cairo_xcb_device_debug_set_precision (display->xcb_device, precision); +} + +int +cairo_xlib_device_debug_get_precision (cairo_device_t *device) +{ + cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device; + + if (device == NULL || device->status) + return -1; + if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) { + cairo_status_t status; + + status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + (void) status; + return -1; + } + + return cairo_xcb_device_debug_get_precision (display->xcb_device); +} + +#endif /* CAIRO_HAS_XLIB_XCB_FUNCTIONS */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h index 52f41591591a..90769467b2da 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h +++ b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h @@ -96,26 +96,35 @@ __attribute__((__unused__)) static void _void_consume_free (Display *p, XID #define PictOpBlendMaximum 0x3e #endif -/* There doesn't appear to be a simple #define that we can conditionalize - * on. Instead, use the version; gradients were introdiced in 0.10. */ -#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 +#if !HAVE_XRENDERCREATESOLIDFILL +#define XRenderCreateSolidFill _int_consume +#endif + +#if !HAVE_XRENDERCREATELINEARGRADIENT #define XRenderCreateLinearGradient _int_consume + +typedef struct _XLinearGradient { + XPointFixed p1; + XPointFixed p2; +} XLinearGradient; +#endif + +#if !HAVE_XRENDERCREATERADIALGRADIENT #define XRenderCreateRadialGradient _int_consume -#define XRenderCreateConicalGradient _int_consume + typedef struct _XCircle { XFixed x; XFixed y; XFixed radius; } XCircle; -typedef struct _XLinearGradient { - XPointFixed p1; - XPointFixed p2; -} XLinearGradient; - typedef struct _XRadialGradient { XCircle inner; XCircle outer; } XRadialGradient; +#endif + +#if !HAVE_XRENDERCREATECONICALGRADIENT +#define XRenderCreateConicalGradient _int_consume typedef struct _XConicalGradient { XPointFixed center; diff --git a/gfx/cairo/cairo/src/cairo-xlib.h b/gfx/cairo/cairo/src/cairo-xlib.h index 4ee592ce49c2..ecf8d6c86be2 100644 --- a/gfx/cairo/cairo/src/cairo-xlib.h +++ b/gfx/cairo/cairo/src/cairo-xlib.h @@ -91,6 +91,24 @@ cairo_xlib_surface_get_width (cairo_surface_t *surface); cairo_public int cairo_xlib_surface_get_height (cairo_surface_t *surface); +/* debug interface */ + +cairo_public void +cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device, + int major_version, + int minor_version); + +/* + * @precision: -1 implies automatically choose based on antialiasing mode, + * any other value overrides and sets the corresponding PolyMode. + */ +cairo_public void +cairo_xlib_device_debug_set_precision (cairo_device_t *device, + int precision); + +cairo_public int +cairo_xlib_device_debug_get_precision (cairo_device_t *device); + CAIRO_END_DECLS #else /* CAIRO_HAS_XLIB_SURFACE */ diff --git a/gfx/cairo/cairo/src/cairo-xml-surface.c b/gfx/cairo/cairo/src/cairo-xml-surface.c index 5583829e7562..43cb6dddfad8 100644 --- a/gfx/cairo/cairo/src/cairo-xml-surface.c +++ b/gfx/cairo/cairo/src/cairo-xml-surface.c @@ -36,7 +36,7 @@ /* 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 what a log of + * such as cairo-sphinx, or by application test-suites that want a log of * operations. */ @@ -46,9 +46,11 @@ #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-private.h" +#include "cairo-recording-surface-inline.h" #define static cairo_warn static @@ -118,8 +120,8 @@ _extend_to_string (cairo_extend_t extend) { static const char *names[] = { "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ - "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */ - "ExtendMode::REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ }; assert (extend < ARRAY_LENGTH (names)); @@ -131,7 +133,7 @@ _filter_to_string (cairo_filter_t filter) { static const char *names[] = { "FILTER_FAST", /* CAIRO_FILTER_FAST */ - "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ "FILTER_BEST", /* CAIRO_FILTER_BEST */ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ @@ -156,10 +158,13 @@ static const char * _antialias_to_string (cairo_antialias_t antialias) { static const char *names[] = { - "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ - "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ - "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ - "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + "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]; @@ -205,6 +210,7 @@ _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_A8: return "A8"; @@ -252,7 +258,7 @@ _cairo_xml_create_internal (cairo_output_stream_t *stream) { cairo_xml_t *xml; - xml = malloc (sizeof (cairo_xml_t)); + xml = _cairo_malloc (sizeof (cairo_xml_t)); if (unlikely (xml == NULL)) return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); @@ -415,13 +421,12 @@ _cairo_xml_close_path (void *closure) static void _cairo_xml_emit_path (cairo_xml_t *xml, - cairo_path_fixed_t *path) + const cairo_path_fixed_t *path) { cairo_status_t status; _cairo_xml_printf_start (xml, ""); status = _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_xml_move_to, _cairo_xml_line_to, _cairo_xml_curve_to, @@ -453,20 +458,80 @@ 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, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_printf (xml, ""); + _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, ""); + _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, ""); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_status_t _cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface, - cairo_clip_path_t *clip_path) + const cairo_clip_path_t *clip_path) { cairo_box_t box; cairo_status_t status; cairo_xml_t *xml; - if (clip_path->prev != NULL) { - status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev); - if (unlikely (status)) - return status; - } + 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 && @@ -500,11 +565,17 @@ _cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface, static cairo_status_t _cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, - cairo_clip_t *clip) + 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); } @@ -555,10 +626,8 @@ _cairo_xml_emit_linear (cairo_xml_t *xml, { _cairo_xml_printf (xml, "", - _cairo_fixed_to_double (linear->p1.x), - _cairo_fixed_to_double (linear->p1.y), - _cairo_fixed_to_double (linear->p2.x), - _cairo_fixed_to_double (linear->p2.y)); + 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); @@ -572,12 +641,8 @@ _cairo_xml_emit_radial (cairo_xml_t *xml, { _cairo_xml_printf (xml, "", - _cairo_fixed_to_double (radial->c1.x), - _cairo_fixed_to_double (radial->c1.y), - _cairo_fixed_to_double (radial->r1), - _cairo_fixed_to_double (radial->c2.x), - _cairo_fixed_to_double (radial->c2.y), - _cairo_fixed_to_double (radial->r2)); + 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); @@ -692,7 +757,7 @@ static cairo_int_status_t _cairo_xml_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); @@ -719,10 +784,10 @@ _cairo_xml_surface_paint (void *abstract_surface, 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, - cairo_clip_t *clip) + 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); @@ -755,13 +820,13 @@ static cairo_int_status_t _cairo_xml_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); @@ -811,11 +876,11 @@ static cairo_int_status_t _cairo_xml_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t*path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); @@ -868,11 +933,11 @@ _cairo_xml_emit_type42_font (cairo_xml_t *xml, if (unlikely (status)) return status; - buf = malloc (size); + 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, NULL); + status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); if (unlikely (status)) { free (buf); return status; @@ -930,7 +995,7 @@ _cairo_xml_emit_scaled_font (cairo_xml_t *xml, cairo_glyph_t *glyphs, int num_glyphs) { - cairo_status_t status; + cairo_int_status_t status; _cairo_xml_printf (xml, ""); _cairo_xml_indent (xml, 2); @@ -942,7 +1007,7 @@ _cairo_xml_emit_scaled_font (cairo_xml_t *xml, } _cairo_xml_indent (xml, -2); - _cairo_xml_printf (xml, ""); + _cairo_xml_printf (xml, ""); return status; } @@ -954,8 +1019,7 @@ _cairo_xml_surface_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { cairo_xml_surface_t *surface = abstract_surface; cairo_xml_t *xml = to_xml (surface); @@ -989,48 +1053,41 @@ _cairo_xml_surface_glyphs (void *abstract_surface, _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, ""); - *remaining_glyphs = 0; return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t _cairo_xml_surface_backend = { CAIRO_SURFACE_TYPE_XML, - _cairo_xml_surface_create_similar, NULL, - NULL, NULL, /* source image */ - NULL, NULL, /* dst image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, NULL, /* copy/show page */ + + _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, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* font fini */ - NULL, /* scaled_glyph_fini */ - /* The 5 high level operations */ _cairo_xml_surface_paint, _cairo_xml_surface_mask, _cairo_xml_surface_stroke, _cairo_xml_surface_fill, - _cairo_xml_surface_glyphs, - - NULL, /* snapshot */ - - NULL, /* is_similar */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - /* The alternate high-level text operation */ - NULL, NULL, /* has, show_text_glyphs */ + _cairo_xml_surface_glyphs, }; static cairo_surface_t * @@ -1041,14 +1098,15 @@ _cairo_xml_surface_create_internal (cairo_device_t *device, { cairo_xml_surface_t *surface; - surface = malloc (sizeof (cairo_xml_surface_t)); + 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); + content, + TRUE); /* is_vector */ surface->width = width; surface->height = height; diff --git a/gfx/cairo/cairo/src/cairo.c b/gfx/cairo/cairo/src/cairo.c index eeee0206e4b7..b2bda657d20b 100644 --- a/gfx/cairo/cairo/src/cairo.c +++ b/gfx/cairo/cairo/src/cairo.c @@ -3,6 +3,7 @@ * * Copyright © 2002 University of Southern California * Copyright © 2005 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 @@ -34,14 +35,20 @@ * * Contributor(s): * Carl D. Worth + * Chris Wilson */ #include "cairoint.h" #include "cairo-private.h" -#include "cairo-arc-private.h" +#include "cairo-backend-private.h" #include "cairo-error-private.h" #include "cairo-path-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" + +#include /** * SECTION:cairo @@ -56,9 +63,9 @@ * draw shapes with cairo_stroke() or cairo_fill(). * * #cairo_t's can be pushed to a stack via cairo_save(). - * They may then safely be changed, without loosing the current state. + * They may then safely be changed, without losing the current state. * Use cairo_restore() to restore to the saved state. - */ + **/ /** * SECTION:cairo-text @@ -85,7 +92,7 @@ * the pangocairo that is part of the Pango text layout and rendering library. * Pango is available from http://www.pango.org/. - */ + **/ /** * SECTION:cairo-transforms @@ -98,91 +105,276 @@ * drawing instruments from the user space into the * surface's canonical coordinate system, also known as the device * space. - */ - -#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1) - -#if !defined(INFINITY) -#define INFINITY HUGE_VAL -#endif - -static const cairo_t _cairo_nil = { - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NO_MEMORY, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - NULL, /* gstate */ - {{ 0 }, { 0 }}, /* gstate_tail */ - NULL, /* gstate_freelist */ - {{ /* path */ - { 0, 0 }, /* last_move_point */ - { 0, 0 }, /* current point */ - FALSE, /* has_current_point */ - FALSE, /* has_last_move_point */ - FALSE, /* has_curve_to */ - FALSE, /* is_box */ - FALSE, /* maybe_fill_region */ - TRUE, /* is_empty_fill */ - { {0, 0}, {0, 0}}, /* extents */ - {{{NULL,NULL}}} /* link */ - }} -}; - -static const cairo_t _cairo_nil__null_pointer = { - CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ - CAIRO_STATUS_NULL_POINTER, /* status */ - { 0, 0, 0, NULL }, /* user_data */ - NULL, /* gstate */ - {{ 0 }, { 0 }}, /* gstate_tail */ - NULL, /* gstate_freelist */ - {{ /* path */ - { 0, 0 }, /* last_move_point */ - { 0, 0 }, /* current point */ - FALSE, /* has_current_point */ - FALSE, /* has_last_move_point */ - FALSE, /* has_curve_to */ - FALSE, /* is_box */ - FALSE, /* maybe_fill_region */ - TRUE, /* is_empty_fill */ - { {0, 0}, {0, 0}}, /* extents */ - {{{NULL,NULL}}} /* link */ - }} -}; -#include + **/ /** - * _cairo_error: - * @status: a status value indicating an error, (eg. not - * %CAIRO_STATUS_SUCCESS) + * SECTION:cairo-tag + * @Title: Tags and Links + * @Short_Description: Hyperlinks and document structure + * @See_Also: #cairo_pdf_surface_t * - * Checks that status is an error status, but does nothing else. + * The tag functions provide the ability to specify hyperlinks and + * document logical structure on supported backends. The following tags are supported: + * * [Link][link] - Create a hyperlink + * * [Destinations][dest] - Create a hyperlink destination + * * [Document Structure Tags][doc-struct] - Create PDF Document Structure * - * All assignments of an error status to any user-visible object - * within the cairo application should result in a call to - * _cairo_error(). + * # Link Tags # {#link} + * A hyperlink is specified by enclosing the hyperlink text with the %CAIRO_TAG_LINK tag. * - * The purpose of this function is to allow the user to set a - * breakpoint in _cairo_error() to generate a stack trace for when the - * user causes cairo to detect an error. + * For example: + * + * cairo_tag_begin (cr, CAIRO_TAG_LINK, "uri='https://cairographics.org'"); + * cairo_move_to (cr, 50, 50); + * cairo_show_text (cr, "This is a link to the cairo website."); + * cairo_tag_end (cr, CAIRO_TAG_LINK); + * + * + * The PDF backend uses one or more rectangles to define the clickable + * area of the link. By default cairo will use the extents of the + * drawing operations enclosed by the begin/end link tags to define the + * clickable area. In some cases, such as a link split across two + * lines, the default rectangle is undesirable. + * + * @rect: [optional] The "rect" attribute allows the application to + * specify one or more rectangles that form the clickable region. The + * value of this attribute is an array of floats. Each rectangle is + * specified by four elements in the array: x, y, width, height. The + * array size must be a multiple of four. + * + * An example of creating a link with user specified clickable region: + * + * struct text { + * const char *s; + * double x, y; + * }; + * const struct text text1 = { "This link is split", 450, 70 }; + * const struct text text2 = { "across two lines", 50, 70 }; + * cairo_text_extents_t text1_extents; + * cairo_text_extents_t text2_extents; + * char attribs[100]; + * + * cairo_text_extents (cr, text1.s, &text1_extents); + * cairo_text_extents (cr, text2.s, &text2_extents); + * sprintf (attribs, + * "rect=[%f %f %f %f %f %f %f %f] uri='https://cairographics.org'", + * text1_extents.x_bearing + text1.x, + * text1_extents.y_bearing + text1.y, + * text1_extents.width, + * text1_extents.height, + * text2_extents.x_bearing + text2.x, + * text2_extents.y_bearing + text2.y, + * text2_extents.width, + * text2_extents.height); + * + * cairo_tag_begin (cr, CAIRO_TAG_LINK, attribs); + * cairo_move_to (cr, text1.x, text1.y); + * cairo_show_text (cr, text1.s); + * cairo_move_to (cr, text2.x, text2.y); + * cairo_show_text (cr, text2.s); + * cairo_tag_end (cr, CAIRO_TAG_LINK); + * + * + * There are three types of links. Each type has its own attributes as detailed below. + * * [Internal Links][internal-link] - A link to a location in the same document + * * [URI Links][uri-link] - A link to a Uniform resource identifier + * * [File Links][file-link] - A link to a location in another document + * + * ## Internal Links ## {#internal-link} + * An internal link is a link to a location in the same document. The destination + * is specified with either: + * + * @dest: a UTF-8 string specifying the destination in the PDF file to link + * to. Destinations are created with the %CAIRO_TAG_DEST tag. + * + * or the two attributes: + * + * @page: An integer specifying the page number in the PDF file to link to. + * + * @pos: [optional] An array of two floats specifying the x,y position + * on the page. + * + * An example of the link attributes to link to a page and x,y position: + * + * "page=3 pos=[3.1 6.2]" + * + * + * ## URI Links ## {#uri-link} + * A URI link is a link to a Uniform Resource Identifier ([RFC 2396](http://tools.ietf.org/html/rfc2396)). + * + * A URI is specified with the following attribute: + * + * @uri: An ASCII string specifying the URI. + * + * An example of the link attributes to the cairo website: + * + * "uri='https://cairographics.org'" + * + * + * ## File Links ## {#file-link} + * A file link is a link a location in another PDF file. + * + * The file attribute (required) specifies the name of the PDF file: + * + * @file: File name of PDF file to link to. + * + * The position is specified by either: + * + * @dest: a UTF-8 string specifying the named destination in the PDF file. + * + * or + * + * @page: An integer specifying the page number in the PDF file. + * + * @pos: [optional] An array of two floats specifying the x,y + * position on the page. Position coordinates in external files are in PDF + * coordinates (0,0 at bottom left). + * + * An example of the link attributes to PDF file: + * + * "file='document.pdf' page=16 pos=[25 40]" + * + * + * # Destination Tags # {#dest} + * + * A destination is specified by enclosing the destination drawing + * operations with the %CAIRO_TAG_DEST tag. + * + * @name: [required] A UTF-8 string specifying the name of this destination. + * + * @x: [optional] A float specifying the x coordinate of destination + * position on this page. If not specified the default + * x coordinate is the left side of the extents of the + * operations enclosed by the %CAIRO_TAG_DEST begin/end tags. If + * no operations are enclosed, the x coordidate is 0. + * + * @y: [optional] A float specifying the y coordinate of destination + * position on this page. If not specified the default + * y coordinate is the top of the extents of the + * operations enclosed by the %CAIRO_TAG_DEST begin/end tags. If + * no operations are enclosed, the y coordidate is 0. + * + * @internal: A boolean that if true, the destination name may be + * omitted from PDF where possible. In this case, links + * refer directly to the page and position instead of via + * the named destination table. Note that if this + * destination is referenced by another PDF (see [File Links][file-link]), + * this attribute must be false. Default is false. + * + * + * /* Create a hyperlink */ + * cairo_tag_begin (cr, CAIRO_TAG_LINK, "dest='mydest' internal"); + * cairo_move_to (cr, 50, 50); + * cairo_show_text (cr, "This is a hyperlink."); + * cairo_tag_end (cr, CAIRO_TAG_LINK); + * + * /* Create a destination */ + * cairo_tag_begin (cr, CAIRO_TAG_DEST, "name='mydest'"); + * cairo_move_to (cr, 50, 250); + * cairo_show_text (cr, "This paragraph is the destination of the above link."); + * cairo_tag_end (cr, CAIRO_TAG_DEST); + * + * + * # Document Structure (PDF) # {#doc-struct} + * + * The document structure tags provide a means of specifying structural information + * such as headers, paragraphs, tables, and figures. The inclusion of structural information facilitates: + * * Extraction of text and graphics for copy and paste + * * Reflow of text and graphics in the viewer + * * Processing text eg searching and indexing + * * Conversion to other formats + * * Accessability support + * + * The list of structure types is specified in section 14.8.4 of the + * [PDF Reference](http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf). + * + * Note the PDF "Link" structure tag is the same as the cairo %CAIRO_TAG_LINK tag. + * + * The following example creates a document structure for a document containing two section, each with + * a header and a paragraph. + * + * + * cairo_tag_begin (cr, "Document", NULL); + * + * cairo_tag_begin (cr, "Sect", NULL); + * cairo_tag_begin (cr, "H1", NULL); + * cairo_show_text (cr, "Heading 1"); + * cairo_tag_end (cr, "H1"); + * + * cairo_tag_begin (cr, "P", NULL); + * cairo_show_text (cr, "Paragraph 1"); + * cairo_tag_end (cr, "P"); + * cairo_tag_end (cr, "Sect"); + * + * cairo_tag_begin (cr, "Sect", NULL); + * cairo_tag_begin (cr, "H1", NULL); + * cairo_show_text (cr, "Heading 2"); + * cairo_tag_end (cr, "H1"); + * + * cairo_tag_begin (cr, "P", NULL); + * cairo_show_text (cr, "Paragraph 2"); + * cairo_tag_end (cr, "P"); + * cairo_tag_end (cr, "Sect"); + * + * cairo_tag_end (cr, "Document"); + * * - * Return value: the error status. **/ -cairo_status_t -_cairo_error (cairo_status_t status) -{ - CAIRO_ENSURE_UNIQUE; - assert (_cairo_status_is_error (status)); -#ifdef MOZILLA_VERSION - static int abort_on_error = -1; - if (abort_on_error < 0) { - abort_on_error = (getenv("MOZ_CAIRO_ERROR_ABORT") != NULL) ? 1 : 0; +#define DEFINE_NIL_CONTEXT(status) \ + { \ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ + status, /* status */ \ + { 0, 0, 0, NULL }, /* user_data */ \ + NULL \ } - if (abort_on_error) { - abort(); - } -#endif - return status; -} + +static const cairo_t _cairo_nil[] = { + DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_MEMORY), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_RESTORE), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_POP_GROUP), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_CURRENT_POINT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MATRIX), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STATUS), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_NULL_POINTER), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRING), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_PATH_DATA), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_READ_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_WRITE_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_FINISHED), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_TYPE_MISMATCH), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_PATTERN_TYPE_MISMATCH), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CONTENT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_FORMAT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_VISUAL), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_FILE_NOT_FOUND), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DASH), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DSC_COMMENT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_INDEX), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_TEMP_FILE_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRIDE), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_FONT_TYPE_MISMATCH), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_IMMUTABLE), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_NEGATIVE_COUNT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CLUSTERS), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SLANT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_WEIGHT), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SIZE), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_TYPE_MISMATCH), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_JBIG2_GLOBAL_MISSING), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_PNG_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_FREETYPE_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_WIN32_GDI_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_TAG_ERROR) + +}; +COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1); /** * _cairo_set_error: @@ -208,145 +400,19 @@ _cairo_set_error (cairo_t *cr, cairo_status_t status) _cairo_status_set_error (&cr->status, _cairo_error (status)); } -/* We keep a small stash of contexts to reduce malloc pressure */ -#define CAIRO_STASH_SIZE 4 -#if CAIRO_NO_MUTEX -static struct { - cairo_t pool[CAIRO_STASH_SIZE]; - int occupied; -} _context_stash; - -static cairo_t * -_context_get (void) -{ - int avail; - - avail = ffs (~_context_stash.occupied) - 1; - if (avail >= CAIRO_STASH_SIZE) - return malloc (sizeof (cairo_t)); - - _context_stash.occupied |= 1 << avail; - return &_context_stash.pool[avail]; -} - -static void -_context_put (cairo_t *cr) -{ - if (cr < &_context_stash.pool[0] || - cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) - { - free (cr); - return; - } - - _context_stash.occupied &= ~(1 << (cr - &_context_stash.pool[0])); -} -#elif HAS_ATOMIC_OPS -static struct { - cairo_t pool[CAIRO_STASH_SIZE]; - cairo_atomic_int_t occupied; -} _context_stash; - -static cairo_t * -_context_get (void) -{ - cairo_atomic_int_t avail, old, new; - - do { - old = _cairo_atomic_int_get (&_context_stash.occupied); - avail = ffs (~old) - 1; - if (avail >= CAIRO_STASH_SIZE) - return malloc (sizeof (cairo_t)); - - new = old | (1 << avail); - } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new)); - - return &_context_stash.pool[avail]; -} - -static void -_context_put (cairo_t *cr) -{ - cairo_atomic_int_t old, new, avail; - - if (cr < &_context_stash.pool[0] || - cr >= &_context_stash.pool[CAIRO_STASH_SIZE]) - { - free (cr); - return; - } - - avail = ~(1 << (cr - &_context_stash.pool[0])); - do { - old = _cairo_atomic_int_get (&_context_stash.occupied); - new = old & avail; - } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new)); -} -#else -#define _context_get() malloc (sizeof (cairo_t)) -#define _context_put(cr) free (cr) -#endif - -/* XXX This should disappear in favour of a common pool of error objects. */ -static cairo_t *_cairo_nil__objects[CAIRO_STATUS_LAST_STATUS + 1]; - -static cairo_t * +cairo_t * _cairo_create_in_error (cairo_status_t status) { cairo_t *cr; assert (status != CAIRO_STATUS_SUCCESS); - /* Sanity check */ - if (status < 0 || status > CAIRO_STATUS_LAST_STATUS) { - abort(); - } - - /* special case OOM in order to avoid another allocation */ - switch ((int) status) { - case CAIRO_STATUS_NO_MEMORY: - return (cairo_t *) &_cairo_nil; - case CAIRO_STATUS_NULL_POINTER: - return (cairo_t *) &_cairo_nil__null_pointer; - } - - CAIRO_MUTEX_LOCK (_cairo_error_mutex); - cr = _cairo_nil__objects[status]; - if (cr == NULL) { - cr = malloc (sizeof (cairo_t)); - if (unlikely (cr == NULL)) { - CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_t *) &_cairo_nil; - } - - *cr = _cairo_nil; - cr->status = status; - _cairo_nil__objects[status] = cr; - } - CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); + cr = (cairo_t *) &_cairo_nil[status - CAIRO_STATUS_NO_MEMORY]; + assert (status == cr->status); return cr; } -void -_cairo_reset_static_data (void) -{ - int status; - - CAIRO_MUTEX_LOCK (_cairo_error_mutex); - for (status = CAIRO_STATUS_SUCCESS; - status <= CAIRO_STATUS_LAST_STATUS; - status++) - { - if (_cairo_nil__objects[status] != NULL) { - free (_cairo_nil__objects[status]); - _cairo_nil__objects[status] = NULL; - } - } - CAIRO_MUTEX_UNLOCK (_cairo_error_mutex); -} - /** * cairo_create: * @target: target surface for the context @@ -355,7 +421,8 @@ _cairo_reset_static_data (void) * default values and with @target as a target surface. The target * surface should be constructed with a backend-specific function such * as cairo_image_surface_create() (or any other - * cairo_backend_surface_create() variant). + * cairo_backend_surface_create() + * variant). * * This function references @target, so you can immediately * call cairo_surface_destroy() on it if you don't need to @@ -366,46 +433,43 @@ _cairo_reset_static_data (void) * with cairo_destroy() when you are done using the #cairo_t. * This function never returns %NULL. If memory cannot be * allocated, a special #cairo_t object will be returned on - * which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. - * You can use this object normally, but no drawing will - * be done. + * which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. If + * you attempt to target a surface which does not support + * writing (such as #cairo_mime_surface_t) then a + * %CAIRO_STATUS_WRITE_ERROR will be raised. You can use this + * object normally, but no drawing will be done. + * + * Since: 1.0 **/ cairo_t * cairo_create (cairo_surface_t *target) { - cairo_t *cr; - cairo_status_t status; - if (unlikely (target == NULL)) return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); if (unlikely (target->status)) return _cairo_create_in_error (target->status); + if (unlikely (target->finished)) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); - cr = _context_get (); - if (unlikely (cr == NULL)) - return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + if (target->backend->create_context == NULL) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); - CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1); + return target->backend->create_context (target); - cr->status = CAIRO_STATUS_SUCCESS; - - _cairo_user_data_array_init (&cr->user_data); - _cairo_path_fixed_init (cr->path); - - cr->gstate = &cr->gstate_tail[0]; - cr->gstate_freelist = &cr->gstate_tail[1]; - cr->gstate_tail[1].next = NULL; - - status = _cairo_gstate_init (cr->gstate, target); - if (unlikely (status)) { - _context_put (cr); - cr = _cairo_create_in_error (status); - } - - return cr; } slim_hidden_def (cairo_create); +void +_cairo_init (cairo_t *cr, + const cairo_backend_t *backend) +{ + CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1); + cr->status = CAIRO_STATUS_SUCCESS; + _cairo_user_data_array_init (&cr->user_data); + + cr->backend = backend; +} + /** * cairo_reference: * @cr: a #cairo_t @@ -414,10 +478,12 @@ slim_hidden_def (cairo_create); * @cr from being destroyed until a matching call to cairo_destroy() * is made. * - * The number of references to a #cairo_t can be get using - * cairo_get_reference_count(). + * Use cairo_get_reference_count() to get the number of references to + * a #cairo_t. * * Return value: the referenced #cairo_t. + * + * Since: 1.0 **/ cairo_t * cairo_reference (cairo_t *cr) @@ -432,6 +498,12 @@ cairo_reference (cairo_t *cr) return cr; } +void +_cairo_fini (cairo_t *cr) +{ + _cairo_user_data_array_fini (&cr->user_data); +} + /** * cairo_destroy: * @cr: a #cairo_t @@ -439,12 +511,12 @@ cairo_reference (cairo_t *cr) * Decreases the reference count on @cr by one. If the result * is zero, then @cr and all associated resources are freed. * See cairo_reference(). + * + * Since: 1.0 **/ void cairo_destroy (cairo_t *cr) { - cairo_surface_t *surface; - if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) return; @@ -453,36 +525,7 @@ cairo_destroy (cairo_t *cr) if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) return; - while (cr->gstate != &cr->gstate_tail[0]) { - if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist)) - break; - } - - /* The context is expected (>99% of all use cases) to be held for the - * duration of a single expose event/sequence of graphic operations. - * Therefore, on destroy we explicitly flush the Cairo pipeline of any - * pending operations. - */ - surface = _cairo_gstate_get_original_target (cr->gstate); - if (surface != NULL) - cairo_surface_flush (surface); - - _cairo_gstate_fini (cr->gstate); - cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */ - while (cr->gstate_freelist != NULL) { - cairo_gstate_t *gstate = cr->gstate_freelist; - cr->gstate_freelist = gstate->next; - free (gstate); - } - - _cairo_path_fixed_fini (cr->path); - - _cairo_user_data_array_fini (&cr->user_data); - - /* mark the context as invalid to protect against misuse */ - cr->status = CAIRO_STATUS_NULL_POINTER; - - _context_put (cr); + cr->backend->destroy (cr); } slim_hidden_def (cairo_destroy); @@ -504,8 +547,7 @@ void * cairo_get_user_data (cairo_t *cr, const cairo_user_data_key_t *key) { - return _cairo_user_data_array_get_data (&cr->user_data, - key); + return _cairo_user_data_array_get_data (&cr->user_data, key); } /** @@ -574,6 +616,8 @@ cairo_get_reference_count (cairo_t *cr) * a #cairo_t is freed. If the reference count of a #cairo_t * drops to zero in response to a call to cairo_destroy(), * any saved states will be freed along with the #cairo_t. + * + * Since: 1.0 **/ void cairo_save (cairo_t *cr) @@ -583,7 +627,7 @@ cairo_save (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist); + status = cr->backend->save (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -596,6 +640,8 @@ slim_hidden_def(cairo_save); * Restores @cr to the state saved by a preceding call to * cairo_save() and removes that state from the stack of * saved states. + * + * Since: 1.0 **/ void cairo_restore (cairo_t *cr) @@ -605,7 +651,7 @@ cairo_restore (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist); + status = cr->backend->restore (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -654,7 +700,7 @@ slim_hidden_def(cairo_restore); * * * Since: 1.2 - */ + **/ void cairo_push_group (cairo_t *cr) { @@ -679,72 +725,16 @@ cairo_push_group (cairo_t *cr) * detailed description of group rendering. * * Since: 1.2 - */ + **/ void cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) { - cairo_surface_t *group_surface; - cairo_clip_t *clip; cairo_status_t status; if (unlikely (cr->status)) return; - clip = _cairo_gstate_get_clip (cr->gstate); - if (clip->all_clipped) { - group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); - status = group_surface->status; - if (unlikely (status)) - goto bail; - } else { - cairo_surface_t *parent_surface; - const cairo_rectangle_int_t *clip_extents; - cairo_rectangle_int_t extents; - cairo_matrix_t matrix; - cairo_bool_t is_empty; - - parent_surface = _cairo_gstate_get_target (cr->gstate); - - /* Get the extents that we'll use in creating our new group surface */ - is_empty = _cairo_surface_get_extents (parent_surface, &extents); - clip_extents = _cairo_clip_get_extents (_cairo_gstate_get_clip (cr->gstate)); - if (clip_extents != NULL) - is_empty = _cairo_rectangle_intersect (&extents, clip_extents); - - group_surface = _cairo_surface_create_similar_solid (parent_surface, - content, - extents.width, - extents.height, - CAIRO_COLOR_TRANSPARENT, - TRUE); - status = group_surface->status; - if (unlikely (status)) - goto bail; - - /* Set device offsets on the new surface so that logically it appears at - * the same location on the parent surface -- when we pop_group this, - * the source pattern will get fixed up for the appropriate target surface - * device offsets, so we want to set our own surface offsets from /that/, - * and not from the device origin. */ - cairo_surface_set_device_offset (group_surface, - parent_surface->device_transform.x0 - extents.x, - parent_surface->device_transform.y0 - extents.y); - - /* If we have a current path, we need to adjust it to compensate for - * the device offset just applied. */ - cairo_matrix_init_translate (&matrix, -extents.x, -extents.y); - _cairo_path_fixed_transform (cr->path, &matrix); - } - - /* create a new gstate for the redirect */ - cairo_save (cr); - if (unlikely (cr->status)) - goto bail; - - status = _cairo_gstate_redirect_target (cr->gstate, group_surface); - -bail: - cairo_surface_destroy (group_surface); + status = cr->backend->push_group (cr, content); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -774,64 +764,14 @@ slim_hidden_def(cairo_push_group_with_content); cairo_pattern_t * cairo_pop_group (cairo_t *cr) { - cairo_surface_t *group_surface, *parent_target; cairo_pattern_t *group_pattern; - cairo_matrix_t group_matrix, device_transform_matrix; - cairo_status_t status; if (unlikely (cr->status)) return _cairo_pattern_create_in_error (cr->status); - /* Grab the active surfaces */ - group_surface = _cairo_gstate_get_target (cr->gstate); - parent_target = _cairo_gstate_get_parent_target (cr->gstate); - - /* Verify that we are at the right nesting level */ - if (parent_target == NULL) { - _cairo_set_error (cr, CAIRO_STATUS_INVALID_POP_GROUP); - return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP); - } - - /* We need to save group_surface before we restore; we don't need - * to reference parent_target and original_target, since the - * gstate will still hold refs to them once we restore. */ - group_surface = cairo_surface_reference (group_surface); - - cairo_restore (cr); - - if (unlikely (cr->status)) { - group_pattern = _cairo_pattern_create_in_error (cr->status); - goto done; - } - - group_pattern = cairo_pattern_create_for_surface (group_surface); - status = group_pattern->status; - if (unlikely (status)) { - _cairo_set_error (cr, status); - goto done; - } - - _cairo_gstate_get_matrix (cr->gstate, &group_matrix); - /* Transform by group_matrix centered around device_transform so that when - * we call _cairo_gstate_copy_transformed_pattern the result is a pattern - * with a matrix equivalent to the device_transform of group_surface. */ - if (_cairo_surface_has_device_transform (group_surface)) { - cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform); - _cairo_pattern_transform (group_pattern, &group_matrix); - _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse); - } else { - cairo_pattern_set_matrix (group_pattern, &group_matrix); - } - - /* If we have a current path, we need to adjust it to compensate for - * the device offset just removed. */ - cairo_matrix_multiply (&device_transform_matrix, - &_cairo_gstate_get_target (cr->gstate)->device_transform, - &group_surface->device_transform_inverse); - _cairo_path_fixed_transform (cr->path, &device_transform_matrix); - -done: - cairo_surface_destroy (group_surface); + group_pattern = cr->backend->pop_group (cr); + if (unlikely (group_pattern->status)) + _cairo_set_error (cr, group_pattern->status); return group_pattern; } @@ -849,7 +789,7 @@ slim_hidden_def(cairo_pop_group); * operations: * * - * #cairo_pattern_t *group = cairo_pop_group (cr); + * cairo_pattern_t *group = cairo_pop_group (cr); * cairo_set_source (cr, group); * cairo_pattern_destroy (group); * @@ -884,6 +824,8 @@ cairo_pop_group_to_source (cairo_t *cr) * each available compositing operator. * * The default operator is %CAIRO_OPERATOR_OVER. + * + * Since: 1.0 **/ void cairo_set_operator (cairo_t *cr, cairo_operator_t op) @@ -893,38 +835,41 @@ cairo_set_operator (cairo_t *cr, cairo_operator_t op) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_operator (cr->gstate, op); + status = cr->backend->set_operator (cr, op); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_operator); -static cairo_bool_t -_current_source_matches_solid (cairo_t *cr, - double red, - double green, - double blue, - double alpha) +#if 0 +/* + * cairo_set_opacity: + * @cr: a #cairo_t + * @opacity: the level of opacity to use when compositing + * + * Sets the compositing opacity to be used for all drawing + * operations. The effect is to fade out the operations + * using the alpha value. + * + * The default opacity is 1. + */ +void +cairo_set_opacity (cairo_t *cr, double opacity) { - const cairo_pattern_t *current; - cairo_color_t color; + cairo_status_t status; - current = cr->gstate->source; - if (current->type != CAIRO_PATTERN_TYPE_SOLID) - return FALSE; + if (unlikely (cr->status)) + return; - red = _cairo_restrict_value (red, 0.0, 1.0); - green = _cairo_restrict_value (green, 0.0, 1.0); - blue = _cairo_restrict_value (blue, 0.0, 1.0); - alpha = _cairo_restrict_value (alpha, 0.0, 1.0); - - _cairo_color_init_rgba (&color, red, green, blue, alpha); - return _cairo_color_equal (&color, - &((cairo_solid_pattern_t *) current)->color); + status = cr->backend->set_opacity (cr, opacity); + if (unlikely (status)) + _cairo_set_error (cr, status); } +#endif + /** - * cairo_set_source_rgb + * cairo_set_source_rgb: * @cr: a cairo context * @red: red component of color * @green: green component of color @@ -940,24 +885,20 @@ _current_source_matches_solid (cairo_t *cr, * * The default source pattern is opaque black, (that is, it is * equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)). + * + * Since: 1.0 **/ void cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue) { - cairo_pattern_t *pattern; + cairo_status_t status; if (unlikely (cr->status)) return; - if (_current_source_matches_solid (cr, red, green, blue, 1.)) - return; - - /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); - - pattern = cairo_pattern_create_rgb (red, green, blue); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); + status = cr->backend->set_source_rgba (cr, red, green, blue, 1.); + if (unlikely (status)) + _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_source_rgb); @@ -979,26 +920,22 @@ slim_hidden_def (cairo_set_source_rgb); * * 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)). + * + * Since: 1.0 **/ void cairo_set_source_rgba (cairo_t *cr, double red, double green, double blue, double alpha) { - cairo_pattern_t *pattern; + cairo_status_t status; if (unlikely (cr->status)) return; - if (_current_source_matches_solid (cr, red, green, blue, alpha)) - return; - - /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); - - pattern = cairo_pattern_create_rgba (red, green, blue, alpha); - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); + status = cr->backend->set_source_rgba (cr, red, green, blue, alpha); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -1023,6 +960,8 @@ cairo_set_source_rgba (cairo_t *cr, * The resulting pattern can be queried with cairo_get_source() so * that these attributes can be modified if desired, (eg. to create a * repeating pattern with cairo_pattern_set_extend()). + * + * Since: 1.0 **/ void cairo_set_source_surface (cairo_t *cr, @@ -1030,27 +969,24 @@ cairo_set_source_surface (cairo_t *cr, double x, double y) { - cairo_pattern_t *pattern; - cairo_matrix_t matrix; + cairo_status_t status; if (unlikely (cr->status)) return; - /* push the current pattern to the freed lists */ - cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black); + if (unlikely (surface == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } - pattern = cairo_pattern_create_for_surface (surface); - - cairo_matrix_init_translate (&matrix, -x, -y); - cairo_pattern_set_matrix (pattern, &matrix); - - cairo_set_source (cr, pattern); - cairo_pattern_destroy (pattern); + status = cr->backend->set_source_surface (cr, surface, x, y); + if (unlikely (status)) + _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_source_surface); /** - * cairo_set_source + * cairo_set_source: * @cr: a cairo context * @source: a #cairo_pattern_t to be used as the source for * subsequent drawing operations. @@ -1067,6 +1003,8 @@ slim_hidden_def (cairo_set_source_surface); * The default source pattern is a solid pattern that is opaque black, * (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, * 0.0)). + * + * Since: 1.0 **/ void cairo_set_source (cairo_t *cr, cairo_pattern_t *source) @@ -1076,17 +1014,17 @@ cairo_set_source (cairo_t *cr, cairo_pattern_t *source) if (unlikely (cr->status)) return; - if (source == NULL) { + if (unlikely (source == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } - if (source->status) { + if (unlikely (source->status)) { _cairo_set_error (cr, source->status); return; } - status = _cairo_gstate_set_source (cr->gstate, source); + status = cr->backend->set_source (cr, source); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1101,6 +1039,8 @@ slim_hidden_def (cairo_set_source); * Return value: the current source pattern. This object is owned by * cairo. To keep a reference to it, you must call * cairo_pattern_reference(). + * + * Since: 1.0 **/ cairo_pattern_t * cairo_get_source (cairo_t *cr) @@ -1108,7 +1048,7 @@ cairo_get_source (cairo_t *cr) if (unlikely (cr->status)) return _cairo_pattern_create_in_error (cr->status); - return _cairo_gstate_get_source (cr->gstate); + return cr->backend->get_source (cr); } /** @@ -1126,6 +1066,8 @@ cairo_get_source (cairo_t *cr) * within Cairo is limited by the precision of its internal arithmetic, and * the prescribed @tolerance is restricted to the smallest * representable internal value. + * + * Since: 1.0 **/ void cairo_set_tolerance (cairo_t *cr, double tolerance) @@ -1135,10 +1077,7 @@ cairo_set_tolerance (cairo_t *cr, double tolerance) if (unlikely (cr->status)) return; - if (tolerance < CAIRO_TOLERANCE_MINIMUM) - tolerance = CAIRO_TOLERANCE_MINIMUM; - - status = _cairo_gstate_set_tolerance (cr->gstate, tolerance); + status = cr->backend->set_tolerance (cr, tolerance); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1156,6 +1095,8 @@ slim_hidden_def (cairo_set_tolerance); * * Note that this option does not affect text rendering, instead see * cairo_font_options_set_antialias(). + * + * Since: 1.0 **/ void cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) @@ -1165,7 +1106,7 @@ cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_antialias (cr->gstate, antialias); + status = cr->backend->set_antialias (cr, antialias); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1182,6 +1123,8 @@ cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias) * on the semantics of each available fill rule. * * The default fill rule is %CAIRO_FILL_RULE_WINDING. + * + * Since: 1.0 **/ void cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) @@ -1191,7 +1134,7 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_fill_rule (cr->gstate, fill_rule); + status = cr->backend->set_fill_rule (cr, fill_rule); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1221,6 +1164,8 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule) * construction. * * The default line width value is 2.0. + * + * Since: 1.0 **/ void cairo_set_line_width (cairo_t *cr, double width) @@ -1233,7 +1178,7 @@ cairo_set_line_width (cairo_t *cr, double width) if (width < 0.) width = 0.; - status = _cairo_gstate_set_line_width (cr->gstate, width); + status = cr->backend->set_line_width (cr, width); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1254,6 +1199,8 @@ slim_hidden_def (cairo_set_line_width); * construction. * * The default line cap style is %CAIRO_LINE_CAP_BUTT. + * + * Since: 1.0 **/ void cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) @@ -1263,7 +1210,7 @@ cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_line_cap (cr->gstate, line_cap); + status = cr->backend->set_line_cap (cr, line_cap); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1284,6 +1231,8 @@ slim_hidden_def (cairo_set_line_cap); * construction. * * The default line join style is %CAIRO_LINE_JOIN_MITER. + * + * Since: 1.0 **/ void cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) @@ -1293,7 +1242,7 @@ cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_line_join (cr->gstate, line_join); + status = cr->backend->set_line_join (cr, line_join); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1330,6 +1279,8 @@ slim_hidden_def (cairo_set_line_join); * If any value in @dashes is negative, or if all values are 0, then * @cr will be put into an error state with a status of * %CAIRO_STATUS_INVALID_DASH. + * + * Since: 1.0 **/ void cairo_set_dash (cairo_t *cr, @@ -1342,8 +1293,7 @@ cairo_set_dash (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_set_dash (cr->gstate, - dashes, num_dashes, offset); + status = cr->backend->set_dash (cr, dashes, num_dashes, offset); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1360,7 +1310,7 @@ cairo_set_dash (cairo_t *cr, * Return value: the length of the dash array, or 0 if no dash array set. * * Since: 1.4 - */ + **/ int cairo_get_dash_count (cairo_t *cr) { @@ -1369,7 +1319,7 @@ cairo_get_dash_count (cairo_t *cr) if (unlikely (cr->status)) return 0; - _cairo_gstate_get_dash (cr->gstate, NULL, &num_dashes, NULL); + cr->backend->get_dash (cr, NULL, &num_dashes, NULL); return num_dashes; } @@ -1394,7 +1344,7 @@ cairo_get_dash (cairo_t *cr, if (unlikely (cr->status)) return; - _cairo_gstate_get_dash (cr->gstate, dashes, NULL, offset); + cr->backend->get_dash (cr, dashes, NULL, offset); } /** @@ -1424,6 +1374,8 @@ cairo_get_dash (cairo_t *cr, * * A miter limit for a desired angle can be computed as: miter limit = * 1/sin(angle/2) + * + * Since: 1.0 **/ void cairo_set_miter_limit (cairo_t *cr, double limit) @@ -1433,7 +1385,7 @@ cairo_set_miter_limit (cairo_t *cr, double limit) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_miter_limit (cr->gstate, limit); + status = cr->backend->set_miter_limit (cr, limit); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1449,6 +1401,8 @@ cairo_set_miter_limit (cairo_t *cr, double limit) * user-space coordinate according to the CTM in place before the new * call to cairo_translate(). In other words, the translation of the * user-space origin takes place after any existing transformation. + * + * Since: 1.0 **/ void cairo_translate (cairo_t *cr, double tx, double ty) @@ -1458,7 +1412,7 @@ cairo_translate (cairo_t *cr, double tx, double ty) if (unlikely (cr->status)) return; - status = _cairo_gstate_translate (cr->gstate, tx, ty); + status = cr->backend->translate (cr, tx, ty); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1474,6 +1428,8 @@ slim_hidden_def (cairo_translate); * and Y user-space axes by @sx and @sy respectively. The scaling of * the axes takes place after any existing transformation of user * space. + * + * Since: 1.0 **/ void cairo_scale (cairo_t *cr, double sx, double sy) @@ -1483,7 +1439,7 @@ cairo_scale (cairo_t *cr, double sx, double sy) if (unlikely (cr->status)) return; - status = _cairo_gstate_scale (cr->gstate, sx, sy); + status = cr->backend->scale (cr, sx, sy); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1500,6 +1456,8 @@ slim_hidden_def (cairo_scale); * places after any existing transformation of user space. The * rotation direction for positive angles is from the positive X axis * toward the positive Y axis. + * + * Since: 1.0 **/ void cairo_rotate (cairo_t *cr, double angle) @@ -1509,7 +1467,7 @@ cairo_rotate (cairo_t *cr, double angle) if (unlikely (cr->status)) return; - status = _cairo_gstate_rotate (cr->gstate, angle); + status = cr->backend->rotate (cr, angle); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1522,6 +1480,8 @@ cairo_rotate (cairo_t *cr, double angle) * Modifies the current transformation matrix (CTM) by applying * @matrix as an additional transformation. The new transformation of * user space takes place after any existing transformation. + * + * Since: 1.0 **/ void cairo_transform (cairo_t *cr, @@ -1532,7 +1492,7 @@ cairo_transform (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_transform (cr->gstate, matrix); + status = cr->backend->transform (cr, matrix); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1545,6 +1505,8 @@ slim_hidden_def (cairo_transform); * * Modifies the current transformation matrix (CTM) by setting it * equal to @matrix. + * + * Since: 1.0 **/ void cairo_set_matrix (cairo_t *cr, @@ -1555,7 +1517,7 @@ cairo_set_matrix (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_set_matrix (cr->gstate, matrix); + status = cr->backend->set_matrix (cr, matrix); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1569,14 +1531,20 @@ slim_hidden_def (cairo_set_matrix); * to the identity matrix. That is, the user-space and device-space * axes will be aligned and one user-space unit will transform to one * device-space unit. + * + * Since: 1.0 **/ void cairo_identity_matrix (cairo_t *cr) { + cairo_status_t status; + if (unlikely (cr->status)) return; - _cairo_gstate_identity_matrix (cr->gstate); + status = cr->backend->set_identity_matrix (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -1588,6 +1556,8 @@ cairo_identity_matrix (cairo_t *cr) * Transform a coordinate from user space to device space by * multiplying the given point by the current transformation matrix * (CTM). + * + * Since: 1.0 **/ void cairo_user_to_device (cairo_t *cr, double *x, double *y) @@ -1595,7 +1565,7 @@ cairo_user_to_device (cairo_t *cr, double *x, double *y) if (unlikely (cr->status)) return; - _cairo_gstate_user_to_device (cr->gstate, x, y); + cr->backend->user_to_device (cr, x, y); } slim_hidden_def (cairo_user_to_device); @@ -1609,6 +1579,8 @@ slim_hidden_def (cairo_user_to_device); * function is similar to cairo_user_to_device() except that the * translation components of the CTM will be ignored when transforming * (@dx,@dy). + * + * Since: 1.0 **/ void cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) @@ -1616,7 +1588,7 @@ cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy) if (unlikely (cr->status)) return; - _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy); + cr->backend->user_to_device_distance (cr, dx, dy); } slim_hidden_def (cairo_user_to_device_distance); @@ -1629,6 +1601,8 @@ slim_hidden_def (cairo_user_to_device_distance); * Transform a coordinate from device space to user space by * multiplying the given point by the inverse of the current * transformation matrix (CTM). + * + * Since: 1.0 **/ void cairo_device_to_user (cairo_t *cr, double *x, double *y) @@ -1636,8 +1610,9 @@ cairo_device_to_user (cairo_t *cr, double *x, double *y) if (unlikely (cr->status)) return; - _cairo_gstate_device_to_user (cr->gstate, x, y); + cr->backend->device_to_user (cr, x, y); } +slim_hidden_def (cairo_device_to_user); /** * cairo_device_to_user_distance: @@ -1649,6 +1624,8 @@ cairo_device_to_user (cairo_t *cr, double *x, double *y) * function is similar to cairo_device_to_user() except that the * translation components of the inverse CTM will be ignored when * transforming (@dx,@dy). + * + * Since: 1.0 **/ void cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) @@ -1656,7 +1633,7 @@ cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) if (unlikely (cr->status)) return; - _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy); + cr->backend->device_to_user_distance (cr, dx, dy); } /** @@ -1665,45 +1642,22 @@ cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy) * * Clears the current path. After this call there will be no path and * no current point. + * + * Since: 1.0 **/ void cairo_new_path (cairo_t *cr) -{ - if (unlikely (cr->status)) - return; - - _cairo_path_fixed_fini (cr->path); - _cairo_path_fixed_init (cr->path); -} -slim_hidden_def(cairo_new_path); - -/** - * cairo_move_to: - * @cr: a cairo context - * @x: the X coordinate of the new position - * @y: the Y coordinate of the new position - * - * Begin a new sub-path. After this call the current point will be (@x, - * @y). - **/ -void -cairo_move_to (cairo_t *cr, double x, double y) { cairo_status_t status; - cairo_fixed_t x_fixed, y_fixed; if (unlikely (cr->status)) return; - _cairo_gstate_user_to_backend (cr->gstate, &x, &y); - x_fixed = _cairo_fixed_from_double (x); - y_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed); + status = cr->backend->new_path (cr); if (unlikely (status)) _cairo_set_error (cr, status); } -slim_hidden_def(cairo_move_to); +slim_hidden_def(cairo_new_path); /** * cairo_new_sub_path: @@ -1726,12 +1680,42 @@ slim_hidden_def(cairo_move_to); void cairo_new_sub_path (cairo_t *cr) { + cairo_status_t status; + if (unlikely (cr->status)) return; - _cairo_path_fixed_new_sub_path (cr->path); + status = cr->backend->new_sub_path (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); } +/** + * cairo_move_to: + * @cr: a cairo context + * @x: the X coordinate of the new position + * @y: the Y coordinate of the new position + * + * Begin a new sub-path. After this call the current point will be (@x, + * @y). + * + * Since: 1.0 + **/ +void +cairo_move_to (cairo_t *cr, double x, double y) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->move_to (cr, x, y); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_move_to); + + /** * cairo_line_to: * @cr: a cairo context @@ -1744,21 +1728,18 @@ cairo_new_sub_path (cairo_t *cr) * * If there is no current point before the call to cairo_line_to() * this function will behave as cairo_move_to(@cr, @x, @y). + * + * Since: 1.0 **/ void cairo_line_to (cairo_t *cr, double x, double y) { cairo_status_t status; - cairo_fixed_t x_fixed, y_fixed; if (unlikely (cr->status)) return; - _cairo_gstate_user_to_backend (cr->gstate, &x, &y); - x_fixed = _cairo_fixed_from_double (x); - y_fixed = _cairo_fixed_from_double (y); - - status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed); + status = cr->backend->line_to (cr, x, y); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1782,6 +1763,8 @@ slim_hidden_def (cairo_line_to); * If there is no current point before the call to cairo_curve_to() * this function will behave as if preceded by a call to * cairo_move_to(@cr, @x1, @y1). + * + * Since: 1.0 **/ void cairo_curve_to (cairo_t *cr, @@ -1790,30 +1773,14 @@ cairo_curve_to (cairo_t *cr, double x3, double y3) { cairo_status_t status; - cairo_fixed_t x1_fixed, y1_fixed; - cairo_fixed_t x2_fixed, y2_fixed; - cairo_fixed_t x3_fixed, y3_fixed; if (unlikely (cr->status)) return; - _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1); - _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2); - _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3); - - x1_fixed = _cairo_fixed_from_double (x1); - y1_fixed = _cairo_fixed_from_double (y1); - - x2_fixed = _cairo_fixed_from_double (x2); - y2_fixed = _cairo_fixed_from_double (y2); - - x3_fixed = _cairo_fixed_from_double (x3); - y3_fixed = _cairo_fixed_from_double (y3); - - status = _cairo_path_fixed_curve_to (cr->path, - x1_fixed, y1_fixed, - x2_fixed, y2_fixed, - x3_fixed, y3_fixed); + status = cr->backend->curve_to (cr, + x1, y1, + x2, y2, + x3, y3); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1831,8 +1798,8 @@ slim_hidden_def (cairo_curve_to); * Adds a circular arc of the given @radius to the current path. The * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in * the direction of increasing angles to end at @angle2. If @angle2 is - * less than @angle1 it will be progressively increased by 2*M_PI - * until it is greater than @angle1. + * less than @angle1 it will be progressively increased by + * 2*M_PI until it is greater than @angle1. * * If there is a current point, an initial line segment will be added * to the path to connect the current point to the beginning of the @@ -1840,11 +1807,12 @@ slim_hidden_def (cairo_curve_to); * calling cairo_new_sub_path() before calling cairo_arc(). * * Angles are measured in radians. An angle of 0.0 is in the direction - * of the positive X axis (in user space). An angle of %M_PI/2.0 radians - * (90 degrees) is in the direction of the positive Y axis (in - * user space). Angles increase in the direction from the positive X - * axis toward the positive Y axis. So with the default transformation - * matrix, angles increase in a clockwise direction. + * of the positive X axis (in user space). An angle of + * M_PI/2.0 radians (90 degrees) is in the + * direction of the positive Y axis (in user space). Angles increase + * in the direction from the positive X axis toward the positive Y + * axis. So with the default transformation matrix, angles increase in + * a clockwise direction. * * (To convert from degrees to radians, use degrees * (M_PI / * 180.).) @@ -1865,6 +1833,8 @@ slim_hidden_def (cairo_curve_to); * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI); * cairo_restore (cr); * + * + * Since: 1.0 **/ void cairo_arc (cairo_t *cr, @@ -1872,24 +1842,23 @@ cairo_arc (cairo_t *cr, double radius, double angle1, double angle2) { + cairo_status_t status; + if (unlikely (cr->status)) return; - /* Do nothing, successfully, if radius is <= 0 */ - if (radius <= 0.0) { - cairo_line_to (cr, xc, yc); - return; + if (angle2 < angle1) { + /* increase angle2 by multiples of full circle until it + * satisfies angle2 >= angle1 */ + angle2 = fmod (angle2 - angle1, 2 * M_PI); + if (angle2 < 0) + angle2 += 2 * M_PI; + angle2 += angle1; } - while (angle2 < angle1) - angle2 += 2 * M_PI; - - cairo_line_to (cr, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - - _cairo_arc_path (cr, xc, yc, radius, - angle1, angle2); + status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, TRUE); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -1904,11 +1873,13 @@ cairo_arc (cairo_t *cr, * Adds a circular arc of the given @radius to the current path. The * arc is centered at (@xc, @yc), begins at @angle1 and proceeds in * the direction of decreasing angles to end at @angle2. If @angle2 is - * greater than @angle1 it will be progressively decreased by 2*M_PI - * until it is less than @angle1. + * greater than @angle1 it will be progressively decreased by + * 2*M_PI until it is less than @angle1. * * See cairo_arc() for more details. This function differs only in the * direction of the arc between the two angles. + * + * Since: 1.0 **/ void cairo_arc_negative (cairo_t *cr, @@ -1916,22 +1887,23 @@ cairo_arc_negative (cairo_t *cr, double radius, double angle1, double angle2) { + cairo_status_t status; + if (unlikely (cr->status)) return; - /* Do nothing, successfully, if radius is <= 0 */ - if (radius <= 0.0) - return; + if (angle2 > angle1) { + /* decrease angle2 by multiples of full circle until it + * satisfies angle2 <= angle1 */ + angle2 = fmod (angle2 - angle1, 2 * M_PI); + if (angle2 > 0) + angle2 -= 2 * M_PI; + angle2 += angle1; + } - while (angle2 > angle1) - angle2 -= 2 * M_PI; - - cairo_line_to (cr, - xc + radius * cos (angle1), - yc + radius * sin (angle1)); - - _cairo_arc_path_negative (cr, xc, yc, radius, - angle1, angle2); + status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, FALSE); + if (unlikely (status)) + _cairo_set_error (cr, status); } /* XXX: NYI @@ -1946,10 +1918,23 @@ cairo_arc_to (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_arc_to (cr->gstate, - x1, y1, - x2, y2, - radius); + status = cr->backend->arc_to (cr, x1, y1, x2, y2, radius); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +void +cairo_rel_arc_to (cairo_t *cr, + double dx1, double dy1, + double dx2, double dy2, + double radius) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->rel_arc_to (cr, dx1, dy1, dx2, dy2, radius); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -1970,22 +1955,18 @@ cairo_arc_to (cairo_t *cr, * It is an error to call this function with no current point. Doing * so will cause @cr to shutdown with a status of * %CAIRO_STATUS_NO_CURRENT_POINT. + * + * Since: 1.0 **/ void cairo_rel_move_to (cairo_t *cr, double dx, double dy) { - cairo_fixed_t dx_fixed, dy_fixed; cairo_status_t status; if (unlikely (cr->status)) return; - _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); - - dx_fixed = _cairo_fixed_from_double (dx); - dy_fixed = _cairo_fixed_from_double (dy); - - status = _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed); + status = cr->backend->rel_move_to (cr, dx, dy); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2007,22 +1988,18 @@ cairo_rel_move_to (cairo_t *cr, double dx, double dy) * It is an error to call this function with no current point. Doing * so will cause @cr to shutdown with a status of * %CAIRO_STATUS_NO_CURRENT_POINT. + * + * Since: 1.0 **/ void cairo_rel_line_to (cairo_t *cr, double dx, double dy) { - cairo_fixed_t dx_fixed, dy_fixed; cairo_status_t status; if (unlikely (cr->status)) return; - _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy); - - dx_fixed = _cairo_fixed_from_double (dx); - dy_fixed = _cairo_fixed_from_double (dy); - - status = _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed); + status = cr->backend->rel_line_to (cr, dx, dy); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2052,6 +2029,8 @@ slim_hidden_def(cairo_rel_line_to); * It is an error to call this function with no current point. Doing * so will cause @cr to shutdown with a status of * %CAIRO_STATUS_NO_CURRENT_POINT. + * + * Since: 1.0 **/ void cairo_rel_curve_to (cairo_t *cr, @@ -2059,31 +2038,15 @@ cairo_rel_curve_to (cairo_t *cr, double dx2, double dy2, double dx3, double dy3) { - cairo_fixed_t dx1_fixed, dy1_fixed; - cairo_fixed_t dx2_fixed, dy2_fixed; - cairo_fixed_t dx3_fixed, dy3_fixed; cairo_status_t status; if (unlikely (cr->status)) return; - _cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1); - _cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2); - _cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3); - - dx1_fixed = _cairo_fixed_from_double (dx1); - dy1_fixed = _cairo_fixed_from_double (dy1); - - dx2_fixed = _cairo_fixed_from_double (dx2); - dy2_fixed = _cairo_fixed_from_double (dy2); - - dx3_fixed = _cairo_fixed_from_double (dx3); - dy3_fixed = _cairo_fixed_from_double (dy3); - - status = _cairo_path_fixed_rel_curve_to (cr->path, - dx1_fixed, dy1_fixed, - dx2_fixed, dy2_fixed, - dx3_fixed, dy3_fixed); + status = cr->backend->rel_curve_to (cr, + dx1, dy1, + dx2, dy2, + dx3, dy3); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2107,20 +2070,22 @@ cairo_rel_curve_to (cairo_t *cr, * cairo_rel_line_to (cr, -width, 0); * cairo_close_path (cr); * + * + * Since: 1.0 **/ void cairo_rectangle (cairo_t *cr, double x, double y, double width, double height) { + cairo_status_t status; + if (unlikely (cr->status)) return; - cairo_move_to (cr, x, y); - cairo_rel_line_to (cr, width, 0); - cairo_rel_line_to (cr, 0, height); - cairo_rel_line_to (cr, -width, 0); - cairo_close_path (cr); + status = cr->backend->rectangle (cr, x, y, width, height); + if (unlikely (status)) + _cairo_set_error (cr, status); } #if 0 @@ -2166,6 +2131,8 @@ cairo_stroke_to_path (cairo_t *cr) * not be necessary to save the "last move_to point" during processing * as the MOVE_TO immediately after the CLOSE_PATH will provide that * point. + * + * Since: 1.0 **/ void cairo_close_path (cairo_t *cr) @@ -2175,7 +2142,7 @@ cairo_close_path (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_path_fixed_close_path (cr->path); + status = cr->backend->close_path (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2229,9 +2196,7 @@ cairo_path_extents (cairo_t *cr, return; } - _cairo_gstate_path_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); + cr->backend->path_extents (cr, x1, y1, x2, y2); } /** @@ -2240,6 +2205,8 @@ cairo_path_extents (cairo_t *cr, * * A drawing operator that paints the current source everywhere within * the current clip region. + * + * Since: 1.0 **/ void cairo_paint (cairo_t *cr) @@ -2249,7 +2216,7 @@ cairo_paint (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_paint (cr->gstate); + status = cr->backend->paint (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2264,36 +2231,21 @@ slim_hidden_def (cairo_paint); * the current clip region using a mask of constant alpha value * @alpha. The effect is similar to cairo_paint(), but the drawing * is faded out using the alpha value. + * + * Since: 1.0 **/ void cairo_paint_with_alpha (cairo_t *cr, double alpha) { cairo_status_t status; - cairo_color_t color; - cairo_solid_pattern_t pattern; if (unlikely (cr->status)) return; - if (CAIRO_ALPHA_IS_OPAQUE (alpha)) { - cairo_paint (cr); - return; - } - - if (CAIRO_ALPHA_IS_ZERO (alpha) && - _cairo_operator_bounded_by_mask (cr->gstate->op)) { - return; - } - - _cairo_color_init_rgba (&color, 0., 0., 0., alpha); - _cairo_pattern_init_solid (&pattern, &color); - - status = _cairo_gstate_mask (cr->gstate, &pattern.base); + status = cr->backend->paint_with_alpha (cr, alpha); if (unlikely (status)) _cairo_set_error (cr, status); - - _cairo_pattern_fini (&pattern.base); } /** @@ -2305,7 +2257,9 @@ cairo_paint_with_alpha (cairo_t *cr, * using the alpha channel of @pattern as a mask. (Opaque * areas of @pattern are painted with the source, transparent * areas are not painted.) - */ + * + * Since: 1.0 + **/ void cairo_mask (cairo_t *cr, cairo_pattern_t *pattern) @@ -2315,17 +2269,17 @@ cairo_mask (cairo_t *cr, if (unlikely (cr->status)) return; - if (pattern == NULL) { + if (unlikely (pattern == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } - if (pattern->status) { + if (unlikely (pattern->status)) { _cairo_set_error (cr, pattern->status); return; } - status = _cairo_gstate_mask (cr->gstate, pattern); + status = cr->backend->mask (cr, pattern); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2342,7 +2296,9 @@ slim_hidden_def (cairo_mask); * using the alpha channel of @surface as a mask. (Opaque * areas of @surface are painted with the source, transparent * areas are not painted.) - */ + * + * Since: 1.0 + **/ void cairo_mask_surface (cairo_t *cr, cairo_surface_t *surface, @@ -2396,13 +2352,20 @@ cairo_mask_surface (cairo_t *cr, * * In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything * to be drawn in the case of either degenerate segments or sub-paths. + * + * Since: 1.0 **/ void cairo_stroke (cairo_t *cr) { - cairo_stroke_preserve (cr); + cairo_status_t status; - cairo_new_path (cr); + if (unlikely (cr->status)) + return; + + status = cr->backend->stroke (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); } slim_hidden_def(cairo_stroke); @@ -2418,6 +2381,8 @@ slim_hidden_def(cairo_stroke); * See cairo_set_line_width(), cairo_set_line_join(), * cairo_set_line_cap(), cairo_set_dash(), and * cairo_stroke_preserve(). + * + * Since: 1.0 **/ void cairo_stroke_preserve (cairo_t *cr) @@ -2427,7 +2392,7 @@ cairo_stroke_preserve (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_stroke (cr->gstate, cr->path); + status = cr->backend->stroke_preserve (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2442,13 +2407,20 @@ slim_hidden_def(cairo_stroke_preserve); * filled). After cairo_fill(), the current path will be cleared from * the cairo context. See cairo_set_fill_rule() and * cairo_fill_preserve(). + * + * Since: 1.0 **/ void cairo_fill (cairo_t *cr) { - cairo_fill_preserve (cr); + cairo_status_t status; - cairo_new_path (cr); + if (unlikely (cr->status)) + return; + + status = cr->backend->fill (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -2461,6 +2433,8 @@ cairo_fill (cairo_t *cr) * path within the cairo context. * * See cairo_set_fill_rule() and cairo_fill(). + * + * Since: 1.0 **/ void cairo_fill_preserve (cairo_t *cr) @@ -2470,7 +2444,7 @@ cairo_fill_preserve (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_fill (cr->gstate, cr->path); + status = cr->backend->fill_preserve (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2487,6 +2461,8 @@ slim_hidden_def(cairo_fill_preserve); * * This is a convenience function that simply calls * cairo_surface_copy_page() on @cr's target. + * + * Since: 1.0 **/ void cairo_copy_page (cairo_t *cr) @@ -2496,7 +2472,7 @@ cairo_copy_page (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_copy_page (cr->gstate); + status = cr->backend->copy_page (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2510,6 +2486,8 @@ cairo_copy_page (cairo_t *cr) * * This is a convenience function that simply calls * cairo_surface_show_page() on @cr's target. + * + * Since: 1.0 **/ void cairo_show_page (cairo_t *cr) @@ -2519,7 +2497,7 @@ cairo_show_page (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_show_page (cr->gstate); + status = cr->backend->show_page (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2541,6 +2519,8 @@ cairo_show_page (cairo_t *cr) * * Return value: A non-zero value if the point is inside, or zero if * outside. + * + * Since: 1.0 **/ cairo_bool_t cairo_in_stroke (cairo_t *cr, double x, double y) @@ -2551,9 +2531,7 @@ cairo_in_stroke (cairo_t *cr, double x, double y) if (unlikely (cr->status)) return FALSE; - status = _cairo_gstate_in_stroke (cr->gstate, - cr->path, - x, y, &inside); + status = cr->backend->in_stroke (cr, x, y, &inside); if (unlikely (status)) _cairo_set_error (cr, status); @@ -2575,14 +2553,23 @@ cairo_in_stroke (cairo_t *cr, double x, double y) * * Return value: A non-zero value if the point is inside, or zero if * outside. + * + * Since: 1.0 **/ cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y) { + cairo_status_t status; + cairo_bool_t inside = FALSE; + if (unlikely (cr->status)) return FALSE; - return _cairo_gstate_in_fill (cr->gstate, cr->path, x, y); + status = cr->backend->in_fill (cr, x, y, &inside); + if (unlikely (status)) + _cairo_set_error (cr, status); + + return inside; } /** @@ -2612,6 +2599,8 @@ cairo_in_fill (cairo_t *cr, double x, double y) * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), * cairo_set_line_cap(), cairo_set_dash(), and * cairo_stroke_preserve(). + * + * Since: 1.0 **/ void cairo_stroke_extents (cairo_t *cr, @@ -2632,9 +2621,7 @@ cairo_stroke_extents (cairo_t *cr, return; } - status = _cairo_gstate_stroke_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); + status = cr->backend->stroke_extents (cr, x1, y1, x2, y2); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2663,6 +2650,8 @@ cairo_stroke_extents (cairo_t *cr, * if the non-inked path extents are desired. * * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). + * + * Since: 1.0 **/ void cairo_fill_extents (cairo_t *cr, @@ -2683,9 +2672,7 @@ cairo_fill_extents (cairo_t *cr, return; } - status = _cairo_gstate_fill_extents (cr->gstate, - cr->path, - x1, y1, x2, y2); + status = cr->backend->fill_extents (cr, x1, y1, x2, y2); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2711,13 +2698,20 @@ cairo_fill_extents (cairo_t *cr, * calling cairo_clip() within a cairo_save()/cairo_restore() * pair. The only other means of increasing the size of the clip * region is cairo_reset_clip(). + * + * Since: 1.0 **/ void cairo_clip (cairo_t *cr) { - cairo_clip_preserve (cr); + cairo_status_t status; - cairo_new_path (cr); + if (unlikely (cr->status)) + return; + + status = cr->backend->clip (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -2741,6 +2735,8 @@ cairo_clip (cairo_t *cr) * calling cairo_clip_preserve() within a cairo_save()/cairo_restore() * pair. The only other means of increasing the size of the clip * region is cairo_reset_clip(). + * + * Since: 1.0 **/ void cairo_clip_preserve (cairo_t *cr) @@ -2750,7 +2746,7 @@ cairo_clip_preserve (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_clip (cr->gstate, cr->path); + status = cr->backend->clip_preserve (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2771,6 +2767,8 @@ slim_hidden_def(cairo_clip_preserve); * higher-level code which calls cairo_clip(). Consider using * cairo_save() and cairo_restore() around cairo_clip() as a more * robust means of temporarily restricting the clip region. + * + * Since: 1.0 **/ void cairo_reset_clip (cairo_t *cr) @@ -2780,7 +2778,7 @@ cairo_reset_clip (cairo_t *cr) if (unlikely (cr->status)) return; - status = _cairo_gstate_reset_clip (cr->gstate); + status = cr->backend->reset_clip (cr); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2803,25 +2801,23 @@ cairo_clip_extents (cairo_t *cr, double *x1, double *y1, double *x2, double *y2) { - if (unlikely (cr->status)) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; + cairo_status_t status; + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + if (unlikely (cr->status)) return; - } - if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) { - *x1 = -INFINITY; - *y1 = -INFINITY; - *x2 = +INFINITY; - *y2 = +INFINITY; - } + status = cr->backend->clip_extents (cr, x1, y1, x2, y2); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -2844,30 +2840,17 @@ cairo_clip_extents (cairo_t *cr, cairo_bool_t cairo_in_clip (cairo_t *cr, double x, double y) { + cairo_status_t status; + cairo_bool_t inside = FALSE; + if (unlikely (cr->status)) return FALSE; - return _cairo_gstate_in_clip (cr->gstate, x, y); -} + status = cr->backend->in_clip (cr, x, y, &inside); + if (unlikely (status)) + _cairo_set_error (cr, status); -static cairo_rectangle_list_t * -_cairo_rectangle_list_create_in_error (cairo_status_t status) -{ - cairo_rectangle_list_t *list; - - if (status == CAIRO_STATUS_NO_MEMORY) - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; - - list = malloc (sizeof (cairo_rectangle_list_t)); - if (unlikely (list == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; - } - - list->status = status; - list->rectangles = NULL; - list->num_rectangles = 0; - return list; + return inside; } /** @@ -2893,7 +2876,101 @@ cairo_copy_clip_rectangle_list (cairo_t *cr) if (unlikely (cr->status)) return _cairo_rectangle_list_create_in_error (cr->status); - return _cairo_gstate_copy_clip_rectangle_list (cr->gstate); + return cr->backend->clip_copy_rectangle_list (cr); +} + +/** + * CAIRO_TAG_DEST: + * + * Create a destination for a hyperlink. Destination tag attributes + * are detailed at [Destinations][dests]. + * + * Since: 1.16 + **/ + +/** + * CAIRO_TAG_LINK: + * + * Create hyperlink. Link tag attributes are detailed at + * [Links][links]. + * + * Since: 1.16 + **/ + +/** + * cairo_tag_begin: + * @cr: a cairo context + * @tag_name: tag name + * @attributes: tag attributes + * + * Marks the beginning of the @tag_name structure. Call + * cairo_tag_end() with the same @tag_name to mark the end of the + * structure. + * + * The attributes string is of the form "key1=value2 key2=value2 ...". + * Values may be boolean (true/false or 1/0), integer, float, string, + * or an array. + * + * String values are enclosed in single quotes + * ('). Single quotes and backslashes inside the string should be + * escaped with a backslash. + * + * Boolean values may be set to true by only + * specifying the key. eg the attribute string "key" is the equivalent + * to "key=true". + * + * Arrays are enclosed in '[]'. eg "rect=[1.2 4.3 2.0 3.0]". + * + * If no attributes are required, @attributes can be an empty string or NULL. + * + * See [Tags and Links Description][cairo-Tags-and-Links.description] + * for the list of tags and attributes. + * + * Invalid nesting of tags or invalid attributes will cause @cr to + * shutdown with a status of %CAIRO_STATUS_TAG_ERROR. + * + * See cairo_tag_end(). + * + * Since: 1.16 + **/ +void +cairo_tag_begin (cairo_t *cr, const char *tag_name, const char *attributes) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->tag_begin (cr, tag_name, attributes); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_tag_end: + * @cr: a cairo context + * @tag_name: tag name + * + * Marks the end of the @tag_name structure. + * + * Invalid nesting of tags will cause @cr to shutdown with a status of + * %CAIRO_STATUS_TAG_ERROR. + * + * See cairo_tag_begin(). + * + * Since: 1.16 + **/ +cairo_public void +cairo_tag_end (cairo_t *cr, const char *tag_name) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->tag_end (cr, tag_name); + if (unlikely (status)) + _cairo_set_error (cr, status); } /** @@ -2946,6 +3023,8 @@ cairo_copy_clip_rectangle_list (cairo_t *cr) * * This function is equivalent to a call to cairo_toy_font_face_create() * followed by cairo_set_font_face(). + * + * Since: 1.0 **/ void cairo_select_font_face (cairo_t *cr, @@ -2953,12 +3032,21 @@ cairo_select_font_face (cairo_t *cr, cairo_font_slant_t slant, cairo_font_weight_t weight) { + cairo_font_face_t *font_face; cairo_status_t status; if (unlikely (cr->status)) return; - status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight); + font_face = cairo_toy_font_face_create (family, slant, weight); + if (unlikely (font_face->status)) { + _cairo_set_error (cr, font_face->status); + return; + } + + status = cr->backend->set_font_face (cr, font_face); + cairo_font_face_destroy (font_face); + if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2970,6 +3058,8 @@ cairo_select_font_face (cairo_t *cr, * will be stored. * * Gets the font extents for the currently selected font. + * + * Since: 1.0 **/ void cairo_font_extents (cairo_t *cr, @@ -2986,7 +3076,7 @@ cairo_font_extents (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_get_font_extents (cr->gstate, extents); + status = cr->backend->font_extents (cr, extents); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -2999,6 +3089,8 @@ cairo_font_extents (cairo_t *cr, * Replaces the current #cairo_font_face_t object in the #cairo_t with * @font_face. The replaced font face in the #cairo_t will be * destroyed if there are no other references to it. + * + * Since: 1.0 **/ void cairo_set_font_face (cairo_t *cr, @@ -3009,7 +3101,7 @@ cairo_set_font_face (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_set_font_face (cr->gstate, font_face); + status = cr->backend->set_font_face (cr, font_face); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3031,23 +3123,16 @@ cairo_set_font_face (cairo_t *cr, * objects it is passed to, (for example, calling * cairo_set_font_face() with a nil font will trigger an error that * will shutdown the #cairo_t object). + * + * Since: 1.0 **/ cairo_font_face_t * cairo_get_font_face (cairo_t *cr) { - cairo_status_t status; - cairo_font_face_t *font_face; - if (unlikely (cr->status)) return (cairo_font_face_t*) &_cairo_font_face_nil; - status = _cairo_gstate_get_font_face (cr->gstate, &font_face); - if (unlikely (status)) { - _cairo_set_error (cr, status); - return (cairo_font_face_t*) &_cairo_font_face_nil; - } - - return font_face; + return cr->backend->get_font_face (cr); } /** @@ -3064,6 +3149,8 @@ cairo_get_font_face (cairo_t *cr) * If text is drawn without a call to cairo_set_font_size(), (nor * cairo_set_font_matrix() nor cairo_set_scaled_font()), the default * font size is 10.0. + * + * Since: 1.0 **/ void cairo_set_font_size (cairo_t *cr, double size) @@ -3073,14 +3160,14 @@ cairo_set_font_size (cairo_t *cr, double size) if (unlikely (cr->status)) return; - status = _cairo_gstate_set_font_size (cr->gstate, size); + status = cr->backend->set_font_size (cr, size); if (unlikely (status)) _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_font_size); /** - * cairo_set_font_matrix + * cairo_set_font_matrix: * @cr: a #cairo_t * @matrix: a #cairo_matrix_t describing a transform to be applied to * the current font. @@ -3091,6 +3178,8 @@ slim_hidden_def (cairo_set_font_size); * simple scale is used (see cairo_set_font_size()), but a more * complex font matrix can be used to shear the font * or stretch it unequally along the two axes + * + * Since: 1.0 **/ void cairo_set_font_matrix (cairo_t *cr, @@ -3101,18 +3190,21 @@ cairo_set_font_matrix (cairo_t *cr, if (unlikely (cr->status)) return; - status = _cairo_gstate_set_font_matrix (cr->gstate, matrix); + status = cr->backend->set_font_matrix (cr, matrix); if (unlikely (status)) _cairo_set_error (cr, status); } +slim_hidden_def (cairo_set_font_matrix); /** - * cairo_get_font_matrix + * cairo_get_font_matrix: * @cr: a #cairo_t * @matrix: return value for the matrix * * Stores the current font matrix into @matrix. See * cairo_set_font_matrix(). + * + * Since: 1.0 **/ void cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) @@ -3122,7 +3214,7 @@ cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) return; } - _cairo_gstate_get_font_matrix (cr->gstate, matrix); + cr->backend->get_font_matrix (cr, matrix); } /** @@ -3135,6 +3227,8 @@ cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix) * options derived from underlying surface; if the value in @options * has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value * from the surface is used. + * + * Since: 1.0 **/ void cairo_set_font_options (cairo_t *cr, @@ -3151,7 +3245,9 @@ cairo_set_font_options (cairo_t *cr, return; } - _cairo_gstate_set_font_options (cr->gstate, options); + status = cr->backend->set_font_options (cr, options); + if (unlikely (status)) + _cairo_set_error (cr, status); } slim_hidden_def (cairo_set_font_options); @@ -3165,6 +3261,8 @@ slim_hidden_def (cairo_set_font_options); * Note that the returned options do not include any options derived * from the underlying surface; they are literally the options * passed to cairo_set_font_options(). + * + * Since: 1.0 **/ void cairo_get_font_options (cairo_t *cr, @@ -3179,7 +3277,7 @@ cairo_get_font_options (cairo_t *cr, return; } - _cairo_gstate_get_font_options (cr->gstate, options); + cr->backend->get_font_options (cr, options); } /** @@ -3200,46 +3298,24 @@ cairo_set_scaled_font (cairo_t *cr, const cairo_scaled_font_t *scaled_font) { cairo_status_t status; - cairo_bool_t was_previous; if (unlikely (cr->status)) return; - if (scaled_font == NULL) { - status = _cairo_error (CAIRO_STATUS_NULL_POINTER); - goto BAIL; + if ((scaled_font == NULL)) { + _cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER)); + return; } status = scaled_font->status; - if (unlikely (status)) - goto BAIL; - - if (scaled_font == cr->gstate->scaled_font) + if (unlikely (status)) { + _cairo_set_error (cr, status); return; + } - was_previous = scaled_font == cr->gstate->previous_scaled_font; - - status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face); + status = cr->backend->set_scaled_font (cr, (cairo_scaled_font_t *) scaled_font); if (unlikely (status)) - goto BAIL; - - status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix); - if (unlikely (status)) - goto BAIL; - - _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options); - - /* XXX: Mozilla code assumes that the ctm of a scaled font doesn't need to - * match the context ctm. This assumption breaks the previous_scaled_font - * cache. So we avoid using the cache for now. - if (was_previous) - cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font); - */ - - return; - -BAIL: - _cairo_set_error (cr, status); + _cairo_set_error (cr, status); } /** @@ -3265,20 +3341,12 @@ BAIL: cairo_scaled_font_t * cairo_get_scaled_font (cairo_t *cr) { - cairo_status_t status; - cairo_scaled_font_t *scaled_font; - if (unlikely (cr->status)) return _cairo_scaled_font_create_in_error (cr->status); - status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font); - if (unlikely (status)) { - _cairo_set_error (cr, status); - return _cairo_scaled_font_create_in_error (status); - } - - return scaled_font; + return cr->backend->get_scaled_font (cr); } +slim_hidden_def (cairo_get_scaled_font); /** * cairo_text_extents: @@ -3299,6 +3367,8 @@ cairo_get_scaled_font (cairo_t *cr) * characters. In particular, trailing whitespace characters are * likely to not affect the size of the rectangle, though they will * affect the x_advance and y_advance values. + * + * Since: 1.0 **/ void cairo_text_extents (cairo_t *cr, @@ -3306,8 +3376,9 @@ cairo_text_extents (cairo_t *cr, cairo_text_extents_t *extents) { cairo_status_t status; + cairo_scaled_font_t *scaled_font; cairo_glyph_t *glyphs = NULL; - int num_glyphs; + int num_glyphs = 0; double x, y; extents->x_bearing = 0.0; @@ -3323,19 +3394,24 @@ cairo_text_extents (cairo_t *cr, if (utf8 == NULL) return; + scaled_font = cairo_get_scaled_font (cr); + if (unlikely (scaled_font->status)) { + _cairo_set_error (cr, scaled_font->status); + return; + } + cairo_get_current_point (cr, &x, &y); + status = cairo_scaled_font_text_to_glyphs (scaled_font, + x, y, + utf8, -1, + &glyphs, &num_glyphs, + NULL, NULL, NULL); - status = _cairo_gstate_text_to_glyphs (cr->gstate, - x, y, - utf8, strlen (utf8), - &glyphs, &num_glyphs, - NULL, NULL, - NULL); - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_gstate_glyph_extents (cr->gstate, - glyphs, num_glyphs, - extents); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = cr->backend->glyph_extents (cr, + glyphs, num_glyphs, + extents); + } cairo_glyph_free (glyphs); if (unlikely (status)) @@ -3359,6 +3435,8 @@ cairo_text_extents (cairo_t *cr, * * Note that whitespace glyphs do not contribute to the size of the * rectangle (extents.width and extents.height). + * + * Since: 1.0 **/ void cairo_glyph_extents (cairo_t *cr, @@ -3381,18 +3459,17 @@ cairo_glyph_extents (cairo_t *cr, if (num_glyphs == 0) return; - if (num_glyphs < 0) { + if (unlikely (num_glyphs < 0)) { _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); return; } - if (glyphs == NULL) { + if (unlikely (glyphs == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } - status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, - extents); + status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3423,6 +3500,8 @@ cairo_glyph_extents (cairo_t *cr, * and simple programs, but it is not expected to be adequate for * serious text-using applications. See cairo_show_glyphs() for the * "real" text display API in cairo. + * + * Since: 1.0 **/ void cairo_show_text (cairo_t *cr, const char *utf8) @@ -3437,6 +3516,8 @@ cairo_show_text (cairo_t *cr, const char *utf8) cairo_bool_t has_show_text_glyphs; cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; + cairo_scaled_font_t *scaled_font; + cairo_glyph_text_info_t info, *i; if (unlikely (cr->status)) return; @@ -3444,7 +3525,11 @@ cairo_show_text (cairo_t *cr, const char *utf8) if (utf8 == NULL) return; - cairo_get_current_point (cr, &x, &y); + scaled_font = cairo_get_scaled_font (cr); + if (unlikely (scaled_font->status)) { + _cairo_set_error (cr, scaled_font->status); + return; + } utf8_len = strlen (utf8); @@ -3462,36 +3547,41 @@ cairo_show_text (cairo_t *cr, const char *utf8) num_clusters = 0; } - status = _cairo_gstate_text_to_glyphs (cr->gstate, - x, y, - utf8, utf8_len, - &glyphs, &num_glyphs, - has_show_text_glyphs ? &clusters : NULL, &num_clusters, - &cluster_flags); + cairo_get_current_point (cr, &x, &y); + status = cairo_scaled_font_text_to_glyphs (scaled_font, + x, y, + utf8, utf8_len, + &glyphs, &num_glyphs, + has_show_text_glyphs ? &clusters : NULL, &num_clusters, + &cluster_flags); if (unlikely (status)) goto BAIL; if (num_glyphs == 0) return; - status = _cairo_gstate_show_text_glyphs (cr->gstate, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags); + i = NULL; + if (has_show_text_glyphs) { + info.utf8 = utf8; + info.utf8_len = utf8_len; + info.clusters = clusters; + info.num_clusters = num_clusters; + info.cluster_flags = cluster_flags; + i = &info; + } + + status = cr->backend->glyphs (cr, glyphs, num_glyphs, i); if (unlikely (status)) goto BAIL; last_glyph = &glyphs[num_glyphs - 1]; - status = _cairo_gstate_glyph_extents (cr->gstate, - last_glyph, 1, - &extents); + status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents); if (unlikely (status)) goto BAIL; x = last_glyph->x + extents.x_advance; y = last_glyph->y + extents.y_advance; - cairo_move_to (cr, x, y); + cr->backend->move_to (cr, x, y); BAIL: if (glyphs != stack_glyphs) @@ -3512,6 +3602,8 @@ cairo_show_text (cairo_t *cr, const char *utf8) * A drawing operator that generates the shape from an array of glyphs, * rendered according to the current font face, font size * (font matrix), and font options. + * + * Since: 1.0 **/ void cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) @@ -3534,11 +3626,7 @@ cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) return; } - status = _cairo_gstate_show_text_glyphs (cr->gstate, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, - FALSE); + status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3616,32 +3704,38 @@ cairo_show_text_glyphs (cairo_t *cr, return; } - /* Make sure clusters cover the entire glyphs and utf8 arrays, - * and that cluster boundaries are UTF-8 boundaries. */ - status = _cairo_validate_text_clusters (utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags); - if (status == CAIRO_STATUS_INVALID_CLUSTERS) { - /* Either got invalid UTF-8 text, or cluster mapping is bad. - * Differentiate those. */ - - cairo_status_t status2; - - status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL); - if (status2) - status = status2; - - _cairo_set_error (cr, status); - return; - } - if (num_glyphs == 0 && utf8_len == 0) return; - status = _cairo_gstate_show_text_glyphs (cr->gstate, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags); + if (utf8) { + /* Make sure clusters cover the entire glyphs and utf8 arrays, + * and that cluster boundaries are UTF-8 boundaries. */ + status = _cairo_validate_text_clusters (utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags); + if (status == CAIRO_STATUS_INVALID_CLUSTERS) { + /* Either got invalid UTF-8 text, or cluster mapping is bad. + * Differentiate those. */ + + cairo_status_t status2; + + status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL); + if (status2) + status = status2; + } else { + cairo_glyph_text_info_t info; + + info.utf8 = utf8; + info.utf8_len = utf8_len; + info.clusters = clusters; + info.num_clusters = num_clusters; + info.cluster_flags = cluster_flags; + + status = cr->backend->glyphs (cr, glyphs, num_glyphs, &info); + } + } else { + status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL); + } if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3669,14 +3763,17 @@ cairo_show_text_glyphs (cairo_t *cr, * and simple programs, but it is not expected to be adequate for * serious text-using applications. See cairo_glyph_path() for the * "real" text path API in cairo. + * + * Since: 1.0 **/ void -cairo_text_path (cairo_t *cr, const char *utf8) +cairo_text_path (cairo_t *cr, const char *utf8) { cairo_status_t status; cairo_text_extents_t extents; cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; cairo_glyph_t *glyphs, *last_glyph; + cairo_scaled_font_t *scaled_font; int num_glyphs; double x, y; @@ -3686,42 +3783,40 @@ cairo_text_path (cairo_t *cr, const char *utf8) if (utf8 == NULL) return; - cairo_get_current_point (cr, &x, &y); glyphs = stack_glyphs; num_glyphs = ARRAY_LENGTH (stack_glyphs); - status = _cairo_gstate_text_to_glyphs (cr->gstate, - x, y, - utf8, strlen (utf8), - &glyphs, &num_glyphs, - NULL, NULL, - NULL); + scaled_font = cairo_get_scaled_font (cr); + if (unlikely (scaled_font->status)) { + _cairo_set_error (cr, scaled_font->status); + return; + } - if (unlikely (status)) - goto BAIL; + cairo_get_current_point (cr, &x, &y); + status = cairo_scaled_font_text_to_glyphs (scaled_font, + x, y, + utf8, -1, + &glyphs, &num_glyphs, + NULL, NULL, NULL); if (num_glyphs == 0) return; - status = _cairo_gstate_glyph_path (cr->gstate, - glyphs, num_glyphs, - cr->path); + status = cr->backend->glyph_path (cr, glyphs, num_glyphs); if (unlikely (status)) goto BAIL; last_glyph = &glyphs[num_glyphs - 1]; - status = _cairo_gstate_glyph_extents (cr->gstate, - last_glyph, 1, - &extents); + status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents); if (unlikely (status)) goto BAIL; x = last_glyph->x + extents.x_advance; y = last_glyph->y + extents.y_advance; - cairo_move_to (cr, x, y); + cr->backend->move_to (cr, x, y); BAIL: if (glyphs != stack_glyphs) @@ -3740,6 +3835,8 @@ cairo_text_path (cairo_t *cr, const char *utf8) * Adds closed paths for the glyphs to the current path. The generated * path if filled, achieves an effect similar to that of * cairo_show_glyphs(). + * + * Since: 1.0 **/ void cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) @@ -3752,19 +3849,17 @@ cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) if (num_glyphs == 0) return; - if (num_glyphs < 0) { + if (unlikely (num_glyphs < 0)) { _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); return; } - if (glyphs == NULL) { + if (unlikely (glyphs == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } - status = _cairo_gstate_glyph_path (cr->gstate, - glyphs, num_glyphs, - cr->path); + status = cr->backend->glyph_path (cr, glyphs, num_glyphs); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -3776,6 +3871,8 @@ cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs) * Gets the current compositing operator for a cairo context. * * Return value: the current compositing operator. + * + * Since: 1.0 **/ cairo_operator_t cairo_get_operator (cairo_t *cr) @@ -3783,9 +3880,30 @@ cairo_get_operator (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_OPERATOR_DEFAULT; - return _cairo_gstate_get_operator (cr->gstate); + return cr->backend->get_operator (cr); } +#if 0 +/** + * cairo_get_opacity: + * @cr: a cairo context + * + * Gets the current compositing opacity for a cairo context. + * + * Return value: the current compositing opacity. + * + * Since: TBD + **/ +double +cairo_get_opacity (cairo_t *cr) +{ + if (unlikely (cr->status)) + return 1.; + + return cr->backend->get_opacity (cr); +} +#endif + /** * cairo_get_tolerance: * @cr: a cairo context @@ -3793,6 +3911,8 @@ cairo_get_operator (cairo_t *cr) * Gets the current tolerance value, as set by cairo_set_tolerance(). * * Return value: the current tolerance value. + * + * Since: 1.0 **/ double cairo_get_tolerance (cairo_t *cr) @@ -3800,7 +3920,7 @@ cairo_get_tolerance (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_TOLERANCE_DEFAULT; - return _cairo_gstate_get_tolerance (cr->gstate); + return cr->backend->get_tolerance (cr); } slim_hidden_def (cairo_get_tolerance); @@ -3808,9 +3928,12 @@ slim_hidden_def (cairo_get_tolerance); * cairo_get_antialias: * @cr: a cairo context * - * Gets the current shape antialiasing mode, as set by cairo_set_shape_antialias(). + * Gets the current shape antialiasing mode, as set by + * cairo_set_antialias(). * * Return value: the current shape antialiasing mode. + * + * Since: 1.0 **/ cairo_antialias_t cairo_get_antialias (cairo_t *cr) @@ -3818,7 +3941,7 @@ cairo_get_antialias (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_ANTIALIAS_DEFAULT; - return _cairo_gstate_get_antialias (cr->gstate); + return cr->backend->get_antialias (cr); } /** @@ -3836,9 +3959,9 @@ cairo_bool_t cairo_has_current_point (cairo_t *cr) { if (unlikely (cr->status)) - return FALSE; + return FALSE; - return cr->path->has_current_point; + return cr->backend->has_current_point (cr); } /** @@ -3870,24 +3993,19 @@ cairo_has_current_point (cairo_t *cr) * * Some functions unset the current path and as a result, current point: * cairo_fill(), cairo_stroke(). + * + * Since: 1.0 **/ void cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret) { - cairo_fixed_t x_fixed, y_fixed; double x, y; + x = y = 0; if (cr->status == CAIRO_STATUS_SUCCESS && - _cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed)) + cr->backend->has_current_point (cr)) { - x = _cairo_fixed_to_double (x_fixed); - y = _cairo_fixed_to_double (y_fixed); - _cairo_gstate_backend_to_user (cr->gstate, &x, &y); - } - else - { - x = 0.0; - y = 0.0; + cr->backend->get_current_point (cr, &x, &y); } if (x_ret) @@ -3904,6 +4022,8 @@ slim_hidden_def(cairo_get_current_point); * Gets the current fill rule, as set by cairo_set_fill_rule(). * * Return value: the current fill rule. + * + * Since: 1.0 **/ cairo_fill_rule_t cairo_get_fill_rule (cairo_t *cr) @@ -3911,7 +4031,7 @@ cairo_get_fill_rule (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_FILL_RULE_DEFAULT; - return _cairo_gstate_get_fill_rule (cr->gstate); + return cr->backend->get_fill_rule (cr); } /** @@ -3924,6 +4044,8 @@ cairo_get_fill_rule (cairo_t *cr) * cairo_get_line_width(). * * Return value: the current line width. + * + * Since: 1.0 **/ double cairo_get_line_width (cairo_t *cr) @@ -3931,7 +4053,7 @@ cairo_get_line_width (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_LINE_WIDTH_DEFAULT; - return _cairo_gstate_get_line_width (cr->gstate); + return cr->backend->get_line_width (cr); } slim_hidden_def (cairo_get_line_width); @@ -3942,6 +4064,8 @@ slim_hidden_def (cairo_get_line_width); * Gets the current line cap style, as set by cairo_set_line_cap(). * * Return value: the current line cap style. + * + * Since: 1.0 **/ cairo_line_cap_t cairo_get_line_cap (cairo_t *cr) @@ -3949,7 +4073,7 @@ cairo_get_line_cap (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_LINE_CAP_DEFAULT; - return _cairo_gstate_get_line_cap (cr->gstate); + return cr->backend->get_line_cap (cr); } /** @@ -3959,6 +4083,8 @@ cairo_get_line_cap (cairo_t *cr) * Gets the current line join style, as set by cairo_set_line_join(). * * Return value: the current line join style. + * + * Since: 1.0 **/ cairo_line_join_t cairo_get_line_join (cairo_t *cr) @@ -3966,7 +4092,7 @@ cairo_get_line_join (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_LINE_JOIN_DEFAULT; - return _cairo_gstate_get_line_join (cr->gstate); + return cr->backend->get_line_join (cr); } /** @@ -3976,6 +4102,8 @@ cairo_get_line_join (cairo_t *cr) * Gets the current miter limit, as set by cairo_set_miter_limit(). * * Return value: the current miter limit. + * + * Since: 1.0 **/ double cairo_get_miter_limit (cairo_t *cr) @@ -3983,7 +4111,7 @@ cairo_get_miter_limit (cairo_t *cr) if (unlikely (cr->status)) return CAIRO_GSTATE_MITER_LIMIT_DEFAULT; - return _cairo_gstate_get_miter_limit (cr->gstate); + return cr->backend->get_miter_limit (cr); } /** @@ -3992,6 +4120,8 @@ cairo_get_miter_limit (cairo_t *cr) * @matrix: return value for the matrix * * Stores the current transformation matrix (CTM) into @matrix. + * + * Since: 1.0 **/ void cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix) @@ -4001,7 +4131,7 @@ cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix) return; } - _cairo_gstate_get_matrix (cr->gstate, matrix); + cr->backend->get_matrix (cr, matrix); } slim_hidden_def (cairo_get_matrix); @@ -4020,6 +4150,8 @@ slim_hidden_def (cairo_get_matrix); * * Return value: the target surface. This object is owned by cairo. To * keep a reference to it, you must call cairo_surface_reference(). + * + * Since: 1.0 **/ cairo_surface_t * cairo_get_target (cairo_t *cr) @@ -4027,7 +4159,7 @@ cairo_get_target (cairo_t *cr) if (unlikely (cr->status)) return _cairo_surface_create_in_error (cr->status); - return _cairo_gstate_get_original_target (cr->gstate); + return cr->backend->get_original_target (cr); } slim_hidden_def (cairo_get_target); @@ -4057,7 +4189,7 @@ cairo_get_group_target (cairo_t *cr) if (unlikely (cr->status)) return _cairo_surface_create_in_error (cr->status); - return _cairo_gstate_get_target (cr->gstate); + return cr->backend->get_current_target (cr); } /** @@ -4085,6 +4217,8 @@ cairo_get_group_target (cairo_t *cr) * Return value: the copy of the current path. The caller owns the * returned object and should call cairo_path_destroy() when finished * with it. + * + * Since: 1.0 **/ cairo_path_t * cairo_copy_path (cairo_t *cr) @@ -4092,7 +4226,7 @@ cairo_copy_path (cairo_t *cr) if (unlikely (cr->status)) return _cairo_path_create_in_error (cr->status); - return _cairo_path_create (cr->path, cr->gstate); + return cr->backend->copy_path (cr); } /** @@ -4127,6 +4261,8 @@ cairo_copy_path (cairo_t *cr) * Return value: the copy of the current path. The caller owns the * returned object and should call cairo_path_destroy() when finished * with it. + * + * Since: 1.0 **/ cairo_path_t * cairo_copy_path_flat (cairo_t *cr) @@ -4134,7 +4270,7 @@ cairo_copy_path_flat (cairo_t *cr) if (unlikely (cr->status)) return _cairo_path_create_in_error (cr->status); - return _cairo_path_create_flat (cr->path, cr->gstate); + return cr->backend->copy_path_flat (cr); } /** @@ -4148,6 +4284,8 @@ cairo_copy_path_flat (cairo_t *cr) * #cairo_path_t for details on how the path data structure should be * initialized, and note that path->status must be * initialized to %CAIRO_STATUS_SUCCESS. + * + * Since: 1.0 **/ void cairo_append_path (cairo_t *cr, @@ -4158,12 +4296,12 @@ cairo_append_path (cairo_t *cr, if (unlikely (cr->status)) return; - if (path == NULL) { + if (unlikely (path == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } - if (path->status) { + if (unlikely (path->status)) { if (path->status > CAIRO_STATUS_SUCCESS && path->status <= CAIRO_STATUS_LAST_STATUS) _cairo_set_error (cr, path->status); @@ -4175,12 +4313,12 @@ cairo_append_path (cairo_t *cr, if (path->num_data == 0) return; - if (path->data == NULL) { + if (unlikely (path->data == NULL)) { _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); return; } - status = _cairo_path_append_to_context (path, cr); + status = cr->backend->append_path (cr, path); if (unlikely (status)) _cairo_set_error (cr, status); } @@ -4192,6 +4330,8 @@ cairo_append_path (cairo_t *cr, * Checks whether an error has previously occurred for this context. * * Returns: the current status of this context, see #cairo_status_t + * + * Since: 1.0 **/ cairo_status_t cairo_status (cairo_t *cr) diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h index b6ae5ef786dc..96427b425017 100644 --- a/gfx/cairo/cairo/src/cairo.h +++ b/gfx/cairo/cairo/src/cairo.h @@ -101,6 +101,8 @@ cairo_version_string (void); * /* do something */ * } * + * + * Since: 1.0 **/ typedef int cairo_bool_t; @@ -116,6 +118,8 @@ typedef int cairo_bool_t; * * Memory management of #cairo_t is done with * cairo_reference() and cairo_destroy(). + * + * Since: 1.0 **/ typedef struct _cairo cairo_t; @@ -143,6 +147,8 @@ typedef struct _cairo cairo_t; * * Memory management of #cairo_surface_t is done with * cairo_surface_reference() and cairo_surface_destroy(). + * + * Since: 1.0 **/ typedef struct _cairo_surface cairo_surface_t; @@ -152,8 +158,8 @@ 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_xcb_device_create() creates a device that wraps the connection - * to an X Windows System using the XCB library. + * cairo_egl_device_create() creates a device that wraps an EGL display and + * context. * * The type of a device can be queried with cairo_device_get_type(). * @@ -180,6 +186,8 @@ typedef struct _cairo_device cairo_device_t; * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * + * + * Since: 1.0 **/ typedef struct _cairo_matrix { double xx; double yx; @@ -196,15 +204,18 @@ typedef struct _cairo_matrix { * cairo_pattern_create_rgb() creates a pattern for a solid * opaque color. * - * Other than various cairo_pattern_create_type() - * functions, some of the pattern types can be implicitly created - * using various cairo_set_source_type() functions; + * Other than various + * cairo_pattern_create_type() + * functions, some of the pattern types can be implicitly created using various + * cairo_set_source_type() functions; * for example cairo_set_source_rgb(). * * The type of a pattern can be queried with cairo_pattern_get_type(). * * Memory management of #cairo_pattern_t is done with * cairo_pattern_reference() and cairo_pattern_destroy(). + * + * Since: 1.0 **/ typedef struct _cairo_pattern cairo_pattern_t; @@ -215,18 +226,11 @@ typedef struct _cairo_pattern cairo_pattern_t; * #cairo_destroy_func_t the type of function which is called when a * data element is destroyed. It is passed the pointer to the data * element and should free any memory and resources allocated for it. + * + * Since: 1.0 **/ typedef void (*cairo_destroy_func_t) (void *data); -/** - * cairo_surface_func_t: - * @surface: The surface being referred to. - * - * #cairo_surface_func_t the type of function which is used for callback - * when a surface needs to be apssed as a parameter. - */ -typedef void (*cairo_surface_func_t) (cairo_surface_t *surface); - /** * cairo_user_data_key_t: * @unused: not used; ignore. @@ -236,6 +240,8 @@ typedef void (*cairo_surface_func_t) (cairo_surface_t *surface); * and there is no need to initialize the object; only the unique * address of a #cairo_data_key_t object is used. Typically, you * would just use the address of a static #cairo_data_key_t object. + * + * Since: 1.0 **/ typedef struct _cairo_user_data_key { int unused; @@ -243,26 +249,26 @@ typedef struct _cairo_user_data_key { /** * cairo_status_t: - * @CAIRO_STATUS_SUCCESS: no error has occurred - * @CAIRO_STATUS_NO_MEMORY: out of memory - * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() - * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() - * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined - * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) - * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t - * @CAIRO_STATUS_NULL_POINTER: %NULL pointer - * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 - * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid - * @CAIRO_STATUS_READ_ERROR: error while reading from input stream - * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream - * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished - * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation - * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation - * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t - * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t - * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* - * @CAIRO_STATUS_FILE_NOT_FOUND: file not found - * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting + * @CAIRO_STATUS_SUCCESS: no error has occurred (Since 1.0) + * @CAIRO_STATUS_NO_MEMORY: out of memory (Since 1.0) + * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() (Since 1.0) + * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() (Since 1.0) + * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined (Since 1.0) + * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) (Since 1.0) + * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t (Since 1.0) + * @CAIRO_STATUS_NULL_POINTER: %NULL pointer (Since 1.0) + * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 (Since 1.0) + * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid (Since 1.0) + * @CAIRO_STATUS_READ_ERROR: error while reading from input stream (Since 1.0) + * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream (Since 1.0) + * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished (Since 1.0) + * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation (Since 1.0) + * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation (Since 1.0) + * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t (Since 1.0) + * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t (Since 1.0) + * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* (Since 1.0) + * @CAIRO_STATUS_FILE_NOT_FOUND: file not found (Since 1.0) + * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting (Since 1.0) * @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2) * @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4) * @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4) @@ -279,6 +285,17 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10) * @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10) * @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10) + * @CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: a mesh pattern + * construction operation was used outside of a + * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch() + * pair (Since 1.12) + * @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12) + * @CAIRO_STATUS_JBIG2_GLOBAL_MISSING: %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID has been used on at least one image + * but no image provided %CAIRO_MIME_TYPE_JBIG2_GLOBAL (Since 1.14) + * @CAIRO_STATUS_PNG_ERROR: error occurred in libpng while reading from or writing to a PNG file (Since 1.16) + * @CAIRO_STATUS_FREETYPE_ERROR: error occurred in libfreetype (Since 1.16) + * @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_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 @@ -291,6 +308,8 @@ typedef struct _cairo_user_data_key { * * New entries may be added in future versions. Use cairo_status_to_string() * to get a human-readable representation of an error message. + * + * Since: 1.0 **/ typedef enum _cairo_status { CAIRO_STATUS_SUCCESS = 0, @@ -330,16 +349,22 @@ typedef enum _cairo_status { CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, CAIRO_STATUS_DEVICE_TYPE_MISMATCH, CAIRO_STATUS_DEVICE_ERROR, - CAIRO_STATUS_NO_DEVICE, + CAIRO_STATUS_INVALID_MESH_CONSTRUCTION, + CAIRO_STATUS_DEVICE_FINISHED, + CAIRO_STATUS_JBIG2_GLOBAL_MISSING, + CAIRO_STATUS_PNG_ERROR, + CAIRO_STATUS_FREETYPE_ERROR, + CAIRO_STATUS_WIN32_GDI_ERROR, + CAIRO_STATUS_TAG_ERROR, CAIRO_STATUS_LAST_STATUS } cairo_status_t; /** * cairo_content_t: - * @CAIRO_CONTENT_COLOR: The surface will hold color content only. - * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. - * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. + * @CAIRO_CONTENT_COLOR: The surface will hold color content only. (Since 1.0) + * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. (Since 1.0) + * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. (Since 1.0) * * #cairo_content_t is used to describe the content that a surface will * contain, whether color information, alpha information (translucence @@ -348,6 +373,8 @@ typedef enum _cairo_status { * Note: The large values here are designed to keep #cairo_content_t * values distinct from #cairo_format_t values so that the * implementation can detect the error if users confuse the two types. + * + * Since: 1.0 **/ typedef enum _cairo_content { CAIRO_CONTENT_COLOR = 0x1000, @@ -355,6 +382,52 @@ typedef enum _cairo_content { CAIRO_CONTENT_COLOR_ALPHA = 0x3000 } cairo_content_t; +/** + * cairo_format_t: + * @CAIRO_FORMAT_INVALID: no such format exists or is supported. + * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with + * alpha in the upper 8 bits, then red, then green, then blue. + * The 32-bit quantities are stored native-endian. Pre-multiplied + * alpha is used. (That is, 50% transparent red is 0x80800000, + * not 0x80ff0000.) (Since 1.0) + * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with + * the upper 8 bits unused. Red, Green, and Blue are stored + * in the remaining 24 bits in that order. (Since 1.0) + * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding + * an alpha value. (Since 1.0) + * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding + * an alpha value. Pixels are packed together into 32-bit + * quantities. The ordering of the bits matches the + * endianness of the platform. On a big-endian machine, the + * first pixel is in the uppermost bit, on a little-endian + * machine the first pixel is in the least-significant bit. (Since 1.0) + * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity + * with red in the upper 5 bits, then green in the middle + * 6 bits, and blue in the lower 5 bits. (Since 1.2) + * @CAIRO_FORMAT_RGB30: like RGB24 but with 10bpc. (Since 1.12) + * @CAIRO_FORMAT_RGB96F: 3 floats, R, G, B. (Since 1.17.2) + * @CAIRO_FORMAT_RGBA128F: 4 floats, R, G, B, A. (Since 1.17.2) + * + * #cairo_format_t is used to identify the memory format of + * image data. + * + * New entries may be added in future versions. + * + * Since: 1.0 + **/ +typedef enum _cairo_format { + CAIRO_FORMAT_INVALID = -1, + CAIRO_FORMAT_ARGB32 = 0, + CAIRO_FORMAT_RGB24 = 1, + CAIRO_FORMAT_A8 = 2, + CAIRO_FORMAT_A1 = 3, + CAIRO_FORMAT_RGB16_565 = 4, + CAIRO_FORMAT_RGB30 = 5, + CAIRO_FORMAT_RGB96F = 6, + CAIRO_FORMAT_RGBA128F = 7 +} cairo_format_t; + + /** * cairo_write_func_t: * @closure: the output closure @@ -370,6 +443,8 @@ typedef enum _cairo_content { * %CAIRO_STATUS_WRITE_ERROR otherwise. * * Returns: the status code of the write operation + * + * Since: 1.0 **/ typedef cairo_status_t (*cairo_write_func_t) (void *closure, const unsigned char *data, @@ -390,11 +465,31 @@ typedef cairo_status_t (*cairo_write_func_t) (void *closure, * %CAIRO_STATUS_READ_ERROR otherwise. * * Returns: the status code of the read operation + * + * Since: 1.0 **/ typedef cairo_status_t (*cairo_read_func_t) (void *closure, unsigned char *data, unsigned int length); +/** + * 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 + * @width: width of the rectangle + * @height: height of the rectangle + * + * A data structure for holding a rectangle with integer coordinates. + * + * Since: 1.10 + **/ + +typedef struct _cairo_rectangle_int { + int x, y; + int width, height; +} cairo_rectangle_int_t; + + /* Functions for manipulating state objects */ cairo_public cairo_t * cairo_create (cairo_surface_t *target); @@ -440,64 +535,64 @@ cairo_pop_group_to_source (cairo_t *cr); /** * cairo_operator_t: - * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) - * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) + * @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) (Since 1.0) + * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) (Since 1.0) * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer - * (bounded) + * (bounded) (Since 1.0) * @CAIRO_OPERATOR_IN: draw source where there was destination content - * (unbounded) + * (unbounded) (Since 1.0) * @CAIRO_OPERATOR_OUT: draw source where there was no destination - * content (unbounded) + * content (unbounded) (Since 1.0) * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and - * only there - * @CAIRO_OPERATOR_DEST: ignore the source - * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source + * only there (Since 1.0) + * @CAIRO_OPERATOR_DEST: ignore the source (Since 1.0) + * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source (Since 1.0) * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was - * source content (unbounded) + * source content (unbounded) (Since 1.0) * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no - * source content + * source content (Since 1.0) * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content - * and only there (unbounded) + * and only there (unbounded) (Since 1.0) * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only - * one of them - * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated + * one of them (Since 1.0) + * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated (Since 1.0) * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are - * disjoint geometries + * disjoint geometries (Since 1.0) * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied. - * This causes the result to be at least as dark as the darker inputs. + * This causes the result to be at least as dark as the darker inputs. (Since 1.10) * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and * multiplied. This causes the result to be at least as light as the lighter - * inputs. + * inputs. (Since 1.10) * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the - * lightness of the destination color. + * lightness of the destination color. (Since 1.10) * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it - * is darker, otherwise keeps the source. + * is darker, otherwise keeps the source. (Since 1.10) * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it - * is lighter, otherwise keeps the source. + * is lighter, otherwise keeps the source. (Since 1.10) * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect - * the source color. + * the source color. (Since 1.10) * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect - * the source color. - * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependant on source - * color. - * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependant on source - * color. + * the source color. (Since 1.10) + * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependent on source + * color. (Since 1.10) + * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependent on source + * color. (Since 1.10) * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and - * destination color. + * destination color. (Since 1.10) * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but - * with lower contrast. + * with lower contrast. (Since 1.10) * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source - * and the saturation and luminosity of the target. + * and the saturation and luminosity of the target. (Since 1.10) * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation * of the source and the hue and luminosity of the target. Painting with - * this mode onto a gray area prduces no change. + * this mode onto a gray area produces no change. (Since 1.10) * @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation * of the source and the luminosity of the target. This preserves the gray * levels of the target and is useful for coloring monochrome images or - * tinting color images. + * tinting color images. (Since 1.10) * @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of * the source and the hue and saturation of the target. This produces an - * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. + * inverse effect to @CAIRO_OPERATOR_HSL_COLOR. (Since 1.10) * * #cairo_operator_t is used to set the compositing operator for all cairo * drawing operations. @@ -515,7 +610,9 @@ cairo_pop_group_to_source (cairo_t *cr); * translucent layers too. * For a more detailed explanation of the effects of each operator, including * the mathematical definitions, see - * http://cairographics.org/operators/. + * https://cairographics.org/operators/. + * + * Since: 1.0 **/ typedef enum _cairo_operator { CAIRO_OPERATOR_CLEAR, @@ -579,21 +676,52 @@ cairo_set_tolerance (cairo_t *cr, double tolerance); /** * cairo_antialias_t: * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for - * the subsystem and target device - * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask + * the subsystem and target device, since 1.0 + * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask, since 1.0 * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using - * shades of gray for black text on a white background, for example). + * shades of gray for black text on a white background, for example), since 1.0 * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking * advantage of the order of subpixel elements on devices - * such as LCD panels + * such as LCD panels, since 1.0 + * @CAIRO_ANTIALIAS_FAST: Hint that the backend should perform some + * antialiasing but prefer speed over quality, since 1.12 + * @CAIRO_ANTIALIAS_GOOD: The backend should balance quality against + * performance, since 1.12 + * @CAIRO_ANTIALIAS_BEST: Hint that the backend should render at the highest + * quality, sacrificing speed if necessary, since 1.12 * * Specifies the type of antialiasing to do when rendering text or shapes. + * + * As it is not necessarily clear from the above what advantages a particular + * antialias method provides, since 1.12, there is also a set of hints: + * @CAIRO_ANTIALIAS_FAST: Allow the backend to degrade raster quality for speed + * @CAIRO_ANTIALIAS_GOOD: A balance between speed and quality + * @CAIRO_ANTIALIAS_BEST: A high-fidelity, but potentially slow, raster mode + * + * These make no guarantee on how the backend will perform its rasterisation + * (if it even rasterises!), nor that they have any differing effect other + * than to enable some form of antialiasing. In the case of glyph rendering, + * @CAIRO_ANTIALIAS_FAST and @CAIRO_ANTIALIAS_GOOD will be mapped to + * @CAIRO_ANTIALIAS_GRAY, with @CAIRO_ANTALIAS_BEST being equivalent to + * @CAIRO_ANTIALIAS_SUBPIXEL. + * + * The interpretation of @CAIRO_ANTIALIAS_DEFAULT is left entirely up to + * the backend, typically this will be similar to @CAIRO_ANTIALIAS_GOOD. + * + * Since: 1.0 **/ typedef enum _cairo_antialias { CAIRO_ANTIALIAS_DEFAULT, + + /* method */ CAIRO_ANTIALIAS_NONE, CAIRO_ANTIALIAS_GRAY, - CAIRO_ANTIALIAS_SUBPIXEL + CAIRO_ANTIALIAS_SUBPIXEL, + + /* hints */ + CAIRO_ANTIALIAS_FAST, + CAIRO_ANTIALIAS_GOOD, + CAIRO_ANTIALIAS_BEST } cairo_antialias_t; cairo_public void @@ -605,11 +733,11 @@ cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias); * left-to-right, counts +1. If the path crosses the ray * from right to left, counts -1. (Left and right are determined * from the perspective of looking along the ray from the starting - * point.) If the total count is non-zero, the point will be filled. + * point.) If the total count is non-zero, the point will be filled. (Since 1.0) * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of * intersections, without regard to the orientation of the contour. If * the total number of intersections is odd, the point will be - * filled. + * filled. (Since 1.0) * * #cairo_fill_rule_t is used to select how paths are filled. For both * fill rules, whether or not a point is included in the fill is @@ -623,6 +751,8 @@ cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias); * The default fill rule is %CAIRO_FILL_RULE_WINDING. * * New entries may be added in future versions. + * + * Since: 1.0 **/ typedef enum _cairo_fill_rule { CAIRO_FILL_RULE_WINDING, @@ -637,13 +767,15 @@ cairo_set_line_width (cairo_t *cr, double width); /** * cairo_line_cap_t: - * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point - * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point - * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point + * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point (Since 1.0) + * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point (Since 1.0) + * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point (Since 1.0) * * Specifies how to render the endpoints of the path when stroking. * * The default line cap style is %CAIRO_LINE_CAP_BUTT. + * + * Since: 1.0 **/ typedef enum _cairo_line_cap { CAIRO_LINE_CAP_BUTT, @@ -657,15 +789,17 @@ cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap); /** * cairo_line_join_t: * @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see - * cairo_set_miter_limit() + * cairo_set_miter_limit() (Since 1.0) * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the - * joint point + * joint point (Since 1.0) * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half - * the line width from the joint point + * the line width from the joint point (Since 1.0) * * Specifies how to render the junction of two lines when stroking. * * The default line join style is %CAIRO_LINE_JOIN_MITER. + * + * Since: 1.0 **/ typedef enum _cairo_line_join { CAIRO_LINE_JOIN_MITER, @@ -896,6 +1030,17 @@ cairo_copy_clip_rectangle_list (cairo_t *cr); cairo_public void cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); +/* Logical structure tagging functions */ + +#define CAIRO_TAG_DEST "cairo.dest" +#define CAIRO_TAG_LINK "Link" + +cairo_public void +cairo_tag_begin (cairo_t *cr, const char *tag_name, const char *attributes); + +cairo_public void +cairo_tag_end (cairo_t *cr, const char *tag_name); + /* Font/Text functions */ /** @@ -912,6 +1057,8 @@ cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list); * * Memory management of #cairo_scaled_font_t is done with * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). + * + * Since: 1.0 **/ typedef struct _cairo_scaled_font cairo_scaled_font_t; @@ -920,7 +1067,7 @@ typedef struct _cairo_scaled_font cairo_scaled_font_t; * * A #cairo_font_face_t specifies all aspects of a font other * than the size or font matrix (a font matrix is used to distort - * a font by sheering it or scaling it unequally in the two + * a font by shearing it or scaling it unequally in the two * directions) . A font face can be set on a #cairo_t by using * cairo_set_font_face(); the size and font matrix are set with * cairo_set_font_size() and cairo_set_font_matrix(). @@ -931,6 +1078,8 @@ typedef struct _cairo_scaled_font cairo_scaled_font_t; * * Memory management of #cairo_font_face_t is done with * cairo_font_face_reference() and cairo_font_face_destroy(). + * + * Since: 1.0 **/ typedef struct _cairo_font_face cairo_font_face_t; @@ -957,6 +1106,8 @@ typedef struct _cairo_font_face cairo_font_face_t; * Note that the offsets given by @x and @y are not cumulative. When * drawing or measuring text, each glyph is individually positioned * with respect to the overall origin + * + * Since: 1.0 **/ typedef struct { unsigned long index; @@ -1004,7 +1155,7 @@ cairo_text_cluster_free (cairo_text_cluster_t *clusters); /** * cairo_text_cluster_flags_t: * @CAIRO_TEXT_CLUSTER_FLAG_BACKWARD: The clusters in the cluster array - * map to glyphs in the glyph array from end to start. + * map to glyphs in the glyph array from end to start. (Since 1.8) * * Specifies properties of a text cluster mapping. * @@ -1040,6 +1191,8 @@ typedef enum _cairo_text_cluster_flags { * doubled. They will change slightly due to hinting (so you can't * assume that metrics are independent of the transformation matrix), * but otherwise will remain unchanged. + * + * Since: 1.0 **/ typedef struct { double x_bearing; @@ -1062,7 +1215,7 @@ typedef struct { * portions below the baseline. Note that this is not always * exactly equal to the maximum of the extents of all the * glyphs in the font, but rather is picked to express the - * font designer's intent as to how the the font should + * font designer's intent as to how the font should * align with elements below it. * @height: the recommended vertical distance between baselines when * setting consecutive lines of text with the font. This @@ -1072,10 +1225,10 @@ typedef struct { * is at a premium, most fonts can be set with only * a distance of @ascent+@descent between lines. * @max_x_advance: the maximum distance in the X direction that - * the the origin is advanced for any glyph in the font. + * the origin is advanced for any glyph in the font. * @max_y_advance: the maximum distance in the Y direction that - * the the origin is advanced for any glyph in the font. - * this will be zero for normal fonts used for horizontal + * the origin is advanced for any glyph in the font. + * This will be zero for normal fonts used for horizontal * writing. (The scripts of East Asia are sometimes written * vertically.) * @@ -1090,6 +1243,8 @@ typedef struct { * not be doubled. They will change slightly due to hinting (so you * can't assume that metrics are independent of the transformation * matrix), but otherwise will remain unchanged. + * + * Since: 1.0 **/ typedef struct { double ascent; @@ -1101,11 +1256,13 @@ typedef struct { /** * cairo_font_slant_t: - * @CAIRO_FONT_SLANT_NORMAL: Upright font style - * @CAIRO_FONT_SLANT_ITALIC: Italic font style - * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style + * @CAIRO_FONT_SLANT_NORMAL: Upright font style, since 1.0 + * @CAIRO_FONT_SLANT_ITALIC: Italic font style, since 1.0 + * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style, since 1.0 * * Specifies variants of a font face based on their slant. + * + * Since: 1.0 **/ typedef enum _cairo_font_slant { CAIRO_FONT_SLANT_NORMAL, @@ -1115,10 +1272,12 @@ typedef enum _cairo_font_slant { /** * cairo_font_weight_t: - * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight - * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight + * @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight, since 1.0 + * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight, since 1.0 * * Specifies variants of a font face based on their weight. + * + * Since: 1.0 **/ typedef enum _cairo_font_weight { CAIRO_FONT_WEIGHT_NORMAL, @@ -1128,19 +1287,21 @@ typedef enum _cairo_font_weight { /** * cairo_subpixel_order_t: * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for - * for the target device + * for the target device, since 1.0 * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally - * with red at the left + * with red at the left, since 1.0 * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally - * with blue at the left + * with blue at the left, since 1.0 * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically - * with red at the top + * with red at the top, since 1.0 * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically - * with blue at the top + * with blue at the top, since 1.0 * * The subpixel order specifies the order of color elements within * each pixel on the display device when rendering with an * antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. + * + * Since: 1.0 **/ typedef enum _cairo_subpixel_order { CAIRO_SUBPIXEL_ORDER_DEFAULT, @@ -1153,15 +1314,15 @@ typedef enum _cairo_subpixel_order { /** * cairo_hint_style_t: * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for - * font backend and target device - * @CAIRO_HINT_STYLE_NONE: Do not hint outlines + * font backend and target device, since 1.0 + * @CAIRO_HINT_STYLE_NONE: Do not hint outlines, since 1.0 * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve * contrast while retaining good fidelity to the original - * shapes. + * shapes, since 1.0 * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength * giving a compromise between fidelity to the original shapes - * and contrast - * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast + * and contrast, since 1.0 + * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast, since 1.0 * * Specifies the type of hinting to do on font outlines. Hinting * is the process of fitting outlines to the pixel grid in order @@ -1171,6 +1332,8 @@ typedef enum _cairo_subpixel_order { * styles are supported by all font backends. * * New entries may be added in future versions. + * + * Since: 1.0 **/ typedef enum _cairo_hint_style { CAIRO_HINT_STYLE_DEFAULT, @@ -1183,15 +1346,17 @@ typedef enum _cairo_hint_style { /** * cairo_hint_metrics_t: * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default - * manner for the font backend and target device - * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics - * @CAIRO_HINT_METRICS_ON: Hint font metrics + * manner for the font backend and target device, since 1.0 + * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics, since 1.0 + * @CAIRO_HINT_METRICS_ON: Hint font metrics, since 1.0 * * Specifies whether to hint font metrics; hinting font metrics * means quantizing them so that they are integer values in * device space. Doing this improves the consistency of * letter and line spacing, however it also means that text * will be laid out differently at different zoom factors. + * + * Since: 1.0 **/ typedef enum _cairo_hint_metrics { CAIRO_HINT_METRICS_DEFAULT, @@ -1199,30 +1364,6 @@ typedef enum _cairo_hint_metrics { CAIRO_HINT_METRICS_ON } cairo_hint_metrics_t; -/** - * cairo_lcd_filter_t: - * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for - * font backend and target device - * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering - * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter - * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel - * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel - * - * The LCD filter specifies the low-pass filter applied to LCD-optimized - * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL. - * - * Note: This API was temporarily made available in the public - * interface during the 1.7.x development series, but was made private - * before 1.8. - **/ -typedef enum _cairo_lcd_filter { - CAIRO_LCD_FILTER_DEFAULT, - CAIRO_LCD_FILTER_NONE, - CAIRO_LCD_FILTER_INTRA_PIXEL, - CAIRO_LCD_FILTER_FIR3, - CAIRO_LCD_FILTER_FIR5 -} cairo_lcd_filter_t; - /** * cairo_font_options_t: * @@ -1231,8 +1372,8 @@ typedef enum _cairo_lcd_filter { * * Individual features of a #cairo_font_options_t can be set or * accessed using functions named - * cairo_font_options_set_feature_name and - * cairo_font_options_get_feature_name, like + * cairo_font_options_set_feature_name() and + * cairo_font_options_get_feature_name(), like * cairo_font_options_set_antialias() and * cairo_font_options_get_antialias(). * @@ -1242,6 +1383,8 @@ typedef enum _cairo_lcd_filter { * cairo_font_options_hash() should be used to copy, check * for equality, merge, or compute a hash value of * #cairo_font_options_t objects. + * + * Since: 1.0 **/ typedef struct _cairo_font_options cairo_font_options_t; @@ -1291,9 +1434,12 @@ cairo_font_options_set_hint_metrics (cairo_font_options_t *options, cairo_public cairo_hint_metrics_t cairo_font_options_get_hint_metrics (const cairo_font_options_t *options); +cairo_public const char * +cairo_font_options_get_variations (cairo_font_options_t *options); + cairo_public void -cairo_font_options_set_lcd_filter (cairo_font_options_t *options, - cairo_lcd_filter_t lcd_filter); +cairo_font_options_set_variations (cairo_font_options_t *options, + const char *variations); /* This interface is for dealing with text as text, not caring about the font object inside the the cairo_t. */ @@ -1390,10 +1536,11 @@ cairo_font_face_status (cairo_font_face_t *font_face); /** * cairo_font_type_t: - * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api - * @CAIRO_FONT_TYPE_FT: The font is of type FreeType - * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 - * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6) + * @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api (Since: 1.2) + * @CAIRO_FONT_TYPE_FT: The font is of type FreeType (Since: 1.2) + * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 (Since: 1.2) + * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6, in 1.2 and + * 1.4 it was named CAIRO_FONT_TYPE_ATSUI) * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8) * * #cairo_font_type_t is used to describe the type of a given font @@ -1402,8 +1549,8 @@ cairo_font_face_status (cairo_font_face_t *font_face); * * The type of a font face is determined by the function used to * create it, which will generally be of the form - * cairo_type_font_face_create(). The font face type can be queried - * with cairo_font_face_get_type() + * cairo_type_font_face_create(). + * The font face type can be queried with cairo_font_face_get_type() * * The various #cairo_font_face_t functions can be used with a font face * of any type. @@ -1416,7 +1563,8 @@ cairo_font_face_status (cairo_font_face_t *font_face); * fonts of any type, but some font backends also provide * type-specific functions that must only be called with a scaled font * of the appropriate type. These functions have names that begin with - * cairo_type_scaled_font() such as cairo_ft_scaled_font_lock_face(). + * cairo_type_scaled_font() + * such as cairo_ft_scaled_font_lock_face(). * * The behavior of calling a type-specific function with a scaled font * of the wrong type is undefined. @@ -1430,8 +1578,7 @@ typedef enum _cairo_font_type { CAIRO_FONT_TYPE_FT, CAIRO_FONT_TYPE_WIN32, CAIRO_FONT_TYPE_QUARTZ, - CAIRO_FONT_TYPE_USER, - CAIRO_FONT_TYPE_DWRITE + CAIRO_FONT_TYPE_USER } cairo_font_type_t; cairo_public cairo_font_type_t @@ -1526,10 +1673,6 @@ cairo_public void cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, cairo_font_options_t *options); -/* mozilla extension, see https://bugzilla.mozilla.org/show_bug.cgi?id=1377257 */ -cairo_public cairo_hint_metrics_t -cairo_scaled_font_get_hint_metrics (cairo_scaled_font_t *scaled_font); - /* Toy fonts */ @@ -1667,7 +1810,8 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal * using cairo_glyph_allocate() and placed in @glyphs. Upon return, * @num_glyphs should contain the number of generated glyphs. If the value * @glyphs points at has changed after the call, the caller will free the - * allocated glyph array using cairo_glyph_free(). + * allocated glyph array using cairo_glyph_free(). The caller will also free + * the original value of @glyphs, so the callback shouldn't do so. * The callback should populate the glyph indices and positions (in font space) * assuming that the text is to be shown at the origin. * @@ -1678,8 +1822,9 @@ typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scal * as a cluster buffer, and @num_clusters points to the number of cluster * entries available there. If the provided cluster array is too short for * the conversion (or for convenience), a new cluster array may be allocated - * using cairo_text_cluster_allocate() and placed in @clusters. Upon return, - * @num_clusters should contain the number of generated clusters. + * using cairo_text_cluster_allocate() and placed in @clusters. In this case, + * the original value of @clusters will still be freed by the caller. Upon + * return, @num_clusters should contain the number of generated clusters. * If the value @clusters points at has changed after the call, the caller * will free the allocated cluster array using cairo_text_cluster_free(). * @@ -1838,14 +1983,16 @@ cairo_get_group_target (cairo_t *cr); /** * cairo_path_data_type_t: - * @CAIRO_PATH_MOVE_TO: A move-to operation - * @CAIRO_PATH_LINE_TO: A line-to operation - * @CAIRO_PATH_CURVE_TO: A curve-to operation - * @CAIRO_PATH_CLOSE_PATH: A close-path operation + * @CAIRO_PATH_MOVE_TO: A move-to operation, since 1.0 + * @CAIRO_PATH_LINE_TO: A line-to operation, since 1.0 + * @CAIRO_PATH_CURVE_TO: A curve-to operation, since 1.0 + * @CAIRO_PATH_CLOSE_PATH: A close-path operation, since 1.0 * * #cairo_path_data_t is used to describe the type of one portion * of a path when represented as a #cairo_path_t. * See #cairo_path_data_t for details. + * + * Since: 1.0 **/ typedef enum _cairo_path_data_type { CAIRO_PATH_MOVE_TO, @@ -1919,6 +2066,8 @@ typedef enum _cairo_path_data_type { * always use data->header.length to * iterate over the path data, instead of hardcoding the number of * elements for each element type. + * + * Since: 1.0 **/ typedef union _cairo_path_data_t cairo_path_data_t; union _cairo_path_data_t { @@ -1949,6 +2098,8 @@ union _cairo_path_data_t { * array. This number is larger than the number of independent path * portions (defined in #cairo_path_data_type_t), since the data * includes both headers and coordinates for each portion. + * + * Since: 1.0 **/ typedef struct cairo_path { cairo_status_t status; @@ -1984,26 +2135,29 @@ cairo_device_reference (cairo_device_t *device); /** * cairo_device_type_t: - * @CAIRO_DEVICE_TYPE_DRM: The surface is of type Direct Render Manager - * @CAIRO_DEVICE_TYPE_GL: The surface is of type OpenGL - * @CAIRO_DEVICE_TYPE_SCRIPT: The surface is of type script - * @CAIRO_DEVICE_TYPE_XCB: The surface is of type xcb - * @CAIRO_DEVICE_TYPE_XLIB: The surface is of type xlib - * @CAIRO_DEVICE_TYPE_XML: The surface is of type XML - * cairo_surface_create_for_rectangle() + * @CAIRO_DEVICE_TYPE_DRM: The device is of type Direct Render Manager, since 1.10 + * @CAIRO_DEVICE_TYPE_GL: The device is of type OpenGL, since 1.10 + * @CAIRO_DEVICE_TYPE_SCRIPT: The device is of type script, since 1.10 + * @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb, since 1.10 + * @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib, since 1.10 + * @CAIRO_DEVICE_TYPE_XML: The device is of type XML, since 1.10 + * @CAIRO_DEVICE_TYPE_COGL: The device is of type cogl, since 1.12 + * @CAIRO_DEVICE_TYPE_WIN32: The device is of type win32, since 1.12 + * @CAIRO_DEVICE_TYPE_INVALID: The device is invalid, since 1.10 * * #cairo_device_type_t is used to describe the type of a given * device. The devices types are also known as "backends" within cairo. * * The device type can be queried with cairo_device_get_type() * - * The various #cairo_device_t functions can be used with surfaces of + * The various #cairo_device_t functions can be used with devices of * any type, but some backends also provide type-specific functions * that must only be called with a device of the appropriate * type. These functions have names that begin with - * cairo_type_device such as cairo_xcb_device_debug_set_render_version(). + * cairo_type_device such as + * cairo_xcb_device_debug_cap_xrender_version(). * - * The behavior of calling a type-specific function with a surface of + * The behavior of calling a type-specific function with a device of * the wrong type is undefined. * * New entries may be added in future versions. @@ -2016,7 +2170,11 @@ typedef enum _cairo_device_type { CAIRO_DEVICE_TYPE_SCRIPT, CAIRO_DEVICE_TYPE_XCB, CAIRO_DEVICE_TYPE_XLIB, - CAIRO_DEVICE_TYPE_XML + CAIRO_DEVICE_TYPE_XML, + CAIRO_DEVICE_TYPE_COGL, + CAIRO_DEVICE_TYPE_WIN32, + + CAIRO_DEVICE_TYPE_INVALID = -1 } cairo_device_type_t; cairo_public cairo_device_type_t @@ -2062,6 +2220,20 @@ cairo_surface_create_similar (cairo_surface_t *other, int width, int height); +cairo_public cairo_surface_t * +cairo_surface_create_similar_image (cairo_surface_t *other, + cairo_format_t format, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_surface_map_to_image (cairo_surface_t *surface, + const cairo_rectangle_int_t *extents); + +cairo_public void +cairo_surface_unmap_image (cairo_surface_t *surface, + cairo_surface_t *image); + cairo_public cairo_surface_t * cairo_surface_create_for_rectangle (cairo_surface_t *target, double x, @@ -2069,6 +2241,93 @@ cairo_surface_create_for_rectangle (cairo_surface_t *target, double width, double height); +/** + * cairo_surface_observer_mode_t: + * @CAIRO_SURFACE_OBSERVER_NORMAL: no recording is done + * @CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS: operations are recorded + * + * Whether operations should be recorded. + * + * Since: 1.12 + **/ +typedef enum { + CAIRO_SURFACE_OBSERVER_NORMAL = 0, + CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS = 0x1 +} cairo_surface_observer_mode_t; + +cairo_public cairo_surface_t * +cairo_surface_create_observer (cairo_surface_t *target, + cairo_surface_observer_mode_t mode); + +typedef void (*cairo_surface_observer_callback_t) (cairo_surface_t *observer, + cairo_surface_t *target, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface, + cairo_surface_observer_callback_t func, + void *data); + +cairo_public cairo_status_t +cairo_surface_observer_print (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure); +cairo_public double +cairo_surface_observer_elapsed (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_device_observer_print (cairo_device_t *device, + cairo_write_func_t write_func, + void *closure); + +cairo_public double +cairo_device_observer_elapsed (cairo_device_t *device); + +cairo_public double +cairo_device_observer_paint_elapsed (cairo_device_t *device); + +cairo_public double +cairo_device_observer_mask_elapsed (cairo_device_t *device); + +cairo_public double +cairo_device_observer_fill_elapsed (cairo_device_t *device); + +cairo_public double +cairo_device_observer_stroke_elapsed (cairo_device_t *device); + +cairo_public double +cairo_device_observer_glyphs_elapsed (cairo_device_t *device); + cairo_public cairo_surface_t * cairo_surface_reference (cairo_surface_t *surface); @@ -2089,20 +2348,20 @@ cairo_surface_status (cairo_surface_t *surface); /** * cairo_surface_type_t: - * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image - * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf - * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps - * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib - * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb - * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz - * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz - * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32 - * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos - * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb - * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg - * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2 - * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface - * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image + * @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image, since 1.2 + * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf, since 1.2 + * @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_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_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_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_RECORDING: The surface is of type recording, since 1.10 @@ -2111,17 +2370,17 @@ cairo_surface_status (cairo_surface_t *surface); * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 * @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 * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with * cairo_surface_create_for_rectangle(), since 1.10 - * @CAIRO_SURFACE_TYPE_D2D: The surface is of type Direct2D + * @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl, since 1.12 * * #cairo_surface_type_t is used to describe the type of a given * surface. The surface types are also known as "backends" or "surface * backends" within cairo. * * The type of a surface is determined by the function used to create - * it, which will generally be of the form cairo_type_surface_create(), + * it, which will generally be of the form + * cairo_type_surface_create(), * (though see cairo_surface_create_similar() as well). * * The surface type can be queried with cairo_surface_get_type() @@ -2130,7 +2389,7 @@ cairo_surface_status (cairo_surface_t *surface); * any type, but some backends also provide type-specific functions * that must only be called with a surface of the appropriate * type. These functions have names that begin with - * cairo_type_surface such as cairo_image_surface_get_width(). + * cairo_type_surface such as cairo_image_surface_get_width(). * * The behavior of calling a type-specific function with a surface of * the wrong type is undefined. @@ -2164,7 +2423,7 @@ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_XML, CAIRO_SURFACE_TYPE_SKIA, CAIRO_SURFACE_TYPE_SUBSURFACE, - CAIRO_SURFACE_TYPE_D2D + CAIRO_SURFACE_TYPE_COGL } cairo_surface_type_t; cairo_public cairo_surface_type_t @@ -2196,18 +2455,18 @@ cairo_surface_set_user_data (cairo_surface_t *surface, void *user_data, cairo_destroy_func_t destroy); -cairo_public void -cairo_surface_attach_snapshot (cairo_surface_t *surface, - cairo_surface_t *snapshot, - cairo_surface_func_t detach_func); - -cairo_public void -cairo_surface_detach_snapshot (cairo_surface_t *snapshot); - #define CAIRO_MIME_TYPE_JPEG "image/jpeg" #define CAIRO_MIME_TYPE_PNG "image/png" #define CAIRO_MIME_TYPE_JP2 "image/jp2" #define CAIRO_MIME_TYPE_URI "text/x-uri" +#define CAIRO_MIME_TYPE_UNIQUE_ID "application/x-cairo.uuid" +#define CAIRO_MIME_TYPE_JBIG2 "application/x-cairo.jbig2" +#define CAIRO_MIME_TYPE_JBIG2_GLOBAL "application/x-cairo.jbig2-global" +#define CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID "application/x-cairo.jbig2-global-id" +#define CAIRO_MIME_TYPE_CCITT_FAX "image/g3fax" +#define CAIRO_MIME_TYPE_CCITT_FAX_PARAMS "application/x-cairo.ccitt.params" +#define CAIRO_MIME_TYPE_EPS "application/postscript" +#define CAIRO_MIME_TYPE_EPS_PARAMS "application/x-cairo.eps.params" cairo_public void cairo_surface_get_mime_data (cairo_surface_t *surface, @@ -2223,6 +2482,10 @@ cairo_surface_set_mime_data (cairo_surface_t *surface, cairo_destroy_func_t destroy, void *closure); +cairo_public cairo_bool_t +cairo_surface_supports_mime_type (cairo_surface_t *surface, + const char *mime_type); + cairo_public void cairo_surface_get_font_options (cairo_surface_t *surface, cairo_font_options_t *options); @@ -2240,6 +2503,16 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, int width, int height); +cairo_public void +cairo_surface_set_device_scale (cairo_surface_t *surface, + double x_scale, + double y_scale); + +cairo_public void +cairo_surface_get_device_scale (cairo_surface_t *surface, + double *x_scale, + double *y_scale); + cairo_public void cairo_surface_set_device_offset (cairo_surface_t *surface, double x_offset, @@ -2269,64 +2542,8 @@ cairo_surface_show_page (cairo_surface_t *surface); cairo_public cairo_bool_t cairo_surface_has_show_text_glyphs (cairo_surface_t *surface); -/** - * _cairo_subpixel_antialiasing_t: - * @CAIRO_SUBPIXEL_ANTIALIASING_ENABLED: subpixel antialiasing is enabled - * for this surface. - * @CAIRO_SUBPIXEL_ANTIALIASING_DISABLED: subpixel antialiasing is disabled - * for this surface. - */ -typedef enum _cairo_subpixel_antialiasing_t { - CAIRO_SUBPIXEL_ANTIALIASING_ENABLED, - CAIRO_SUBPIXEL_ANTIALIASING_DISABLED -} cairo_subpixel_antialiasing_t; - -cairo_public void -cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, - cairo_subpixel_antialiasing_t enabled); - -cairo_public cairo_subpixel_antialiasing_t -cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface); - /* Image-surface functions */ -/** - * cairo_format_t: - * @CAIRO_FORMAT_INVALID: no such format exists or is supported. - * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with - * alpha in the upper 8 bits, then red, then green, then blue. - * The 32-bit quantities are stored native-endian. Pre-multiplied - * alpha is used. (That is, 50% transparent red is 0x80800000, - * not 0x80ff0000.) - * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with - * the upper 8 bits unused. Red, Green, and Blue are stored - * in the remaining 24 bits in that order. - * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding - * an alpha value. - * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding - * an alpha value. Pixels are packed together into 32-bit - * quantities. The ordering of the bits matches the - * endianess of the platform. On a big-endian machine, the - * first pixel is in the uppermost bit, on a little-endian - * machine the first pixel is in the least-significant bit. - * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity - * with red in the upper 5 bits, then green in the middle - * 6 bits, and blue in the lower 5 bits. - * - * #cairo_format_t is used to identify the memory format of - * image data. - * - * New entries may be added in future versions. - **/ -typedef enum _cairo_format { - CAIRO_FORMAT_INVALID = -1, - CAIRO_FORMAT_ARGB32 = 0, - CAIRO_FORMAT_RGB24 = 1, - CAIRO_FORMAT_A8 = 2, - CAIRO_FORMAT_A1 = 3, - CAIRO_FORMAT_RGB16_565 = 4 -} cairo_format_t; - cairo_public cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, @@ -2382,10 +2599,154 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface, double *width, double *height); -/* Null-surface functions */ +cairo_public cairo_bool_t +cairo_recording_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_t *extents); -cairo_public cairo_surface_t * -cairo_null_surface_create (cairo_content_t content); +/* raster-source pattern (callback) functions */ + +/** + * cairo_raster_source_acquire_func_t: + * @pattern: the pattern being rendered from + * @callback_data: the user data supplied during creation + * @target: the rendering target surface + * @extents: rectangular region of interest in pixels in sample space + * + * #cairo_raster_source_acquire_func_t is the type of function which is + * called when a pattern is being rendered from. It should create a surface + * that provides the pixel data for the region of interest as defined by + * extents, though the surface itself does not have to be limited to that + * area. For convenience the surface should probably be of image type, + * created with cairo_surface_create_similar_image() for the target (which + * enables the number of copies to be reduced during transfer to the + * device). Another option, might be to return a similar surface to the + * target for explicit handling by the application of a set of cached sources + * on the device. The region of sample data provided should be defined using + * cairo_surface_set_device_offset() to specify the top-left corner of the + * sample data (along with width and height of the surface). + * + * Returns: a #cairo_surface_t + * + * Since: 1.12 + **/ +typedef cairo_surface_t * +(*cairo_raster_source_acquire_func_t) (cairo_pattern_t *pattern, + void *callback_data, + cairo_surface_t *target, + const cairo_rectangle_int_t *extents); + +/** + * cairo_raster_source_release_func_t: + * @pattern: the pattern being rendered from + * @callback_data: the user data supplied during creation + * @surface: the surface created during acquire + * + * #cairo_raster_source_release_func_t is the type of function which is + * called when the pixel data is no longer being access by the pattern + * for the rendering operation. Typically this function will simply + * destroy the surface created during acquire. + * + * Since: 1.12 + **/ +typedef void +(*cairo_raster_source_release_func_t) (cairo_pattern_t *pattern, + void *callback_data, + cairo_surface_t *surface); + +/** + * cairo_raster_source_snapshot_func_t: + * @pattern: the pattern being rendered from + * @callback_data: the user data supplied during creation + * + * #cairo_raster_source_snapshot_func_t is the type of function which is + * called when the pixel data needs to be preserved for later use + * during printing. This pattern will be accessed again later, and it + * is expected to provide the pixel data that was current at the time + * of snapshotting. + * + * Return value: CAIRO_STATUS_SUCCESS on success, or one of the + * #cairo_status_t error codes for failure. + * + * Since: 1.12 + **/ +typedef cairo_status_t +(*cairo_raster_source_snapshot_func_t) (cairo_pattern_t *pattern, + void *callback_data); + +/** + * cairo_raster_source_copy_func_t: + * @pattern: the #cairo_pattern_t that was copied to + * @callback_data: the user data supplied during creation + * @other: the #cairo_pattern_t being used as the source for the copy + * + * #cairo_raster_source_copy_func_t is the type of function which is + * called when the pattern gets copied as a normal part of rendering. + * + * Return value: CAIRO_STATUS_SUCCESS on success, or one of the + * #cairo_status_t error codes for failure. + * + * Since: 1.12 + **/ +typedef cairo_status_t +(*cairo_raster_source_copy_func_t) (cairo_pattern_t *pattern, + void *callback_data, + const cairo_pattern_t *other); + +/** + * cairo_raster_source_finish_func_t: + * @pattern: the pattern being rendered from + * @callback_data: the user data supplied during creation + * + * #cairo_raster_source_finish_func_t is the type of function which is + * called when the pattern (or a copy thereof) is no longer required. + * + * Since: 1.12 + **/ +typedef void +(*cairo_raster_source_finish_func_t) (cairo_pattern_t *pattern, + void *callback_data); + +cairo_public cairo_pattern_t * +cairo_pattern_create_raster_source (void *user_data, + cairo_content_t content, + int width, int height); + +cairo_public void +cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *pattern, + void *data); + +cairo_public void * +cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *pattern); + +cairo_public void +cairo_raster_source_pattern_set_acquire (cairo_pattern_t *pattern, + cairo_raster_source_acquire_func_t acquire, + cairo_raster_source_release_func_t release); + +cairo_public void +cairo_raster_source_pattern_get_acquire (cairo_pattern_t *pattern, + cairo_raster_source_acquire_func_t *acquire, + cairo_raster_source_release_func_t *release); +cairo_public void +cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *pattern, + cairo_raster_source_snapshot_func_t snapshot); + +cairo_public cairo_raster_source_snapshot_func_t +cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *pattern); + +cairo_public void +cairo_raster_source_pattern_set_copy (cairo_pattern_t *pattern, + cairo_raster_source_copy_func_t copy); + +cairo_public cairo_raster_source_copy_func_t +cairo_raster_source_pattern_get_copy (cairo_pattern_t *pattern); + +cairo_public void +cairo_raster_source_pattern_set_finish (cairo_pattern_t *pattern, + cairo_raster_source_finish_func_t finish); + +cairo_public cairo_raster_source_finish_func_t +cairo_raster_source_pattern_get_finish (cairo_pattern_t *pattern); /* Pattern creation functions */ @@ -2407,6 +2768,9 @@ cairo_public cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1); +cairo_public cairo_pattern_t * +cairo_pattern_create_mesh (void); + cairo_public cairo_pattern_t * cairo_pattern_reference (cairo_pattern_t *pattern); @@ -2432,10 +2796,12 @@ cairo_pattern_set_user_data (cairo_pattern_t *pattern, /** * cairo_pattern_type_t: * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform) - * color. It may be opaque or translucent. - * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image). - * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient. - * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient. + * color. It may be opaque or translucent, since 1.2. + * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image), since 1.2. + * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient, since 1.2. + * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient, since 1.2. + * @CAIRO_PATTERN_TYPE_MESH: The pattern is a mesh, since 1.12. + * @CAIRO_PATTERN_TYPE_RASTER_SOURCE: The pattern is a user pattern providing raster data, since 1.12. * * #cairo_pattern_type_t is used to describe the type of a given pattern. * @@ -2463,7 +2829,9 @@ typedef enum _cairo_pattern_type { CAIRO_PATTERN_TYPE_SOLID, CAIRO_PATTERN_TYPE_SURFACE, CAIRO_PATTERN_TYPE_LINEAR, - CAIRO_PATTERN_TYPE_RADIAL + CAIRO_PATTERN_TYPE_RADIAL, + CAIRO_PATTERN_TYPE_MESH, + CAIRO_PATTERN_TYPE_RASTER_SOURCE } cairo_pattern_type_t; cairo_public cairo_pattern_type_t @@ -2480,6 +2848,42 @@ cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double red, double green, double blue, double alpha); +cairo_public void +cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern); + +cairo_public void +cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern); + +cairo_public void +cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern, + double x1, double y1, + double x2, double y2, + double x3, double y3); + +cairo_public void +cairo_mesh_pattern_line_to (cairo_pattern_t *pattern, + double x, double y); + +cairo_public void +cairo_mesh_pattern_move_to (cairo_pattern_t *pattern, + double x, double y); + +cairo_public void +cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern, + unsigned int point_num, + double x, double y); + +cairo_public void +cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern, + unsigned int corner_num, + double red, double green, double blue); + +cairo_public void +cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern, + unsigned int corner_num, + double red, double green, double blue, + double alpha); + cairo_public void cairo_pattern_set_matrix (cairo_pattern_t *pattern, const cairo_matrix_t *matrix); @@ -2491,10 +2895,10 @@ cairo_pattern_get_matrix (cairo_pattern_t *pattern, /** * cairo_extend_t: * @CAIRO_EXTEND_NONE: pixels outside of the source pattern - * are fully transparent - * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating + * are fully transparent (Since 1.0) + * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating (Since 1.0) * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting - * at the edges (Implemented for surface patterns since 1.6) + * at the edges (Since 1.0; but only implemented for surface patterns since 1.6) * @CAIRO_EXTEND_PAD: pixels outside of the pattern copy * the closest pixel from the source (Since 1.2; but only * implemented for surface patterns since 1.6) @@ -2504,10 +2908,14 @@ cairo_pattern_get_matrix (cairo_pattern_t *pattern, * example, outside the surface bounds or outside the gradient * geometry). * + * Mesh patterns are not affected by the extend mode. + * * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns * and %CAIRO_EXTEND_PAD for gradient patterns. * * New entries may be added in future versions. + * + * Since: 1.0 **/ typedef enum _cairo_extend { CAIRO_EXTEND_NONE, @@ -2525,21 +2933,23 @@ cairo_pattern_get_extend (cairo_pattern_t *pattern); /** * cairo_filter_t: * @CAIRO_FILTER_FAST: A high-performance filter, with quality similar - * to %CAIRO_FILTER_NEAREST + * to %CAIRO_FILTER_NEAREST (Since 1.0) * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality - * similar to %CAIRO_FILTER_BILINEAR + * similar to %CAIRO_FILTER_BILINEAR (Since 1.0) * @CAIRO_FILTER_BEST: The highest-quality available, performance may - * not be suitable for interactive use. - * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering - * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions + * not be suitable for interactive use. (Since 1.0) + * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering (Since 1.0) + * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions (Since 1.0) * @CAIRO_FILTER_GAUSSIAN: This filter value is currently - * unimplemented, and should not be used in current code. + * unimplemented, and should not be used in current code. (Since 1.0) * * #cairo_filter_t is used to indicate what filtering should be * applied when reading pixel values from patterns. See - * cairo_pattern_set_source() for indicating the desired filter to be + * cairo_pattern_set_filter() for indicating the desired filter to be * used with a particular pattern. - */ + * + * Since: 1.0 + **/ typedef enum _cairo_filter { CAIRO_FILTER_FAST, CAIRO_FILTER_GOOD, @@ -2585,6 +2995,27 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, double *x0, double *y0, double *r0, double *x1, double *y1, double *r1); +cairo_public cairo_status_t +cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern, + unsigned int *count); + +cairo_public cairo_path_t * +cairo_mesh_pattern_get_path (cairo_pattern_t *pattern, + unsigned int patch_num); + +cairo_public cairo_status_t +cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern, + unsigned int patch_num, + unsigned int corner_num, + double *red, double *green, + double *blue, double *alpha); + +cairo_public cairo_status_t +cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern, + unsigned int patch_num, + unsigned int point_num, + double *x, double *y); + /* Matrix functions */ cairo_public void @@ -2651,22 +3082,16 @@ cairo_matrix_transform_point (const cairo_matrix_t *matrix, typedef struct _cairo_region cairo_region_t; /** - * 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 - * @width: width of the rectangle - * @height: height of the rectangle + * cairo_region_overlap_t: + * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region. (Since 1.10) + * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region. (Since 1.10) + * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and + * partially outside the region. (Since 1.10) * - * A data structure for holding a rectangle with integer coordinates. + * Used as the return value for cairo_region_contains_rectangle(). * * Since: 1.10 **/ - -typedef struct _cairo_rectangle_int { - int x, y; - int width, height; -} cairo_rectangle_int_t; - typedef enum _cairo_region_overlap { CAIRO_REGION_OVERLAP_IN, /* completely inside region */ CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ diff --git a/gfx/cairo/cairo/src/cairo.pc.in b/gfx/cairo/cairo/src/cairo.pc.in new file mode 100644 index 000000000000..b361edf18fb1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo.pc.in @@ -0,0 +1,13 @@ +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/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 95dcafc97222..c97ad57fa7d6 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -71,6 +71,14 @@ #include #include "cairo-compiler-private.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_PDF_SURFACE || \ + CAIRO_HAS_PS_SURFACE || \ + CAIRO_HAS_SCRIPT_SURFACE || \ + CAIRO_HAS_XML_SURFACE +#define CAIRO_HAS_DEFLATE_STREAM 1 +#endif #if CAIRO_HAS_PS_SURFACE || \ CAIRO_HAS_PDF_SURFACE || \ @@ -79,7 +87,9 @@ #define CAIRO_HAS_FONT_SUBSET 1 #endif -#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_FONT_SUBSET +#if CAIRO_HAS_PS_SURFACE || \ + CAIRO_HAS_PDF_SURFACE || \ + CAIRO_HAS_FONT_SUBSET #define CAIRO_HAS_PDF_OPERATORS 1 #endif @@ -97,6 +107,12 @@ _cairo_win32_tmpfile (void); #undef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) +#define ISFINITE(x) isfinite (x) +#else +#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */ +#endif + #ifndef FALSE #define FALSE 0 #endif @@ -109,13 +125,6 @@ _cairo_win32_tmpfile (void); #define M_PI 3.14159265358979323846 #endif -#ifndef NDEBUG -#undef assert -#define assert(expr) \ - do { if (!(expr)) fprintf(stderr, "Assertion failed at %s:%d: %s\n", \ - __FILE__, __LINE__, #expr); } while (0) -#endif - #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 #endif @@ -178,7 +187,7 @@ do { \ static inline int cairo_const _cairo_popcount (uint32_t mask) { -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __clang__ +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) return __builtin_popcount (mask); #else register int y; @@ -189,6 +198,13 @@ _cairo_popcount (uint32_t mask) #endif } +static cairo_always_inline cairo_bool_t +_cairo_is_little_endian (void) +{ + static const int i = 1; + return *((char *) &i) == 0x01; +} + #ifdef WORDS_BIGENDIAN #define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c) #else @@ -219,7 +235,7 @@ be16_to_cpu(uint16_t v) static inline uint32_t cairo_const cpu_to_be32(uint32_t v) { - return ((uint32_t) cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); + return (v >> 24) | ((v >> 8) & 0xff00) | ((v << 8) & 0xff0000) | (v << 24); } static inline uint32_t cairo_const @@ -230,6 +246,32 @@ be32_to_cpu(uint32_t v) #endif +/* Unaligned big endian access + */ + +static inline uint16_t get_unaligned_be16 (const unsigned char *p) +{ + return p[0] << 8 | p[1]; +} + +static inline uint32_t get_unaligned_be32 (const unsigned char *p) +{ + return (uint32_t)p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +static inline void put_unaligned_be16 (uint16_t v, unsigned char *p) +{ + p[0] = (v >> 8) & 0xff; + p[1] = v & 0xff; +} + +static inline void put_unaligned_be32 (uint32_t v, unsigned char *p) +{ + p[0] = (v >> 24) & 0xff; + p[1] = (v >> 16) & 0xff; + p[2] = (v >> 8) & 0xff; + p[3] = v & 0xff; +} /* The glibc versions of ispace() and isdigit() are slow in UTF-8 locales. */ @@ -246,10 +288,17 @@ _cairo_isdigit (int c) return (c >= '0' && c <= '9'); } +static inline int cairo_const +_cairo_isalpha (int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + #include "cairo-types-private.h" #include "cairo-cache-private.h" #include "cairo-reference-count-private.h" #include "cairo-spans-private.h" +#include "cairo-surface-private.h" cairo_private void _cairo_box_from_doubles (cairo_box_t *box, @@ -265,82 +314,76 @@ cairo_private void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rectangle); -cairo_private cairo_bool_t -_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, - const cairo_rectangle_int_t *contained_rectangle); - cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle); +cairo_private void +_cairo_box_add_curve_to (cairo_box_t *extents, + const cairo_point_t *a, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d); + cairo_private void _cairo_boxes_get_extents (const cairo_box_t *boxes, int num_boxes, cairo_box_t *extents); +cairo_private extern const cairo_rectangle_int_t _cairo_empty_rectangle; +cairo_private extern const cairo_rectangle_int_t _cairo_unbounded_rectangle; + static inline void _cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect) { - rect->x = CAIRO_RECT_INT_MIN; - rect->y = CAIRO_RECT_INT_MIN; - rect->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; - rect->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN; + *rect = _cairo_unbounded_rectangle; } -cairo_private cairo_bool_t +cairo_private_no_warn cairo_bool_t _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, const cairo_rectangle_int_t *src); +static inline cairo_bool_t +_cairo_rectangle_intersects (const cairo_rectangle_int_t *dst, + const cairo_rectangle_int_t *src) +{ + return !(src->x >= dst->x + (int) dst->width || + src->x + (int) src->width <= dst->x || + src->y >= dst->y + (int) dst->height || + src->y + (int) src->height <= dst->y); +} + +static inline cairo_bool_t +_cairo_rectangle_contains_rectangle (const cairo_rectangle_int_t *a, + const cairo_rectangle_int_t *b) +{ + return (a->x <= b->x && + a->x + (int) a->width >= b->x + (int) b->width && + a->y <= b->y && + a->y + (int) a->height >= b->y + (int) b->height); +} + +cairo_private void +_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti, + const cairo_rectangle_t *rectf); + +/* Extends the dst rectangle to also contain src. + * If one of the rectangles is empty, the result is undefined + */ +cairo_private void +_cairo_rectangle_union (cairo_rectangle_int_t *dst, + const cairo_rectangle_int_t *src); + cairo_private cairo_bool_t -_cairo_box_intersects_line_segment (cairo_box_t *box, +_cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line) cairo_pure; cairo_private cairo_bool_t -_cairo_box_contains_point (cairo_box_t *box, - const cairo_point_t *point) cairo_pure; - -/* cairo-array.c structures and functions */ - -cairo_private void -_cairo_array_init (cairo_array_t *array, int element_size); - -cairo_private void -_cairo_array_init_snapshot (cairo_array_t *array, - const cairo_array_t *other); - -cairo_private void -_cairo_array_fini (cairo_array_t *array); - -cairo_private cairo_status_t -_cairo_array_grow_by (cairo_array_t *array, unsigned int additional); - -cairo_private void -_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements); - -cairo_private cairo_status_t -_cairo_array_append (cairo_array_t *array, const void *element); - -cairo_private cairo_status_t -_cairo_array_append_multiple (cairo_array_t *array, - const void *elements, - int num_elements); - -cairo_private cairo_status_t -_cairo_array_allocate (cairo_array_t *array, - unsigned int num_elements, - void **elements); - -cairo_private void * -_cairo_array_index (cairo_array_t *array, unsigned int index); - -cairo_private void -_cairo_array_copy_element (cairo_array_t *array, int index, void *dst); - -cairo_private int -_cairo_array_num_elements (cairo_array_t *array); - -cairo_private int -_cairo_array_size (cairo_array_t *array); +_cairo_spline_intersects (const cairo_point_t *a, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d, + const cairo_box_t *box) cairo_pure; typedef struct { const cairo_user_data_key_t *key; @@ -365,8 +408,8 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, cairo_destroy_func_t destroy); cairo_private cairo_status_t -_cairo_user_data_array_copy (cairo_user_data_array_t *dst, - cairo_user_data_array_t *src); +_cairo_user_data_array_copy (cairo_user_data_array_t *dst, + const cairo_user_data_array_t *src); cairo_private void _cairo_user_data_array_foreach (cairo_user_data_array_t *array, @@ -385,7 +428,10 @@ _cairo_hash_bytes (unsigned long hash, const void *bytes, unsigned int length); -#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash) +/* We use bits 24-27 to store phases for subpixel positions */ +#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash & 0xffffff) +#define _cairo_scaled_glyph_xphase(g) (int)(((g)->hash_entry.hash >> 24) & 3) +#define _cairo_scaled_glyph_yphase(g) (int)(((g)->hash_entry.hash >> 26) & 3) #define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) #include "cairo-scaled-font-private.h" @@ -400,7 +446,7 @@ struct _cairo_font_face { }; cairo_private void -_cairo_reset_static_data (void); +_cairo_default_context_reset_static_data (void); cairo_private void _cairo_toy_font_face_reset_static_data (void); @@ -411,10 +457,15 @@ _cairo_ft_font_reset_static_data (void); cairo_private void _cairo_win32_font_reset_static_data (void); +#if CAIRO_HAS_COGL_SURFACE +void +_cairo_cogl_context_reset_static_data (void); +#endif + /* the font backend interface */ struct _cairo_unscaled_font_backend { - void (*destroy) (void *unscaled_font); + cairo_bool_t (*destroy) (void *unscaled_font); }; /* #cairo_toy_font_face_t - simple family/slant/weight font faces used for @@ -435,7 +486,8 @@ typedef enum _cairo_scaled_glyph_info { CAIRO_SCALED_GLYPH_INFO_METRICS = (1 << 0), CAIRO_SCALED_GLYPH_INFO_SURFACE = (1 << 1), CAIRO_SCALED_GLYPH_INFO_PATH = (1 << 2), - CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3) + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE = (1 << 3), + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE = (1 << 4) } cairo_scaled_glyph_info_t; typedef struct _cairo_scaled_font_subset { @@ -447,12 +499,14 @@ typedef struct _cairo_scaled_font_subset { * Value of glyphs array is scaled_font_glyph_index. */ unsigned long *glyphs; - unsigned long *to_unicode; char **utf8; char **glyph_names; + int *to_latin_char; + unsigned long *latin_to_subset_glyph_index; unsigned int num_glyphs; cairo_bool_t is_composite; cairo_bool_t is_scaled; + cairo_bool_t is_latin; } cairo_scaled_font_subset_t; struct _cairo_scaled_font_backend { @@ -485,22 +539,25 @@ struct _cairo_scaled_font_backend { unsigned long (*ucs4_to_index) (void *scaled_font, uint32_t ucs4); - cairo_warn cairo_int_status_t - (*show_glyphs) (void *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region, - int *remaining_glyphs); + /* Read data from a sfnt font table. + * @scaled_font: font + * @tag: 4 byte table name specifying the table to read. + * @offset: offset into the table + * @buffer: buffer to write data into. Caller must ensure there is sufficient space. + * If NULL, return the size of the table in @length. + * @length: If @buffer is NULL, the size of the table will be returned in @length. + * If @buffer is not null, @length specifies the number of bytes to read. + * + * If less than @length bytes are available to read this function + * returns CAIRO_INT_STATUS_UNSUPPORTED. Note that requesting more + * bytes than are available in the table may continue reading data + * from the following table and return success. If this is + * undesirable the caller should first query the table size. If an + * error occurs the output value of @length is undefined. + * + * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a sfnt style font or table not found. + */ cairo_warn cairo_int_status_t (*load_truetype_table)(void *scaled_font, unsigned long tag, @@ -514,6 +571,55 @@ struct _cairo_scaled_font_backend { (*index_to_ucs4)(void *scaled_font, unsigned long index, uint32_t *ucs4); + + /* Determine if this scaled font differs from the outlines in the font tables. + * eg synthesized bold/italic or a non default variant of a variable font. + */ + cairo_warn cairo_int_status_t + (*is_synthetic)(void *scaled_font, + cairo_bool_t *is_synthetic); + + /* For type 1 fonts, return the glyph name for a given glyph index. + * A glyph index and list of glyph names in the Type 1 fonts is provided. + * The function returns the index of the glyph in the list of glyph names. + * @scaled_font: font + * @glyph_names: the names of each glyph in the Type 1 font in the + * order they appear in the CharStrings array + * @num_glyph_names: the number of names in the glyph_names array + * @glyph_index: the given glyph index + * @glyph_array_index: (index into glyph_names) the glyph name corresponding + * to the glyph_index + */ + + cairo_warn cairo_int_status_t + (*index_to_glyph_name)(void *scaled_font, + char **glyph_names, + int num_glyph_names, + unsigned long glyph_index, + unsigned long *glyph_array_index); + + /* Read data from a PostScript font. + * @scaled_font: font + * @offset: offset into the table + * @buffer: buffer to write data into. Caller must ensure there is sufficient space. + * If NULL, return the size of the table in @length. + * @length: If @buffer is NULL, the size of the table will be returned in @length. + * If @buffer is not null, @length specifies the number of bytes to read. + * + * If less than @length bytes are available to read this function + * returns CAIRO_INT_STATUS_UNSUPPORTED. If an error occurs the + * output value of @length is undefined. + * + * Returns CAIRO_INT_STATUS_UNSUPPORTED if not a Type 1 font. + */ + cairo_warn cairo_int_status_t + (*load_type1_data) (void *scaled_font, + long offset, + unsigned char *buffer, + unsigned long *length); + + cairo_bool_t + (*has_color_glyphs) (void *scaled_font); }; struct _cairo_font_face_backend { @@ -526,7 +632,7 @@ struct _cairo_font_face_backend { /* The destroy() function is allowed to resurrect the font face * by re-referencing. This is needed for the FreeType backend. */ - void + cairo_bool_t (*destroy) (void *font_face); cairo_warn cairo_status_t @@ -541,9 +647,6 @@ struct _cairo_font_face_backend { const cairo_matrix_t *font_matrix, const cairo_matrix_t *ctm, const cairo_font_options_t *options); - - void (*lock) (void *font_face); - void (*unlock) (void *font_face); }; extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend; @@ -561,313 +664,12 @@ extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_fac #endif -#if CAIRO_HAS_DWRITE_FONT - -extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend; - -#endif - #if CAIRO_HAS_QUARTZ_FONT extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; #endif -struct _cairo_surface_backend { - cairo_surface_type_t type; - - cairo_surface_t * - (*create_similar) (void *surface, - cairo_content_t content, - int width, - int height); - - cairo_warn cairo_status_t - (*finish) (void *surface); - - cairo_warn cairo_status_t - (*acquire_source_image) (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra); - - void - (*release_source_image) (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra); - - cairo_warn cairo_status_t - (*acquire_dest_image) (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra); - - void - (*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); - - /* Create a new surface (@clone_out) with the following - * characteristics: - * - * 1. It is as compatible as possible with @surface (in terms of - * efficiency) - * - * 2. It has the same contents as @src within the given rectangle. - * - * 3. The offset of the similar surface with respect to the original - * surface is returned in the clone_offset vector. - * - if you clone the entire surface, this vector is zero. - * - if you clone (src_x, src_y)x(w, h) the vector is (src_x, src_y); - */ - cairo_warn cairo_status_t - (*clone_similar) (void *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); - - /* XXX remove to a separate cairo_surface_compositor_t */ - /* XXX: dst should be the first argument for consistency */ - cairo_warn cairo_int_status_t - (*composite) (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - void *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - - cairo_warn cairo_int_status_t - (*fill_rectangles) (void *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - - /* XXX: dst should be the first argument for consistency */ - cairo_warn cairo_int_status_t - (*composite_trapezoids) (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *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 *region); - - cairo_warn cairo_span_renderer_t * - (*create_span_renderer) (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region); - - cairo_warn cairo_bool_t - (*check_span_renderer) (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *dst, - cairo_antialias_t antialias); - - cairo_warn cairo_int_status_t - (*copy_page) (void *surface); - - cairo_warn cairo_int_status_t - (*show_page) (void *surface); - - /* Get the extents of the current surface. For many surface types - * this will be as simple as { x=0, y=0, width=surface->width, - * height=surface->height}. - * - * If this function is not implemented, or if it returns - * FALSE the surface is considered to be - * boundless and infinite bounds are used for it. - */ - cairo_warn cairo_bool_t - (*get_extents) (void *surface, - cairo_rectangle_int_t *extents); - - /* - * This is an optional entry to let the surface manage its own glyph - * resources. If null, render against this surface, using image - * surfaces as glyphs. - */ - cairo_warn cairo_int_status_t - (*old_show_glyphs) (cairo_scaled_font_t *font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - void *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region); - - void - (*get_font_options) (void *surface, - cairo_font_options_t *options); - - cairo_warn cairo_status_t - (*flush) (void *surface); - - cairo_warn cairo_status_t - (*mark_dirty_rectangle) (void *surface, - int x, - int y, - int width, - int height); - - void - (*scaled_font_fini) (cairo_scaled_font_t *scaled_font); - - void - (*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - - /* OK, I'm starting over somewhat by defining the 5 top-level - * drawing operators for the surface backend here with consistent - * naming and argument-order conventions. */ - cairo_warn cairo_int_status_t - (*paint) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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, - cairo_clip_t *clip); - - cairo_warn cairo_int_status_t - (*stroke) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip); - - cairo_warn cairo_int_status_t - (*fill) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip); - - cairo_warn cairo_int_status_t - (*show_glyphs) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - - cairo_surface_t * - (*snapshot) (void *surface); - - cairo_bool_t - (*is_similar) (void *surface_a, - void *surface_b); - - cairo_warn cairo_int_status_t - (*fill_stroke) (void *surface, - 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, - 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, - cairo_clip_t *clip); - - cairo_surface_t * - (*create_solid_pattern_surface) - (void *surface, - const cairo_solid_pattern_t *solid_pattern); - - cairo_bool_t - (*can_repaint_solid_pattern_surface) - (void *surface, - const cairo_solid_pattern_t *solid_pattern); - - cairo_bool_t - (*has_show_text_glyphs) (void *surface); - - cairo_warn cairo_int_status_t - (*show_text_glyphs) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); -}; - -#include "cairo-surface-private.h" - -struct _cairo_image_surface { - cairo_surface_t base; - - pixman_format_code_t pixman_format; - cairo_format_t format; - unsigned char *data; - - int width; - int height; - int stride; - int depth; - - pixman_image_t *pixman_image; - - unsigned owns_data : 1; - unsigned transparency : 2; -}; - -extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend; - #define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE #define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD @@ -876,7 +678,7 @@ extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear; extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black; extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white; -typedef struct _cairo_surface_attributes { +struct _cairo_surface_attributes { cairo_matrix_t matrix; cairo_extend_t extend; cairo_filter_t filter; @@ -884,24 +686,7 @@ typedef struct _cairo_surface_attributes { int x_offset; int y_offset; void *extra; -} cairo_surface_attributes_t; - -typedef struct _cairo_traps { - cairo_status_t status; - - const cairo_box_t *limits; - int num_limits; - - unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */ - unsigned int has_intersections : 1; - unsigned int is_rectilinear : 1; - unsigned int is_rectangular : 1; - - int num_traps; - int traps_size; - cairo_trapezoid_t *traps; - cairo_trapezoid_t traps_embedded[16]; -} cairo_traps_t; +}; #define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL #define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL @@ -911,12 +696,7 @@ typedef struct _cairo_traps { #define CAIRO_FT_FONT_FAMILY_DEFAULT "" #define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" -#if CAIRO_HAS_DWRITE_FONT - -#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT -#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend - -#elif CAIRO_HAS_WIN32_FONT +#if CAIRO_HAS_WIN32_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend @@ -939,13 +719,7 @@ typedef struct _cairo_traps { #endif #define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER -#ifdef MOZ_GFX_OPTIMIZE_MOBILE -// Skia uses a tolerance of 0.5 we'll use something more -// tolerant for now -#define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.3 -#else #define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1 -#endif #define CAIRO_GSTATE_FILL_RULE_DEFAULT CAIRO_FILL_RULE_WINDING #define CAIRO_GSTATE_LINE_WIDTH_DEFAULT 2.0 #define CAIRO_GSTATE_LINE_CAP_DEFAULT CAIRO_LINE_CAP_BUTT @@ -961,7 +735,9 @@ typedef struct _cairo_stroke_face { cairo_point_t point; cairo_point_t cw; cairo_slope_t dev_vector; + cairo_point_double_t dev_slope; cairo_point_double_t usr_vector; + double length; } cairo_stroke_face_t; /* cairo.c */ @@ -978,7 +754,7 @@ _cairo_restrict_value (double value, double min, double max) } /* C99 round() rounds to the nearest integral value with halfway cases rounded - * away from 0. _cairo_round rounds halfway cases toward negative infinity. + * away from 0. _cairo_round rounds halfway cases toward positive infinity. * This matches the rounding behaviour of _cairo_lround. */ static inline double cairo_const _cairo_round (double r) @@ -986,11 +762,15 @@ _cairo_round (double r) return floor (r + .5); } -#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L +#if DISABLE_SOME_FLOATING_POINT cairo_private int _cairo_lround (double d) cairo_const; #else -#define _cairo_lround lround +static inline int cairo_const +_cairo_lround (double r) +{ + return _cairo_round (r); +} #endif cairo_private uint16_t @@ -1020,13 +800,6 @@ _cairo_stock_color (cairo_stock_t stock) cairo_pure; cairo_private uint16_t _cairo_color_double_to_short (double d) cairo_const; -cairo_private void -_cairo_color_init (cairo_color_t *color); - -cairo_private void -_cairo_color_init_rgb (cairo_color_t *color, - double red, double green, double blue); - cairo_private void _cairo_color_init_rgba (cairo_color_t *color, double red, double green, double blue, @@ -1064,11 +837,15 @@ _cairo_color_get_content (const cairo_color_t *color) cairo_pure; /* cairo-font-face.c */ extern const cairo_private cairo_font_face_t _cairo_font_face_nil; +extern const cairo_private cairo_font_face_t _cairo_font_face_nil_file_not_found; cairo_private void _cairo_font_face_init (cairo_font_face_t *font_face, const cairo_font_face_backend_t *backend); +cairo_private cairo_bool_t +_cairo_font_face_destroy (void *abstract_face); + cairo_private cairo_status_t _cairo_font_face_set_error (cairo_font_face_t *font_face, cairo_status_t status); @@ -1106,6 +883,13 @@ cairo_private void _cairo_font_options_init_copy (cairo_font_options_t *options, const cairo_font_options_t *other); +cairo_private void +_cairo_font_options_fini (cairo_font_options_t *options); + +cairo_private void +_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, + cairo_lcd_filter_t lcd_filter); + cairo_private cairo_lcd_filter_t _cairo_font_options_get_lcd_filter (const cairo_font_options_t *options); @@ -1134,12 +918,21 @@ _cairo_validate_text_clusters (const char *utf8, int num_clusters, cairo_text_cluster_flags_t cluster_flags); +cairo_private unsigned long +_cairo_string_hash (const char *str, int len); + cairo_private cairo_status_t _cairo_intern_string (const char **str_inout, int len); cairo_private void _cairo_intern_string_reset_static_data (void); +cairo_private const char * +_cairo_get_locale_decimal_point (void); + +cairo_private double +_cairo_strtod (const char *nptr, char **endptr); + /* cairo-path-fixed.c */ cairo_private cairo_path_fixed_t * _cairo_path_fixed_create (void); @@ -1151,10 +944,6 @@ cairo_private cairo_status_t _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, const cairo_path_fixed_t *other); -cairo_private cairo_bool_t -_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path, - const cairo_path_fixed_t *other); - cairo_private void _cairo_path_fixed_fini (cairo_path_fixed_t *path); @@ -1223,7 +1012,6 @@ typedef cairo_status_t cairo_private cairo_status_t _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, - cairo_direction_t dir, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_curve_to_func_t *curve_to, @@ -1232,13 +1020,17 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path, cairo_private cairo_status_t _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, - cairo_direction_t dir, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_close_path_func_t *close_path, void *closure, double tolerance); + +cairo_private cairo_bool_t +_cairo_path_bounder_extents (const cairo_path_fixed_t *path, + cairo_box_t *box); + cairo_private cairo_bool_t _cairo_path_fixed_extents (const cairo_path_fixed_t *path, cairo_box_t *box); @@ -1261,6 +1053,7 @@ cairo_private void _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, + cairo_bool_t vector, cairo_rectangle_int_t *extents); cairo_private cairo_status_t @@ -1297,14 +1090,15 @@ _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, double tolerance, cairo_polygon_t *polygon); -cairo_private cairo_int_status_t -_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps); +cairo_private cairo_status_t +_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path, + cairo_antialias_t antialias, + cairo_polygon_t *polygon); cairo_private cairo_status_t _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias, cairo_boxes_t *boxes); cairo_private cairo_region_t * @@ -1328,18 +1122,29 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, cairo_polygon_t *polygon); cairo_private cairo_int_status_t -_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - cairo_traps_t *traps); +_cairo_path_fixed_stroke_to_tristrip (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_tristrip_t *strip); + +cairo_private cairo_status_t +_cairo_path_fixed_stroke_dashed_to_polygon (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_polygon_t *polygon); cairo_private cairo_int_status_t _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, + cairo_antialias_t antialias, cairo_boxes_t *boxes); -cairo_private cairo_status_t +cairo_private cairo_int_status_t _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, @@ -1347,6 +1152,14 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, double tolerance, cairo_traps_t *traps); +cairo_private cairo_int_status_t +_cairo_path_fixed_stroke_polygon_to_traps (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_traps_t *traps); + cairo_private cairo_status_t _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, @@ -1417,7 +1230,7 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, cairo_rectangle_int_t *extents, cairo_bool_t *overlap); -cairo_private void +cairo_private cairo_bool_t _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, const cairo_glyph_t *glyphs, int num_glyphs, @@ -1464,6 +1277,11 @@ _cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, cairo_scaled_font_t *scaled_font, cairo_surface_t *recording_surface); +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_private cairo_int_status_t _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, unsigned long index, @@ -1490,8 +1308,20 @@ _cairo_stroke_style_fini (cairo_stroke_style_t *style); cairo_private void _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, + const cairo_path_fixed_t *path, const cairo_matrix_t *ctm, double *dx, double *dy); +cairo_private void +_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style, + const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm, + double *dx, double *dy); + +cairo_private void +_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style, + const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm, + double *dx, double *dy); cairo_private double _cairo_stroke_style_dash_period (const cairo_stroke_style_t *style); @@ -1515,16 +1345,16 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, /* cairo-surface.c */ -cairo_private cairo_surface_t * -_cairo_surface_create_in_error (cairo_status_t status); +cairo_private cairo_bool_t +_cairo_surface_has_mime_image (cairo_surface_t *surface); cairo_private cairo_status_t _cairo_surface_copy_mime_data (cairo_surface_t *dst, cairo_surface_t *src); -cairo_private cairo_status_t +cairo_private_no_warn cairo_int_status_t _cairo_surface_set_error (cairo_surface_t *surface, - cairo_status_t status); + cairo_int_status_t status); cairo_private void _cairo_surface_set_resolution (cairo_surface_t *surface, @@ -1532,87 +1362,47 @@ _cairo_surface_set_resolution (cairo_surface_t *surface, double y_res); cairo_private cairo_surface_t * -_cairo_surface_create_similar_scratch (cairo_surface_t *other, - cairo_content_t content, - int width, - int height); +_cairo_surface_create_for_rectangle_int (cairo_surface_t *target, + const cairo_rectangle_int_t *extents); cairo_private cairo_surface_t * -_cairo_surface_create_similar_solid (cairo_surface_t *other, - cairo_content_t content, - int width, - int height, - const cairo_color_t *color, - cairo_bool_t allow_fallback); - -cairo_private cairo_surface_t * -_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other, - const cairo_solid_pattern_t *solid_pattern); - -cairo_private cairo_int_status_t -_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other, - cairo_surface_t *solid_surface, - const cairo_solid_pattern_t *solid_pattern); +_cairo_surface_create_scratch (cairo_surface_t *other, + cairo_content_t content, + int width, + int height, + const cairo_color_t *color); cairo_private void _cairo_surface_init (cairo_surface_t *surface, const cairo_surface_backend_t *backend, cairo_device_t *device, - cairo_content_t content); + cairo_content_t content, + cairo_bool_t is_vector); cairo_private void _cairo_surface_set_font_options (cairo_surface_t *surface, cairo_font_options_t *options); -cairo_private cairo_status_t -_cairo_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_fill_rectangle (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - int x, - int y, - int width, - int height); - -cairo_private cairo_status_t -_cairo_surface_fill_region (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_region_t *region); - -cairo_private cairo_status_t -_cairo_surface_fill_rectangles (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - cairo_private cairo_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip); + const cairo_clip_t *clip); + +cairo_private cairo_image_surface_t * +_cairo_surface_map_to_image (cairo_surface_t *surface, + const cairo_rectangle_int_t *extents); + +cairo_private_no_warn cairo_int_status_t +_cairo_surface_unmap_image (cairo_surface_t *surface, + cairo_image_surface_t *image); cairo_private cairo_status_t _cairo_surface_mask (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fill_stroke (cairo_surface_t *surface, @@ -1629,29 +1419,29 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, const cairo_matrix_t *stroke_ctm_inverse, double stroke_tolerance, cairo_antialias_t stroke_antialias, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_stroke (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_fill (cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_show_text_glyphs (cairo_surface_t *surface, @@ -1665,85 +1455,18 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); + const cairo_clip_t *clip); cairo_private cairo_status_t -_cairo_surface_paint_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_mask_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_stroke_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - 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_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_fill_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_glyphs_extents (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *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 ntraps, - cairo_region_t *clip_region); - -cairo_private cairo_span_renderer_t * -_cairo_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region); - -cairo_private cairo_bool_t -_cairo_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *dst, - cairo_antialias_t antialias); +_cairo_surface_tag (cairo_surface_t *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + const cairo_clip_t *clip); cairo_private cairo_status_t _cairo_surface_acquire_source_image (cairo_surface_t *surface, @@ -1755,106 +1478,27 @@ _cairo_surface_release_source_image (cairo_surface_t *surface, cairo_image_surface_t *image, void *image_extra); -cairo_private cairo_status_t -_cairo_surface_acquire_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra); - -cairo_private void -_cairo_surface_release_dest_image (cairo_surface_t *surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra); - -cairo_private cairo_status_t -_cairo_surface_clone_similar (cairo_surface_t *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_private cairo_surface_t * _cairo_surface_snapshot (cairo_surface_t *surface); +cairo_private void +_cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func); + cairo_private cairo_surface_t * _cairo_surface_has_snapshot (cairo_surface_t *surface, - const cairo_surface_backend_t *backend); - -cairo_private cairo_bool_t -_cairo_surface_is_similar (cairo_surface_t *surface_a, - cairo_surface_t *surface_b); - -cairo_private cairo_bool_t -_cairo_surface_get_extents (cairo_surface_t *surface, - cairo_rectangle_int_t *extents); - -cairo_private cairo_status_t -_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - cairo_surface_attributes_t *mask_attr, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_status_t -_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst, - cairo_surface_attributes_t *src_attr, - int src_width, - int src_height, - int mask_width, - int mask_height, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region); - -cairo_private cairo_bool_t -_cairo_surface_is_opaque (const cairo_surface_t *surface); - -cairo_private int -_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface); + const cairo_surface_backend_t *backend); cairo_private void -_cairo_surface_set_device_scale (cairo_surface_t *surface, - double sx, - double sy); +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot); + +cairo_private cairo_status_t +_cairo_surface_begin_modification (cairo_surface_t *surface); + +cairo_private_no_warn cairo_bool_t +_cairo_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_int_t *extents); cairo_private cairo_bool_t _cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure; @@ -1877,11 +1521,11 @@ _cairo_surface_release_device_reference (cairo_surface_t *surface); * for that, even without being considered "valid" for the sake of * things like cairo_image_surface_create(). * - * Since 1.2.0 we ran into the same situtation with X servers with BGR + * Since 1.2.0 we ran into the same situation with X servers with BGR * visuals. This time we invented #cairo_internal_format_t instead, * (see it for more discussion). * - * The punchline is that %CAIRO_FORMAT_VALID must not conside any + * The punchline is that %CAIRO_FORMAT_VALID must not consider any * internal format to be valid. Also we need to decide if the * RGB16_565 should be moved to instead be an internal format. If so, * this macro need not change for it. (We probably will need to leave @@ -1895,7 +1539,7 @@ _cairo_surface_release_device_reference (cairo_surface_t *surface); * in cairo-xlib-surface.c--again see -Wswitch-enum). */ #define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ - (format) <= CAIRO_FORMAT_RGB16_565) + (format) <= CAIRO_FORMAT_RGBA128F) /* pixman-required stride alignment in bytes. */ #define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) @@ -1908,12 +1552,6 @@ _cairo_surface_release_device_reference (cairo_surface_t *surface); CAIRO_CONTENT_COLOR_ALPHA))\ == 0)) -static inline cairo_bool_t -_cairo_valid_stride_alignment(int stride) -{ - return !(stride & (CAIRO_STRIDE_ALIGNMENT-1)); -} - cairo_private int _cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; @@ -1944,9 +1582,16 @@ cairo_private cairo_bool_t _pixman_format_to_masks (pixman_format_code_t pixman_format, cairo_format_masks_t *masks); +cairo_private void +_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + cairo_private void _cairo_image_reset_static_data (void); +cairo_private void +_cairo_image_compositor_reset_static_data (void); + cairo_private cairo_surface_t * _cairo_image_surface_create_with_pixman_format (unsigned char *data, pixman_format_code_t pixman_format, @@ -1969,23 +1614,18 @@ cairo_private cairo_image_surface_t * _cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, cairo_format_t format); -cairo_private void -_cairo_image_surface_span_render_row (int y, - const cairo_half_open_span_t *spans, - unsigned num_spans, - uint8_t *data, - uint32_t stride); - cairo_private cairo_image_transparency_t _cairo_image_analyze_transparency (cairo_image_surface_t *image); -cairo_private cairo_bool_t -_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure; - -cairo_private cairo_bool_t -_cairo_surface_is_recording (const cairo_surface_t *surface) cairo_pure; +cairo_private cairo_image_color_t +_cairo_image_analyze_color (cairo_image_surface_t *image); /* cairo-pen.c */ +cairo_private int +_cairo_pen_vertices_needed (double tolerance, + double radius, + const cairo_matrix_t *matrix); + cairo_private cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, @@ -2004,13 +1644,6 @@ _cairo_pen_fini (cairo_pen_t *pen); cairo_private cairo_status_t _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points); -cairo_private cairo_status_t -_cairo_pen_add_points_for_slopes (cairo_pen_t *pen, - cairo_point_t *a, - cairo_point_t *b, - cairo_point_t *c, - cairo_point_t *d); - cairo_private int _cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope); @@ -2019,39 +1652,88 @@ cairo_private int _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope); -/* cairo-polygon.c */ cairo_private void -_cairo_polygon_init (cairo_polygon_t *polygon); +_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen, + const cairo_slope_t *in, + const cairo_slope_t *out, + int *start, int *stop); cairo_private void -_cairo_polygon_limit (cairo_polygon_t *polygon, - const cairo_box_t *boxes, - int num_boxes); +_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen, + const cairo_slope_t *in, + const cairo_slope_t *out, + int *start, int *stop); + +/* cairo-polygon.c */ +cairo_private void +_cairo_polygon_init (cairo_polygon_t *polygon, + const cairo_box_t *boxes, + int num_boxes); + +cairo_private void +_cairo_polygon_init_with_clip (cairo_polygon_t *polygon, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_polygon_init_boxes (cairo_polygon_t *polygon, + const cairo_boxes_t *boxes); + +cairo_private cairo_status_t +_cairo_polygon_init_box_array (cairo_polygon_t *polygon, + cairo_box_t *boxes, + int num_boxes); + +cairo_private void +_cairo_polygon_limit (cairo_polygon_t *polygon, + const cairo_box_t *limits, + int num_limits); + +cairo_private void +_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon, + const cairo_clip_t *clip); cairo_private void _cairo_polygon_fini (cairo_polygon_t *polygon); -cairo_private cairo_status_t +cairo_private_no_warn cairo_status_t _cairo_polygon_add_line (cairo_polygon_t *polygon, const cairo_line_t *line, int top, int bottom, int dir); -cairo_private cairo_status_t +cairo_private_no_warn cairo_status_t _cairo_polygon_add_external_edge (void *polygon, const cairo_point_t *p1, const cairo_point_t *p2); -cairo_private cairo_status_t -_cairo_polygon_move_to (cairo_polygon_t *polygon, - const cairo_point_t *point); +cairo_private_no_warn cairo_status_t +_cairo_polygon_add_contour (cairo_polygon_t *polygon, + const cairo_contour_t *contour); + +cairo_private void +_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy); cairo_private cairo_status_t -_cairo_polygon_line_to (cairo_polygon_t *polygon, - const cairo_point_t *point); +_cairo_polygon_reduce (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule); cairo_private cairo_status_t -_cairo_polygon_close (cairo_polygon_t *polygon); +_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a, + cairo_polygon_t *b, int winding_b); + +cairo_private cairo_status_t +_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon, + cairo_fill_rule_t *winding, + cairo_box_t *boxes, + int num_boxes); + +static inline cairo_bool_t +_cairo_polygon_is_empty (const cairo_polygon_t *polygon) +{ + return + polygon->num_edges == 0 || + polygon->extents.p2.x <= polygon->extents.p1.x; +} #define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status @@ -2103,11 +1785,26 @@ cairo_private cairo_status_t _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, double *sx, double *sy, int x_major); -cairo_private cairo_bool_t -_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure; +static inline cairo_bool_t +_cairo_matrix_is_identity (const cairo_matrix_t *matrix) +{ + return (matrix->xx == 1.0 && matrix->yx == 0.0 && + matrix->xy == 0.0 && matrix->yy == 1.0 && + matrix->x0 == 0.0 && matrix->y0 == 0.0); +} -cairo_private cairo_bool_t -_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure; +static inline cairo_bool_t +_cairo_matrix_is_translation (const cairo_matrix_t *matrix) +{ + return (matrix->xx == 1.0 && matrix->yx == 0.0 && + matrix->xy == 0.0 && matrix->yy == 1.0); +} + +static inline cairo_bool_t +_cairo_matrix_is_scale (const cairo_matrix_t *matrix) +{ + return matrix->yx == 0.0 && matrix->xy == 0.0; +} cairo_private cairo_bool_t _cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix, @@ -2119,55 +1816,30 @@ _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix); cairo_private cairo_bool_t _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure; -cairo_private void -_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix, - double radius, - double *major, - double *minor); - cairo_private double _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, double radius) cairo_pure; -cairo_private void -_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform, - double xc, - double yc); - -/* cairo-traps.c */ -cairo_private void -_cairo_traps_init (cairo_traps_t *traps); - -cairo_private void -_cairo_traps_limit (cairo_traps_t *traps, - const cairo_box_t *boxes, - int num_boxes); +cairo_private cairo_bool_t +_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, + cairo_filter_t filter, + int *out_x_offset, + int *out_y_offset); cairo_private cairo_status_t -_cairo_traps_init_boxes (cairo_traps_t *traps, - const cairo_boxes_t *boxes); +_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, + double yc, + pixman_transform_t *out_transform, + int *out_x_offset, + int *out_y_offset); cairo_private void -_cairo_traps_clear (cairo_traps_t *traps); +_cairo_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix); cairo_private void -_cairo_traps_fini (cairo_traps_t *traps); - -#define _cairo_traps_status(T) (T)->status - -cairo_private void -_cairo_traps_translate (cairo_traps_t *traps, int x, int y); - -cairo_private cairo_status_t -_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, - const cairo_point_t *top_left, - const cairo_point_t *bottom_right); - -cairo_private void -_cairo_traps_add_trap (cairo_traps_t *traps, - cairo_fixed_t top, cairo_fixed_t bottom, - cairo_line_t *left, cairo_line_t *right); +_cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect); cairo_private cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps, @@ -2201,22 +1873,6 @@ _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_poly cairo_fill_rule_t fill_rule, cairo_boxes_t *boxes); -cairo_private int -_cairo_traps_contain (const cairo_traps_t *traps, - double x, double y); - -cairo_private void -_cairo_traps_extents (const cairo_traps_t *traps, - cairo_box_t *extents); - -cairo_private cairo_int_status_t -_cairo_traps_extract_region (cairo_traps_t *traps, - cairo_region_t **region); - -cairo_private cairo_status_t -_cairo_traps_path (const cairo_traps_t *traps, - cairo_path_fixed_t *path); - cairo_private void _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, cairo_trapezoid_t *src_traps, @@ -2224,142 +1880,6 @@ _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, double tx, double ty, double sx, double sy); -/* cairo-pattern.c */ - -cairo_private cairo_pattern_t * -_cairo_pattern_create_in_error (cairo_status_t status); - -cairo_private cairo_status_t -_cairo_pattern_create_copy (cairo_pattern_t **pattern, - const cairo_pattern_t *other); - -cairo_private cairo_status_t -_cairo_pattern_init_copy (cairo_pattern_t *pattern, - const cairo_pattern_t *other); - -cairo_private void -_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, - const cairo_pattern_t *other); - -cairo_private cairo_status_t -_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, - const cairo_pattern_t *other); - -cairo_private void -_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, - const cairo_color_t *color); - -cairo_private void -_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, - cairo_surface_t *surface); - -cairo_private void -_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, - double x0, double y0, double x1, double y1); - -cairo_private void -_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, - double cx0, double cy0, double radius0, - double cx1, double cy1, double radius1); - -cairo_private void -_cairo_pattern_fini (cairo_pattern_t *pattern); - -cairo_private cairo_pattern_t * -_cairo_pattern_create_solid (const cairo_color_t *color); - -cairo_private void -_cairo_pattern_transform (cairo_pattern_t *pattern, - const cairo_matrix_t *ctm_inverse); - -cairo_private cairo_bool_t -_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, - const cairo_rectangle_int_t *extents, - cairo_color_t *color); - -cairo_private cairo_bool_t -_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern); - -cairo_private cairo_bool_t -_cairo_pattern_is_opaque (const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents); - -cairo_private cairo_bool_t -_cairo_pattern_is_clear (const cairo_pattern_t *pattern); - -cairo_private_no_warn cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, - double *pad_out); - -enum { - CAIRO_PATTERN_ACQUIRE_NONE = 0x0, - CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1 -}; -cairo_private cairo_int_status_t -_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, - cairo_surface_t *dst, - int x, - int y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **surface_out, - cairo_surface_attributes_t *attributes); - -cairo_private void -_cairo_pattern_release_surface (const cairo_pattern_t *pattern, - cairo_surface_t *surface, - cairo_surface_attributes_t *attributes); - -cairo_private cairo_int_status_t -_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, - const cairo_pattern_t *mask, - cairo_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - unsigned int flags, - cairo_surface_t **src_out, - cairo_surface_t **mask_out, - cairo_surface_attributes_t *src_attributes, - cairo_surface_attributes_t *mask_attributes); - -cairo_private void -_cairo_pattern_get_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents); - -cairo_private unsigned long -_cairo_pattern_hash (const cairo_pattern_t *pattern); - -cairo_private unsigned long -_cairo_linear_pattern_hash (unsigned long hash, - const cairo_linear_pattern_t *linear); - -cairo_private unsigned long -_cairo_radial_pattern_hash (unsigned long hash, - const cairo_radial_pattern_t *radial); - -cairo_private cairo_bool_t -_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, - const cairo_linear_pattern_t *b); - -cairo_private unsigned long -_cairo_pattern_size (const cairo_pattern_t *pattern); - -cairo_private cairo_bool_t -_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, - const cairo_radial_pattern_t *b); - -cairo_private cairo_bool_t -_cairo_pattern_equal (const cairo_pattern_t *a, - const cairo_pattern_t *b); - -cairo_private void -_cairo_pattern_reset_static_data (void); - #if CAIRO_HAS_DRM_SURFACE cairo_private void @@ -2370,6 +1890,9 @@ _cairo_drm_device_reset_static_data (void); cairo_private void _cairo_clip_reset_static_data (void); +cairo_private void +_cairo_pattern_reset_static_data (void); + /* cairo-unicode.c */ cairo_private int @@ -2386,7 +1909,11 @@ cairo_private int _cairo_ucs4_to_utf8 (uint32_t unicode, char *utf8); -#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS || CAIRO_HAS_DW_FONT +cairo_private int +_cairo_ucs4_to_utf16 (uint32_t unicode, + uint16_t *utf16); + +#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS # define CAIRO_HAS_UTF8_TO_UTF16 1 #endif #if CAIRO_HAS_UTF8_TO_UTF16 @@ -2397,11 +1924,20 @@ _cairo_utf8_to_utf16 (const char *str, int *items_written); #endif +cairo_private void +_cairo_matrix_multiply (cairo_matrix_t *r, + const cairo_matrix_t *a, + const cairo_matrix_t *b); + /* cairo-observer.c */ cairo_private void _cairo_observers_notify (cairo_list_t *observers, void *arg); +/* Open a file with a UTF-8 filename */ +cairo_private cairo_status_t +_cairo_fopen (const char *filename, const char *mode, FILE **file_out); + /* Avoid unnecessary PLT entries. */ slim_hidden_proto (cairo_clip_preserve); slim_hidden_proto (cairo_close_path); @@ -2419,13 +1955,13 @@ slim_hidden_proto (cairo_font_options_merge); slim_hidden_proto (cairo_font_options_set_antialias); slim_hidden_proto (cairo_font_options_set_hint_metrics); slim_hidden_proto (cairo_font_options_set_hint_style); -slim_hidden_proto (cairo_font_options_set_lcd_filter); slim_hidden_proto (cairo_font_options_set_subpixel_order); slim_hidden_proto (cairo_font_options_status); slim_hidden_proto (cairo_format_stride_for_width); slim_hidden_proto (cairo_get_current_point); slim_hidden_proto (cairo_get_line_width); slim_hidden_proto (cairo_get_matrix); +slim_hidden_proto (cairo_get_scaled_font); slim_hidden_proto (cairo_get_target); slim_hidden_proto (cairo_get_tolerance); slim_hidden_proto (cairo_glyph_allocate); @@ -2453,15 +1989,26 @@ slim_hidden_proto (cairo_matrix_translate); slim_hidden_proto (cairo_move_to); slim_hidden_proto (cairo_new_path); slim_hidden_proto (cairo_paint); +slim_hidden_proto (cairo_pattern_add_color_stop_rgba); slim_hidden_proto (cairo_pattern_create_for_surface); slim_hidden_proto (cairo_pattern_create_rgb); slim_hidden_proto (cairo_pattern_create_rgba); slim_hidden_proto (cairo_pattern_destroy); slim_hidden_proto (cairo_pattern_get_extend); +slim_hidden_proto (cairo_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_no_warn (cairo_pattern_reference); slim_hidden_proto (cairo_pattern_set_matrix); slim_hidden_proto (cairo_pop_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_rel_line_to); slim_hidden_proto (cairo_restore); slim_hidden_proto (cairo_save); @@ -2473,13 +2020,13 @@ 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_hint_metrics); 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_text_to_glyphs); +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_line_cap); @@ -2495,25 +2042,25 @@ slim_hidden_proto (cairo_status); slim_hidden_proto (cairo_stroke); slim_hidden_proto (cairo_stroke_preserve); slim_hidden_proto (cairo_surface_copy_page); +slim_hidden_proto (cairo_surface_create_similar_image); slim_hidden_proto (cairo_surface_destroy); slim_hidden_proto (cairo_surface_finish); slim_hidden_proto (cairo_surface_flush); -slim_hidden_proto (cairo_surface_get_content); slim_hidden_proto (cairo_surface_get_device_offset); +slim_hidden_proto (cairo_surface_get_device_scale); slim_hidden_proto (cairo_surface_get_font_options); slim_hidden_proto (cairo_surface_get_mime_data); -slim_hidden_proto (cairo_surface_get_type); slim_hidden_proto (cairo_surface_has_show_text_glyphs); -slim_hidden_proto (cairo_surface_set_subpixel_antialiasing); -slim_hidden_proto (cairo_surface_get_subpixel_antialiasing); slim_hidden_proto (cairo_surface_mark_dirty); slim_hidden_proto (cairo_surface_mark_dirty_rectangle); slim_hidden_proto_no_warn (cairo_surface_reference); slim_hidden_proto (cairo_surface_set_device_offset); +slim_hidden_proto (cairo_surface_set_device_scale); slim_hidden_proto (cairo_surface_set_fallback_resolution); slim_hidden_proto (cairo_surface_set_mime_data); slim_hidden_proto (cairo_surface_show_page); slim_hidden_proto (cairo_surface_status); +slim_hidden_proto (cairo_surface_supports_mime_type); slim_hidden_proto (cairo_text_cluster_allocate); slim_hidden_proto (cairo_text_cluster_free); slim_hidden_proto (cairo_toy_font_face_create); @@ -2525,6 +2072,7 @@ slim_hidden_proto (cairo_user_font_face_create); slim_hidden_proto (cairo_user_font_face_set_init_func); slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); +slim_hidden_proto (cairo_device_to_user); slim_hidden_proto (cairo_user_to_device); slim_hidden_proto (cairo_user_to_device_distance); slim_hidden_proto (cairo_version_string); @@ -2558,10 +2106,6 @@ slim_hidden_proto (cairo_surface_write_to_png_stream); #endif -cairo_private_no_warn cairo_filter_t -_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, - double *pad_out); - CAIRO_END_DECLS #include "cairo-mutex-private.h" @@ -2586,9 +2130,23 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); #endif cairo_private void -_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path); +_cairo_debug_print_path (FILE *stream, const cairo_path_fixed_t *path); cairo_private void -_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip); +_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon); + +cairo_private void +_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps); + +cairo_private void +_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip); + +#if 0 +#define TRACE(x) fprintf (stderr, "%s: ", __FILE__), fprintf x +#define TRACE_(x) x +#else +#define TRACE(x) +#define TRACE_(x) +#endif #endif diff --git a/gfx/cairo/cairo/src/check-def.sh b/gfx/cairo/cairo/src/check-def.sh new file mode 100755 index 000000000000..beefb46a3336 --- /dev/null +++ b/gfx/cairo/cairo/src/check-def.sh @@ -0,0 +1,48 @@ +#!/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.*\\ /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/gfx/cairo/cairo/src/check-doc-syntax.awk b/gfx/cairo/cairo/src/check-doc-syntax.awk new file mode 100644 index 000000000000..1fa8b8d22324 --- /dev/null +++ b/gfx/cairo/cairo/src/check-doc-syntax.awk @@ -0,0 +1,106 @@ +BEGIN { + name_found = 1 + SECTION_DOC = 0 + PUBLIC_DOC = 1 + PRIVATE_DOC = 2 +} + +function log_msg(severity, msg) +{ + printf "%s (%d): %s: %s %s\n", FILENAME, FNR, severity, doc_name, msg +} + +function log_error(msg) +{ + log_msg("ERROR", msg) +} + +function log_warning(msg) +{ + log_msg("WARNING", msg) +} + +/^\/\*\*$/ { + in_doc = 1 + doc_line = 0 +} + +/^(\/\*\*$| \*[ \t]| \*$| \*\*\/$)/ { + valid_doc = 1 +} + +in_doc { + if (!valid_doc) + log_error("bad line: '" $0 "'") + valid_doc = 0 + + doc_line++ + if (doc_line == 2) { + # new doc name. Did we find the previous one? + # (macros are not expected to be found in the same place as + # their documentation) + if (!name_found && doc_name !~ /CAIRO_/) + log_warning("not found") + doc_name = $2 + if (doc_name ~ /^SECTION:.*$/) { + doc_type = SECTION_DOC + name_found = 1 + } else if (tolower(doc_name) ~ /^cairo_[a-z0-9_]*:$/) { + doc_type = PUBLIC_DOC + name_found = 0 + real_name = substr(doc_name, 1, length(doc_name) - 1) + } else if (tolower(doc_name) ~ /^_[a-z0-9_]*:$/) { + doc_type = PRIVATE_DOC + name_found = 0 + real_name = substr(doc_name, 1, length(doc_name) - 1) + } else { + log_error("invalid doc id (should be 'cairo_...:')") + name_found = 1 + } + } +} + +!in_doc { + regex = "(^|[ \\t\\*])" real_name "([ ;()]|$)" + if ($0 ~ regex) + name_found = 1 +} + +/^ \* Since: ([0-9]*.[0-9]*|TBD)$/ { + if (doc_has_since != 0) { + log_error("Duplicate 'Since' field") + } + doc_has_since = doc_line +} + +/^ \*\*\// { + if (doc_type == PUBLIC_DOC) { + if (!doc_has_since) { + # private types can start with cairo_ + if (doc_name ~ /^cairo_.*_t:$/) + log_warning("missing 'Since' field (is it a private type?)") + else + log_error("missing 'Since' field") + } else if (doc_has_since != doc_line - 1) + log_warning("misplaced 'Since' field (should be right before the end of the comment)") + } else { + if (doc_has_since) + log_warning("'Since' field in non-public element") + } + + in_doc = 0 + doc_has_since = 0 + doc_type = 0 +} + +/\*\// { + if (in_doc) { + in_doc = 0 + log_error("documentation comment not closed with **/") + } +} + +END { + if (!name_found) + log_warning("not found") +} diff --git a/gfx/cairo/cairo/src/check-doc-syntax.sh b/gfx/cairo/cairo/src/check-doc-syntax.sh new file mode 100755 index 000000000000..762a48429b02 --- /dev/null +++ b/gfx/cairo/cairo/src/check-doc-syntax.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +if grep --version 2>/dev/null | grep GNU >/dev/null; then + : +else + echo "GNU grep not found; skipping test" + exit 0 +fi + +test -z "$srcdir" && srcdir=. +stat=0 + +echo Checking documentation for incorrect syntax + +cd "$srcdir" + +if test "x$SGML_DOCS" = x; then + FILES=$all_cairo_files + if test "x$FILES" = x; then + FILES=`find . -name 'cairo*.h' -or -name 'cairo*.c' -or -name 'cairo*.cpp'` + fi +fi + +enum_regexp="\([^%@']\|^\)\<\(FALSE\|TRUE\|NULL\|CAIRO_[0-9A-Z_]*\)\($\|[^(A-Za-z0-9_]\)" +if test "x$SGML_DOCS" = x; then + enum_regexp='^[^:]*:[/ ][*]\(\|[ \t].*\)'$enum_regexp\($\|[^:]\) +fi +if echo $FILES | xargs grep . /dev/null | sed -e '//,/<\/programlisting>/d' | grep "$enum_regexp" | grep -v '#####'; then + stat=1 + echo Error: some macros in the docs are not prefixed by percent sign. + echo Fix this by searching for the following regexp in the above files: + echo " '$enum_regexp'" +fi >&2 + +type_regexp="\( .*[^#']\| \|^\)\\($\|[^:]$\|[^:].\)" +if test "x$SGML_DOCS" = x; then + type_regexp='^[^:]*:[/ ][*]'$type_regexp +else + type_regexp='\(.'$type_regexp'\)\|\('$type_regexp'.\)' +fi + +if echo $FILES | xargs grep . /dev/null | sed -e '//,/<\/programlisting>/d' | grep -v "@Title" | grep "$type_regexp" | grep -v '#####'; then + stat=1 + echo Error: some type names in the docs are not prefixed by hash sign, + echo neither are the only token in the doc line followed by colon. + echo Fix this by searching for the following regexp in the above files: + echo " '$type_regexp'" +fi >&2 + +func_regexp="\([^#']\|^\)\<\(cairo_[][<>/0-9a-z_]*\>[^][<>(]\)" +if test "x$SGML_DOCS" = x; then + func_regexp='^[^:]*:[/ ][*]\(\|[ \t].*\)'$func_regexp +fi + +# We need to filter out gtk-doc markup errors for program listings. +if echo $FILES | xargs grep . /dev/null | sed -e '//,/<\/programlisting>/d' | grep "$func_regexp" | grep -v '^[^:]*: [*] [a-z_0-9]*:$' | grep -v '#####'; then + stat=1 + echo Error: some function names in the docs are not followed by parentheses. + echo Fix this by searching for the following regexp in the above files: + echo " '$func_regexp'" +fi >&2 + +note_regexp='\' +if echo $FILES | xargs grep "$note_regexp" /dev/null; then + stat=1 + echo Error: some source files contain the string 'NOTE'. + echo Be civil and replace it by 'Note' please. +fi >&2 + +# Only run the syntax checker on the source files (not doc/) +if test -e ./check-doc-syntax.awk; then + if echo $FILES | xargs awk -f ./check-doc-syntax.awk ; then + : + else + stat=1 + fi >&2 +fi + +exit $stat diff --git a/gfx/cairo/cairo/src/check-headers.sh b/gfx/cairo/cairo/src/check-headers.sh new file mode 100755 index 000000000000..61232954baec --- /dev/null +++ b/gfx/cairo/cairo/src/check-headers.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + +echo Checking public headers for missing cairo_public decorators + +cd "$srcdir" +FILES=$all_cairo_headers +if test "x$FILES" = x; then + FILES=`find . -name 'cairo*.h' ! -name '*-private.h' ! -name 'cairoint.h'` +fi + +grep -B 1 '^cairo_.*[ ]\+(' /dev/null $FILES | +awk ' +/^--$/ { context=""; public=0; next; } +/:cairo_.*[ ]+\(/ { if (!public) {print context; print; print "--";} next; } +/-cairo_public.*[ ]/ {public=1;} +{ context=$0; } +' | +sed 's/[.]h-/.h:/' | +grep . >&2 && stat=1 + +exit $stat diff --git a/gfx/cairo/cairo/src/check-link.c b/gfx/cairo/cairo/src/check-link.c index 66ca1b2413ca..2d943d644ce0 100644 --- a/gfx/cairo/cairo/src/check-link.c +++ b/gfx/cairo/cairo/src/check-link.c @@ -1,11 +1,4 @@ -#define CAIRO_VERSION_H 1 - #include - -/* get the "real" version info instead of dummy cairo-version.h */ -#undef CAIRO_VERSION_H -#include "../cairo-version.h" - #include int diff --git a/gfx/cairo/cairo/src/check-plt.sh b/gfx/cairo/cairo/src/check-plt.sh new file mode 100755 index 000000000000..5a9dae1269da --- /dev/null +++ b/gfx/cairo/cairo/src/check-plt.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +if which readelf 2>/dev/null >/dev/null; then + : +else + echo "'readelf' 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 + +for so in .libs/lib*.so; do + echo Checking "$so" for local PLT entries + readelf -W -r "$so" | grep 'JU\?MP_SLO' | grep 'cairo' >&2 && stat=1 +done + +exit $stat diff --git a/gfx/cairo/cairo/src/check-preprocessor-syntax.sh b/gfx/cairo/cairo/src/check-preprocessor-syntax.sh new file mode 100755 index 000000000000..b718f604ee72 --- /dev/null +++ b/gfx/cairo/cairo/src/check-preprocessor-syntax.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +cd "$srcdir" +stat=0 + + +HEADERS=$all_cairo_headers +test "x$HEADERS" = x && HEADERS=`find . -name 'cairo*.h' ! -name 'cairo*-private.h' ! -name 'cairo*-inline.h' ! -name 'cairoint.h'` + +PRIVATE=$all_cairo_private +test "x$PRIVATE" = x && PRIVATE=`find . -name 'cairo*-private.h' -or -name 'cairo*-inline.h' -or -name 'cairoint.h'` + +SOURCES=$all_cairo_sources +test "x$SOURCES" = x && SOURCES=`find . -name 'cairo*.c' -or -name 'cairo*.cpp'` + +ALL="/dev/null $HEADERS $PRIVATE $SOURCES" + +echo 'Checking that public header files #include "cairo.h" first (or none)' + +for x in $HEADERS; do + grep '#.*\' "$x" /dev/null | head -n 1 +done | +grep -v '"cairo[.]h"' | +grep -v 'cairo[.]h:' | +grep . >&2 && stat=1 + + +echo 'Checking that private header files #include "some cairo header" first (or none)' + +for x in $PRIVATE; do + grep '#.*\' "$x" /dev/null | head -n 1 +done | +grep -v '"cairo.*[.]h"' | +grep -v 'cairoint[.]h:' | +grep . >&2 && stat=1 + + +echo 'Checking that source files #include "cairoint.h" first (or none)' + +for x in $SOURCES; do + grep '#.*\' "$x" /dev/null | head -n 1 +done | +grep -v '"cairoint[.]h"' | +grep . >&2 && stat=1 + + +echo 'Checking that there is no #include ' +grep '#.*\.*<.*cairo' $ALL >&2 && stat=1 + + +echo 'Checking that feature conditionals are used with #if only (not #ifdef)' +grep '#ifdef CAIRO_HAS_' $ALL && stat=1 +grep '#if.*defined[ ]*(CAIRO_HAS_' $ALL && stat=1 + +exit $stat diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-bo.c b/gfx/cairo/cairo/src/drm/cairo-drm-bo.c new file mode 100644 index 000000000000..c82f9331d8d1 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-bo.c @@ -0,0 +1,99 @@ +/* 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. + * + */ + +#include "cairoint.h" +#include "cairo-drm-private.h" +#include "cairo-error-private.h" + +#include +#include +#include + +#define ERR_DEBUG(x) x + +cairo_status_t +_cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev, + cairo_drm_bo_t *bo, + uint32_t name) +{ + struct drm_gem_open open; + int ret; + + open.name = name; + open.handle = 0; + open.size = 0; + do { + ret = ioctl (dev->fd, DRM_IOCTL_GEM_OPEN, &open); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + ERR_DEBUG((fprintf (stderr, "Failed to open bo for name %d: %s\n", + name, strerror (errno)))); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + bo->name = name; + bo->size = open.size; + bo->handle = open.handle; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_drm_bo_flink (const cairo_drm_device_t *dev, + cairo_drm_bo_t *bo) +{ + struct drm_gem_flink flink; + int ret; + + memset (&flink, 0, sizeof (flink)); + flink.handle = bo->handle; + ret = ioctl (dev->fd, DRM_IOCTL_GEM_FLINK, &flink); + if (ret == -1) { + ERR_DEBUG((fprintf (stderr, "Failed to flink bo: %s\n", + strerror (errno)))); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + bo->name = flink.name; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_drm_bo_close (const cairo_drm_device_t *dev, + cairo_drm_bo_t *bo) +{ + struct drm_gem_close close; + int ret; + + close.handle = bo->handle; + do { + ret = ioctl (dev->fd, DRM_IOCTL_GEM_CLOSE, &close); + } while (ret == -1 && errno == EINTR); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-gallium-surface.c b/gfx/cairo/cairo/src/drm/cairo-drm-gallium-surface.c new file mode 100644 index 000000000000..ca18f7336c1c --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-gallium-surface.c @@ -0,0 +1,826 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * Copyright © 2009 Eric Anholt + * + * 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. + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" + +#include + +#include +#include +#include +#include +#include + +#include + +typedef struct _gallium_surface gallium_surface_t; +typedef struct _gallium_device gallium_device_t; + +struct _gallium_device { + cairo_drm_device_t drm; + + void *dlhandle; + struct drm_api *api; + + struct pipe_screen *screen; + struct pipe_context *pipe; + + int max_size; +}; + +struct _gallium_surface { + cairo_drm_surface_t drm; + + enum pipe_format pipe_format; + + struct pipe_resource *texture; + struct pipe_transfer *map_transfer; + + cairo_surface_t *fallback; +}; + +static cairo_surface_t * +gallium_surface_create_internal (gallium_device_t *device, + enum pipe_format format, + int width, int height); + +static inline gallium_device_t * +gallium_device (gallium_surface_t *surface) +{ + return (gallium_device_t *) surface->drm.base.device; +} + +static cairo_format_t +_cairo_format_from_pipe_format (enum pipe_format format) +{ + switch ((int) format) { + case PIPE_FORMAT_A8_UNORM: + return CAIRO_FORMAT_A8; + case PIPE_FORMAT_A8R8G8B8_UNORM: + return CAIRO_FORMAT_ARGB32; + default: + return CAIRO_FORMAT_INVALID; + } +} + +static enum pipe_format +pipe_format_from_format (cairo_format_t format) +{ + switch ((int) format) { + case CAIRO_FORMAT_A8: + return PIPE_FORMAT_A8_UNORM; + case CAIRO_FORMAT_ARGB32: + return PIPE_FORMAT_A8R8G8B8_UNORM; + default: + return (enum pipe_format) -1; + } +} + +static enum pipe_format +pipe_format_from_content (cairo_content_t content) +{ + if (content == CAIRO_CONTENT_ALPHA) + return PIPE_FORMAT_A8_UNORM; + else + return PIPE_FORMAT_A8R8G8B8_UNORM; +} + +static cairo_bool_t +format_is_supported_destination (gallium_device_t *device, + enum pipe_format format) +{ + if (format == (enum pipe_format) -1) + return FALSE; + + return device->screen->is_format_supported (device->screen, + format, + 0, + PIPE_BIND_RENDER_TARGET, + 0); +} + +#if 0 +static cairo_bool_t +format_is_supported_source (gallium_device_t *device, + enum pipe_format format) +{ + return device->screen->is_format_supported (device->screen, + format, + 0, + PIPE_BIND_SAMPLER_VIEW, + 0); +} +#endif + +static cairo_surface_t * +gallium_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + gallium_surface_t *other = abstract_src; + gallium_device_t *device = gallium_device (other); + enum pipe_format pipe_format; + cairo_surface_t *surface = NULL; + cairo_status_t status; + + status = cairo_device_acquire (&device->drm.base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (MAX (width, height) > device->max_size) + goto RELEASE; + + if (content == other->drm.base.content) + pipe_format = other->pipe_format; + else + pipe_format = pipe_format_from_content (content); + + if (! format_is_supported_destination (device, pipe_format)) + goto RELEASE; + + surface = gallium_surface_create_internal (device, + pipe_format, + width, height); + +RELEASE: + cairo_device_release (&device->drm.base); + + return surface; +} + +static cairo_status_t +gallium_surface_finish (void *abstract_surface) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device = gallium_device (surface); + cairo_status_t status; + + status = cairo_device_acquire (&device->drm.base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + pipe_resource_reference (&surface->texture, NULL); + cairo_device_release (&device->drm.base); + } + + return _cairo_drm_surface_finish (&surface->drm); +} + +static cairo_surface_t * +gallium_surface_map_to_image (gallium_surface_t *surface) +{ + gallium_device_t *device = gallium_device (surface); + cairo_status_t status; + void *ptr = NULL; + + status = cairo_device_acquire (&device->drm.base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface->map_transfer = + pipe_get_transfer (device->pipe, + surface->texture, 0, 0, 0, + PIPE_TRANSFER_MAP_DIRECTLY | + PIPE_TRANSFER_READ_WRITE, + 0, 0, + surface->drm.width, + surface->drm.height); + if (likely (surface->map_transfer != NULL)) + ptr = device->pipe->transfer_map (device->pipe, surface->map_transfer); + + cairo_device_release (&device->drm.base); + + if (unlikely (ptr == NULL)) { + if (surface->map_transfer != NULL) { + device->pipe->transfer_destroy (device->pipe, + surface->map_transfer); + surface->map_transfer = NULL; + } + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + return cairo_image_surface_create_for_data (ptr, + surface->drm.format, + surface->drm.width, + surface->drm.height, + surface->map_transfer->stride); +} + +static cairo_status_t +gallium_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device = gallium_device (surface); + cairo_format_t format; + cairo_surface_t *image; + cairo_status_t status; + struct pipe_transfer *transfer; + void *ptr; + + if (surface->fallback != NULL) { + *image_out = (cairo_image_surface_t *) + cairo_surface_reference (surface->fallback); + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + if (unlikely (surface->drm.width == 0 || surface->drm.height == 0)) { + image = cairo_image_surface_create (surface->drm.format, 0, 0); + if (unlikely (image->status)) + return image->status; + + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + format = _cairo_format_from_pipe_format (surface->pipe_format); + if (format == CAIRO_FORMAT_INVALID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = cairo_device_acquire (&device->drm.base); + if (unlikely (status)) + return status; + + transfer = pipe_get_transfer (device->pipe, + surface->texture, 0, 0, 0, + PIPE_TRANSFER_READ, + 0, 0, + surface->drm.width, + surface->drm.height); + ptr = device->pipe->transfer_map (device->pipe, transfer); + cairo_device_release (&device->drm.base); + + image = cairo_image_surface_create_for_data (ptr, format, + surface->drm.width, + surface->drm.height, + surface->drm.stride); + if (unlikely (image->status)) + return image->status; + + *image_out = (cairo_image_surface_t *) image; + *image_extra = transfer; + return CAIRO_STATUS_SUCCESS; +} + +static void +gallium_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); + + if (image_extra != NULL) { + gallium_device_t *device = gallium_device (abstract_surface); + + device->pipe->transfer_unmap (device->pipe, image_extra); + device->pipe->transfer_destroy (device->pipe, image_extra); + } +} + +static cairo_status_t +gallium_surface_flush (void *abstract_surface, + unsigned flags) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device = gallium_device (surface); + cairo_status_t status; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->fallback == NULL) { + device->pipe->flush (device->pipe, + PIPE_FLUSH_RENDER_CACHE, + NULL); + return CAIRO_STATUS_SUCCESS; + } + + /* kill any outstanding maps */ + cairo_surface_finish (surface->fallback); + + status = cairo_device_acquire (&device->drm.base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + device->pipe->transfer_unmap (device->pipe, + surface->map_transfer); + device->pipe->transfer_destroy (device->pipe, + surface->map_transfer); + surface->map_transfer = NULL; + cairo_device_release (&device->drm.base); + } + + status = cairo_surface_status (surface->fallback); + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; + + return status; +} + +static cairo_int_status_t +gallium_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_paint (surface->fallback, op, source, clip); +} + +static cairo_int_status_t +gallium_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_mask (surface->fallback, + op, source, mask, + clip); +} + +static cairo_int_status_t +gallium_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_stroke (surface->fallback, + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +gallium_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + gallium_surface_t *surface = abstract_surface; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_fill (surface->fallback, + op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +gallium_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, + cairo_clip_t *clip, + int *num_remaining) +{ + gallium_surface_t *surface = abstract_surface; + + *num_remaining = 0; + + if (surface->fallback == NULL) { + /* XXX insert magic */ + surface->fallback = gallium_surface_map_to_image (surface); + } + + return _cairo_surface_show_text_glyphs (surface->fallback, + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + clip); +} + +static const cairo_surface_backend_t gallium_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + gallium_surface_create_similar, + gallium_surface_finish, + + NULL, + gallium_surface_acquire_source_image, + gallium_surface_release_source_image, + + NULL, //gallium_surface_acquire_dest_image, + NULL, //gallium_surface_release_dest_image, + NULL, //gallium_surface_clone_similar, + NULL, //gallium_surface_composite, + NULL, //gallium_surface_fill_rectangles, + NULL, //gallium_surface_composite_trapezoids, + NULL, //gallium_surface_create_span_renderer, + NULL, //gallium_surface_check_span_renderer, + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old_show_glyphs */ + _cairo_drm_surface_get_font_options, + gallium_surface_flush, + NULL, /* mark_dirty_rectangle */ + NULL, //gallium_surface_scaled_font_fini, + NULL, //gallium_surface_scaled_glyph_fini, + + gallium_surface_paint, + gallium_surface_mask, + gallium_surface_stroke, + gallium_surface_fill, + gallium_surface_glyphs, + + NULL, /* snapshot */ + + NULL, /* is_similar */ + + NULL, /* reset */ +}; + +static int +gallium_format_stride_for_width (enum pipe_format format, int width) +{ + int stride; + + stride = 1024; /* XXX fugly */ + while (stride < width) + stride *= 2; + + if (format == PIPE_FORMAT_A8R8G8B8_UNORM) + stride *= 4; + + return stride; +} + +static cairo_drm_bo_t * +_gallium_fake_bo_create (uint32_t size, uint32_t name) +{ + cairo_drm_bo_t *bo; + + /* XXX integrate with winsys handle */ + + bo = _cairo_malloc (sizeof (cairo_drm_bo_t)); + + CAIRO_REFERENCE_COUNT_INIT (&bo->ref_count, 1); + bo->name = name; + bo->handle = 0; + bo->size = size; + + return bo; +} + +static void +_gallium_fake_bo_release (void *dev, void *bo) +{ + free (bo); +} + +static cairo_surface_t * +gallium_surface_create_internal (gallium_device_t *device, + enum pipe_format pipe_format, + int width, int height) +{ + gallium_surface_t *surface; + struct pipe_resource template; + cairo_status_t status; + cairo_format_t format; + int stride, size; + + surface = _cairo_malloc (sizeof (gallium_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + format = _cairo_format_from_pipe_format (pipe_format); + _cairo_surface_init (&surface->drm.base, + &gallium_surface_backend, + &device->drm.base, + _cairo_content_from_format (format)); + _cairo_drm_surface_init (&surface->drm, format, width, height); + + stride = gallium_format_stride_for_width (pipe_format, width); + size = stride * height; + + surface->drm.stride = stride; + surface->drm.bo = _gallium_fake_bo_create (size, 0); + + memset(&template, 0, sizeof(template)); + template.target = PIPE_TEXTURE_2D; + template.format = pipe_format; + template.width0 = width; + template.height0 = height; + template.depth0 = 1; + template.last_level = 0; + template.bind = PIPE_BIND_RENDER_TARGET; + surface->texture = device->screen->resource_create (device->screen, + &template); + + if (unlikely (surface->texture == NULL)) { + status = _cairo_drm_surface_finish (&surface->drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + surface->pipe_format = pipe_format; + surface->texture = NULL; + + return &surface->drm.base; +} + +static cairo_surface_t * +gallium_surface_create (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height) +{ + gallium_device_t *device = (gallium_device_t *) base_dev; + cairo_surface_t *surface; + enum pipe_format pipe_format; + cairo_status_t status; + + status = cairo_device_acquire (&device->drm.base); + + if (MAX (width, height) > device->max_size) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + goto RELEASE; + } + + pipe_format = pipe_format_from_format (format); + if (! format_is_supported_destination (device, pipe_format)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + goto RELEASE; + } + + surface = gallium_surface_create_internal (device, + pipe_format, + width, height); + +RELEASE: + cairo_device_release (&device->drm.base); + + return surface; +} + +#if 0 +static cairo_surface_t * +gallium_surface_create_for_name (cairo_drm_device_t *base_dev, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + gallium_device_t *device; + gallium_surface_t *surface; + cairo_status_t status; + cairo_content_t content; + + surface = _cairo_malloc (sizeof (gallium_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_A8: + surface->pipe_format = PIPE_FORMAT_A8_UNORM; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + surface->pipe_format = PIPE_FORMAT_A8R8G8B8_UNORM; + break; + } + + status = cairo_device_acquire (&device->drm.base); + + if (MAX (width, height) > device->max_size) { + cairo_device_release (&device->drm.base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + if (! format_is_supported_destination (device, surface->pipe_format)) { + cairo_device_release (&device->drm.base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + content = _cairo_content_from_format (format); + _cairo_surface_init (&surface->drm.base, + &gallium_surface_backend, + content); + _cairo_drm_surface_init (&surface->drm, base_dev); + + surface->drm.bo = _gallium_fake_bo_create (height * stride, name); + + surface->drm.width = width; + surface->drm.height = height; + surface->drm.stride = stride; + +#if 0 + /* XXX screen->create_from_handle */ + surface->buffer = device->api->buffer_from_handle (device->api, + device->screen, + "cairo-gallium alien", + name); + if (unlikely (surface->buffer == NULL)) { + status = _cairo_drm_surface_finish (&surface->drm); + cairo_device_release (&device->drm.base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } +#endif + + surface->texture = NULL; + + surface->fallback = NULL; + + cairo_device_release (&device->drm.base); + + return &surface->drm.base; +} + +static cairo_int_status_t +gallium_surface_flink (void *abstract_surface) +{ + gallium_surface_t *surface = abstract_surface; + gallium_device_t *device; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + status = cairo_device_acquire (&device->drm.base); + if (! device->api->global_handle_from_buffer (device->api, + device->screen, + surface->buffer, + &surface->drm.bo->name)) + { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + cairo_device_release (&device->drm.base); + + return status; +} +#endif + +static void +gallium_device_destroy (void *abstract_device) +{ + gallium_device_t *device = abstract_device; + + device->pipe->destroy (device->pipe); + device->screen->destroy (device->screen); + device->api->destroy (device->api); + + dlclose (device->dlhandle); + free (device); +} + +cairo_drm_device_t * +_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id) +{ + gallium_device_t *device; + cairo_status_t status; + void *handle; + const char *libdir; + char buf[4096]; + struct drm_api *(*ctor) (void); + + /* XXX need search path + probe */ + libdir = getenv ("CAIRO_GALLIUM_LIBDIR"); + if (libdir == NULL) + libdir = "/usr/lib/dri"; + buf[snprintf (buf, sizeof (buf)-1, "%s/i915_dri.so", libdir)] = '\0'; + + handle = dlopen (buf, RTLD_LAZY); + if (handle == NULL) + return NULL; + + ctor = dlsym (handle, "drm_api_create"); + if (ctor == NULL) { + dlclose (handle); + return NULL; + } + + device = _cairo_malloc (sizeof (gallium_device_t)); + if (device == NULL) { + dlclose (handle); + return _cairo_drm_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + device->dlhandle = handle; + + device->drm.surface.create = gallium_surface_create; + device->drm.surface.create_for_name = NULL; + //device->drm.surface.create_for_name = gallium_surface_create_for_name; + device->drm.surface.enable_scan_out = NULL; + //device->drm.surface.flink = gallium_surface_flink; + device->drm.surface.flink = NULL; + + device->drm.device.flush = NULL; + device->drm.device.throttle = NULL; + device->drm.device.destroy = gallium_device_destroy; + + device->drm.bo.release = _gallium_fake_bo_release; + + device->api = ctor (); + if (device->api == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + device->screen = device->api->create_screen (device->api, fd, NULL); + if (device->screen == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_API; + } + + device->max_size = 1 << device->screen->get_param (device->screen, + PIPE_CAP_MAX_TEXTURE_2D_LEVELS); + + device->pipe = device->screen->context_create (device->screen, device); + if (device->pipe == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_SCREEN; + } + + return _cairo_drm_device_init (&device->drm, + fd, dev, + 0, 0, + device->max_size); + +CLEANUP_SCREEN: + device->screen->destroy (device->screen); +CLEANUP_API: + device->api->destroy (device->api); +CLEANUP: + free (device); + dlclose (handle); + return _cairo_drm_device_create_in_error (status); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i915-glyphs.c b/gfx/cairo/cairo/src/drm/cairo-drm-i915-glyphs.c new file mode 100644 index 000000000000..3b0efc248984 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i915-glyphs.c @@ -0,0 +1,564 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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 + */ + +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-drm-i915-private.h" +#include "cairo-error-private.h" +#include "cairo-rtree-private.h" +#include "cairo-clip-inline.h" + +static void +i915_emit_glyph_rectangle_zero (i915_device_t *device, + i915_shader_t *shader, + int x1, int y1, + int x2, int y2, + intel_glyph_t *glyph) +{ + float *v; + + /* Each vertex is: + * 2 vertex coordinates + */ + + v = i915_add_rectangle (device); + *v++ = x2; *v++ = y2; + *v++ = x1; *v++ = y2; + *v++ = x1; *v++ = y1; +} + +static void +i915_emit_glyph_rectangle_constant (i915_device_t *device, + i915_shader_t *shader, + int x1, int y1, + int x2, int y2, + intel_glyph_t *glyph) +{ + float *v; + + /* Each vertex is: + * 2 vertex coordinates + * 2 glyph texture coordinates + */ + + v = i915_add_rectangle (device); + + /* bottom right */ + *v++ = x2; *v++ = y2; + *v++ = glyph->texcoord[0]; + + /* bottom left */ + *v++ = x1; *v++ = y2; + *v++ = glyph->texcoord[1]; + + /* top left */ + *v++ = x1; *v++ = y1; + *v++ = glyph->texcoord[2]; +} + +static void +i915_emit_glyph_rectangle_general (i915_device_t *device, + i915_shader_t *shader, + int x1, int y1, + int x2, int y2, + intel_glyph_t *glyph) +{ + double s, t; + float *v; + + /* Each vertex is: + * 2 vertex coordinates + * [0-2] source texture coordinates + * 2 glyph texture coordinates + */ + + v = i915_add_rectangle (device); + + /* bottom right */ + *v++ = x2; *v++ = y2; + s = x2, t = y2; + switch (shader->source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); + break; + case VS_TEXTURE: + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = s; *v++ = t; + break; + case VS_TEXTURE_16: + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = texcoord_2d_16 (s, t); + break; + } + *v++ = glyph->texcoord[0]; + + /* bottom left */ + *v++ = x1; *v++ = y2; + s = x1, t = y2; + switch (shader->source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); + break; + case VS_TEXTURE: + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = s; *v++ = t; + break; + case VS_TEXTURE_16: + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = texcoord_2d_16 (s, t); + break; + } + *v++ = glyph->texcoord[1]; + + /* top left */ + *v++ = x1; *v++ = y1; + s = x1, t = y2; + switch (shader->source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *v++ = i915_shader_linear_texcoord (&shader->source.linear, s, t); + break; + case VS_TEXTURE: + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = s; *v++ = t; + break; + case VS_TEXTURE_16: + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = texcoord_2d_16 (s, t); + break; + } + *v++ = glyph->texcoord[2]; +} + +typedef void +(*i915_emit_glyph_rectangle_func_t) (i915_device_t *device, + i915_shader_t *shader, + int x1, int y1, + int x2, int y2, + intel_glyph_t *glyph); + +static cairo_status_t +i915_surface_mask_internal (i915_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + i915_surface_t *mask, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + i915_device_t *device; + i915_shader_t shader; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + i915_shader_init (&shader, dst, op, 1.); + + status = i915_shader_acquire_pattern (&shader, &shader.source, + source, &extents->bounded); + if (unlikely (status)) + return status; + + shader.mask.type.vertex = VS_TEXTURE_16; + shader.mask.type.pattern = PATTERN_TEXTURE; + shader.mask.type.fragment = FS_TEXTURE; + shader.mask.base.content = mask->intel.drm.base.content; + shader.mask.base.texfmt = TEXCOORDFMT_2D_16; + shader.mask.base.n_samplers = 1; + shader.mask.base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (CAIRO_FILTER_NEAREST); + shader.mask.base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (CAIRO_EXTEND_NONE); + + cairo_matrix_init_translate (&shader.mask.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + cairo_matrix_scale (&shader.mask.base.matrix, + 1. / mask->intel.drm.width, + 1. / mask->intel.drm.height); + + shader.mask.base.bo = intel_bo_reference (to_intel_bo (mask->intel.drm.bo)); + shader.mask.base.offset[0] = 0; + shader.mask.base.map[0] = mask->map0; + shader.mask.base.map[1] = mask->map1; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i915_shader_set_clip (&shader, clip); + } + + status = cairo_device_acquire (dst->intel.drm.base.device); + if (unlikely (status)) + goto CLEANUP_SHADER; + + device = i915_device (dst); + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto CLEANUP_DEVICE; + + if (clip_region != NULL) { + unsigned int n, num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rectangles; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + + shader.add_rectangle (&shader, + rect.x, rect.y, + rect.x + rect.width, rect.y + rect.height); + } + } else { + shader.add_rectangle (&shader, + extents->bounded.x, extents->bounded.y, + extents->bounded.x + extents->bounded.width, + extents->bounded.y + extents->bounded.height); + } + + if (! extents->is_bounded) + status = i915_fixup_unbounded (dst, extents, clip); + +CLEANUP_DEVICE: + cairo_device_release (&device->intel.base.base); +CLEANUP_SHADER: + i915_shader_fini (&shader); + return status; +} + +cairo_int_status_t +i915_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, + cairo_clip_t *clip, + int *num_remaining) +{ + i915_surface_t *surface = abstract_surface; + i915_surface_t *mask = NULL; + i915_device_t *device; + i915_shader_t shader; + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_bool_t overlap; + cairo_region_t *clip_region = NULL; + intel_bo_t *last_bo = NULL; + i915_emit_glyph_rectangle_func_t emit_func; + cairo_scaled_glyph_t *glyph_cache[64]; + cairo_status_t status; + int mask_x = 0, mask_y = 0; + int i = 0; + + *num_remaining = 0; + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + surface->intel.drm.width, + surface->intel.drm.height, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_rectangle (clip, &extents.mask)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + + have_clip = TRUE; + } + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + if (have_clip) + _cairo_clip_fini (&local_clip); + return status; + } + } + + if (i915_surface_needs_tiling (surface)) { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (overlap || ! extents.is_bounded) { + cairo_format_t format; + + format = CAIRO_FORMAT_A8; + if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) + format = CAIRO_FORMAT_ARGB32; + + mask = (i915_surface_t *) + i915_surface_create_internal (&i915_device (surface)->intel.base, + format, + extents.bounded.width, + extents.bounded.height, + I915_TILING_DEFAULT, + TRUE); + if (unlikely (mask->intel.drm.base.status)) + return mask->intel.drm.base.status; + + status = i915_surface_clear (mask); + if (unlikely (status)) { + cairo_surface_destroy (&mask->intel.drm.base); + return status; + } + + i915_shader_init (&shader, mask, CAIRO_OPERATOR_ADD, 1.); + + status = i915_shader_acquire_pattern (&shader, &shader.source, + &_cairo_pattern_white.base, + &extents.bounded); + if (unlikely (status)) { + cairo_surface_destroy (&mask->intel.drm.base); + return status; + } + + mask_x = -extents.bounded.x; + mask_y = -extents.bounded.y; + } else { + i915_shader_init (&shader, surface, op, 1.); + + status = i915_shader_acquire_pattern (&shader, &shader.source, + source, &extents.bounded); + if (unlikely (status)) + return status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i915_shader_set_clip (&shader, clip); + } + } + + shader.mask.type.fragment = FS_TEXTURE; + shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ + shader.mask.base.texfmt = TEXCOORDFMT_2D_16; + shader.mask.base.n_samplers = 1; + shader.mask.base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (CAIRO_FILTER_NEAREST); + shader.mask.base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (CAIRO_EXTEND_NONE); + + switch (shader.source.type.vertex) { + case VS_ZERO: + emit_func = i915_emit_glyph_rectangle_zero; + break; + case VS_CONSTANT: + emit_func = i915_emit_glyph_rectangle_constant; + break; + default: + case VS_LINEAR: + case VS_TEXTURE: + case VS_TEXTURE_16: + emit_func = i915_emit_glyph_rectangle_general; + break; + } + + status = cairo_device_acquire (surface->intel.drm.base.device); + if (unlikely (status)) + goto CLEANUP_SHADER; + + device = i915_device (surface); + + _cairo_scaled_font_freeze_cache (scaled_font); + if (scaled_font->surface_private == NULL) { + scaled_font->surface_private = device; + scaled_font->surface_backend = surface->intel.drm.base.backend; + cairo_list_add (&scaled_font->link, &device->intel.fonts); + } + + memset (glyph_cache, 0, sizeof (glyph_cache)); + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + int x, y, x1, x2, y1, y2; + int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); + intel_glyph_t *glyph; + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) + { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + goto FINISH; + + glyph_cache[cache_index] = scaled_glyph; + } + + if (unlikely (scaled_glyph->metrics.width == 0 || + scaled_glyph->metrics.height == 0)) + { + continue; + } + + /* XXX glyph images are snapped to pixel locations */ + x = _cairo_lround (glyphs[i].x); + y = _cairo_lround (glyphs[i].y); + + x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + + if (x2 < extents.bounded.x || + y2 < extents.bounded.y || + x1 > extents.bounded.x + extents.bounded.width || + y1 > extents.bounded.y + extents.bounded.height) + { + continue; + } + + if (scaled_glyph->surface_private == NULL) { + status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { + status = CAIRO_STATUS_SUCCESS; + continue; + } + if (unlikely (status)) + goto FINISH; + } + + glyph = intel_glyph_pin (scaled_glyph->surface_private); + if (glyph->cache->buffer.bo != last_bo) { + intel_buffer_cache_t *cache = glyph->cache; + + shader.mask.base.bo = cache->buffer.bo; + shader.mask.base.offset[0] = cache->buffer.offset; + shader.mask.base.map[0] = cache->buffer.map0; + shader.mask.base.map[1] = cache->buffer.map1; + shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto FINISH; + + last_bo = cache->buffer.bo; + } + + x2 = x1 + glyph->width; + y2 = y1 + glyph->height; + + if (mask_x) + x1 += mask_x, x2 += mask_x; + if (mask_y) + y1 += mask_y, y2 += mask_y; + + /* XXX clip glyph */ + emit_func (device, &shader, x1, y1, x2, y2, glyph); + } + + status = CAIRO_STATUS_SUCCESS; + FINISH: + _cairo_scaled_font_thaw_cache (scaled_font); + cairo_device_release (surface->intel.drm.base.device); + CLEANUP_SHADER: + i915_shader_fini (&shader); + + if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { + cairo_path_fixed_t path; + + _cairo_path_fixed_init (&path); + status = _cairo_scaled_font_glyph_path (scaled_font, + glyphs + i, num_glyphs - i, + &path); + if (mask_x | mask_y) { + _cairo_path_fixed_translate (&path, + _cairo_fixed_from_int (mask_x), + _cairo_fixed_from_int (mask_y)); + } + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = surface->intel.drm.base.backend->fill (shader.target, + shader.op, + mask != NULL ? &_cairo_pattern_white.base : source, + &path, + CAIRO_FILL_RULE_WINDING, + 0, + scaled_font->options.antialias, + clip); + } + _cairo_path_fixed_fini (&path); + } + + if (mask != NULL) { + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = i915_surface_mask_internal (surface, op, source, mask, + clip, &extents); + } + cairo_surface_finish (&mask->intel.drm.base); + cairo_surface_destroy (&mask->intel.drm.base); + } + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i915-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-i915-private.h new file mode 100644 index 000000000000..7585756dc580 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i915-private.h @@ -0,0 +1,1270 @@ +/* + * Copyright © 2006, 2009 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Eric Anholt + * Chris Wilson + */ + +#ifndef CAIRO_DRM_I915_PRIVATE_H +#define CAIRO_DRM_I915_PRIVATE_H + +#include "cairo-types-private.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-intel-private.h" +#include "cairo-drm-intel-command-private.h" +#include "cairo-drm-intel-ioctl-private.h" +#include "cairo-freelist-private.h" + +#include + +#define I915_VERBOSE 1 + +#define I915_MAX_TEX_INDIRECT 4 +#define I915_MAX_TEX_INSN 32 +#define I915_MAX_ALU_INSN 64 +#define I915_MAX_DECL_INSN 27 +#define I915_MAX_TEMPORARY 16 + +/* Each instruction is 3 dwords long, though most don't require all + * this space. Maximum of 123 instructions. Smaller maxes per insn + * type. + */ +#define _3DSTATE_PIXEL_SHADER_PROGRAM (CMD_3D|(0x1d<<24)|(0x5<<16)) + +#define REG_TYPE_R 0 /* temporary regs, no need to + * dcl, must be written before + * read -- Preserved between + * phases. + */ +#define REG_TYPE_T 1 /* Interpolated values, must be + * dcl'ed before use. + * + * 0..7: texture coord, + * 8: diffuse spec, + * 9: specular color, + * 10: fog parameter in w. + */ +#define REG_TYPE_CONST 2 /* Restriction: only one const + * can be referenced per + * instruction, though it may be + * selected for multiple inputs. + * Constants not initialized + * default to zero. + */ +#define REG_TYPE_S 3 /* sampler */ +#define REG_TYPE_OC 4 /* output color (rgba) */ +#define REG_TYPE_OD 5 /* output depth (w), xyz are + * temporaries. If not written, + * interpolated depth is used? + */ +#define REG_TYPE_U 6 /* unpreserved temporaries */ +#define REG_TYPE_MASK 0x7 +#define REG_TYPE_SHIFT 4 +#define REG_NR_MASK 0xf + +/* REG_TYPE_T: + */ +#define T_TEX0 0 +#define T_TEX1 1 +#define T_TEX2 2 +#define T_TEX3 3 +#define T_TEX4 4 +#define T_TEX5 5 +#define T_TEX6 6 +#define T_TEX7 7 +#define T_DIFFUSE 8 +#define T_SPECULAR 9 +#define T_FOG_W 10 /* interpolated fog is in W coord */ + +/* Arithmetic instructions */ + +/* .replicate_swizzle == selection and replication of a particular + * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww + */ +#define A0_NOP (0x0<<24) /* no operation */ +#define A0_ADD (0x1<<24) /* dst = src0 + src1 */ +#define A0_MOV (0x2<<24) /* dst = src0 */ +#define A0_MUL (0x3<<24) /* dst = src0 * src1 */ +#define A0_MAD (0x4<<24) /* dst = src0 * src1 + src2 */ +#define A0_DP2ADD (0x5<<24) /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */ +#define A0_DP3 (0x6<<24) /* dst.xyzw = src0.xyz dot src1.xyz */ +#define A0_DP4 (0x7<<24) /* dst.xyzw = src0.xyzw dot src1.xyzw */ +#define A0_FRC (0x8<<24) /* dst = src0 - floor(src0) */ +#define A0_RCP (0x9<<24) /* dst.xyzw = 1/(src0.replicate_swizzle) */ +#define A0_RSQ (0xa<<24) /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */ +#define A0_EXP (0xb<<24) /* dst.xyzw = exp2(src0.replicate_swizzle) */ +#define A0_LOG (0xc<<24) /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */ +#define A0_CMP (0xd<<24) /* dst = (src0 >= 0.0) ? src1 : src2 */ +#define A0_MIN (0xe<<24) /* dst = (src0 < src1) ? src0 : src1 */ +#define A0_MAX (0xf<<24) /* dst = (src0 >= src1) ? src0 : src1 */ +#define A0_FLR (0x10<<24) /* dst = floor(src0) */ +#define A0_MOD (0x11<<24) /* dst = src0 fmod 1.0 */ +#define A0_TRC (0x12<<24) /* dst = int(src0) */ +#define A0_SGE (0x13<<24) /* dst = src0 >= src1 ? 1.0 : 0.0 */ +#define A0_SLT (0x14<<24) /* dst = src0 < src1 ? 1.0 : 0.0 */ +#define A0_DEST_SATURATE (1<<22) +#define A0_DEST_TYPE_SHIFT 19 +/* Allow: R, OC, OD, U */ +#define A0_DEST_NR_SHIFT 14 +/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ +#define A0_DEST_CHANNEL_X (1<<10) +#define A0_DEST_CHANNEL_Y (2<<10) +#define A0_DEST_CHANNEL_Z (4<<10) +#define A0_DEST_CHANNEL_W (8<<10) +#define A0_DEST_CHANNEL_ALL (0xf<<10) +#define A0_DEST_CHANNEL_SHIFT 10 +#define A0_SRC0_TYPE_SHIFT 7 +#define A0_SRC0_NR_SHIFT 2 + +#define A0_DEST_CHANNEL_XY (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y) +#define A0_DEST_CHANNEL_XYZ (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z) + +#define SRC_X 0 +#define SRC_Y 1 +#define SRC_Z 2 +#define SRC_W 3 +#define SRC_ZERO 4 +#define SRC_ONE 5 + +#define A1_SRC0_CHANNEL_X_NEGATE ((int)(1u<<31)) +#define A1_SRC0_CHANNEL_X_SHIFT 28 +#define A1_SRC0_CHANNEL_Y_NEGATE (1<<27) +#define A1_SRC0_CHANNEL_Y_SHIFT 24 +#define A1_SRC0_CHANNEL_Z_NEGATE (1<<23) +#define A1_SRC0_CHANNEL_Z_SHIFT 20 +#define A1_SRC0_CHANNEL_W_NEGATE (1<<19) +#define A1_SRC0_CHANNEL_W_SHIFT 16 +#define A1_SRC1_TYPE_SHIFT 13 +#define A1_SRC1_NR_SHIFT 8 +#define A1_SRC1_CHANNEL_X_NEGATE (1<<7) +#define A1_SRC1_CHANNEL_X_SHIFT 4 +#define A1_SRC1_CHANNEL_Y_NEGATE (1<<3) +#define A1_SRC1_CHANNEL_Y_SHIFT 0 + +#define A2_SRC1_CHANNEL_Z_NEGATE ((int)(1u<<31)) +#define A2_SRC1_CHANNEL_Z_SHIFT 28 +#define A2_SRC1_CHANNEL_W_NEGATE (1<<27) +#define A2_SRC1_CHANNEL_W_SHIFT 24 +#define A2_SRC2_TYPE_SHIFT 21 +#define A2_SRC2_NR_SHIFT 16 +#define A2_SRC2_CHANNEL_X_NEGATE (1<<15) +#define A2_SRC2_CHANNEL_X_SHIFT 12 +#define A2_SRC2_CHANNEL_Y_NEGATE (1<<11) +#define A2_SRC2_CHANNEL_Y_SHIFT 8 +#define A2_SRC2_CHANNEL_Z_NEGATE (1<<7) +#define A2_SRC2_CHANNEL_Z_SHIFT 4 +#define A2_SRC2_CHANNEL_W_NEGATE (1<<3) +#define A2_SRC2_CHANNEL_W_SHIFT 0 + +/* Texture instructions */ +#define T0_TEXLD (0x15<<24) /* Sample texture using predeclared + * sampler and address, and output + * filtered texel data to destination + * register */ +#define T0_TEXLDP (0x16<<24) /* Same as texld but performs a + * perspective divide of the texture + * coordinate .xyz values by .w before + * sampling. */ +#define T0_TEXLDB (0x17<<24) /* Same as texld but biases the + * computed LOD by w. Only S4.6 two's + * comp is used. This implies that a + * float to fixed conversion is + * done. */ +#define T0_TEXKILL (0x18<<24) /* Does not perform a sampling + * operation. Simply kills the pixel + * if any channel of the address + * register is < 0.0. */ +#define T0_DEST_TYPE_SHIFT 19 +/* Allow: R, OC, OD, U */ +/* Note: U (unpreserved) regs do not retain their values between + * phases (cannot be used for feedback) + * + * Note: oC and OD registers can only be used as the destination of a + * texture instruction once per phase (this is an implementation + * restriction). + */ +#define T0_DEST_NR_SHIFT 14 +/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ +#define T0_SAMPLER_NR_SHIFT 0 /* This field ignored for TEXKILL */ +#define T0_SAMPLER_NR_MASK (0xf<<0) + +#define T1_ADDRESS_REG_TYPE_SHIFT 24 /* Reg to use as texture coord */ +/* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */ +#define T1_ADDRESS_REG_NR_SHIFT 17 +#define T2_MBZ 0 + +/* Declaration instructions */ +#define D0_DCL (0x19<<24) /* Declare a t (interpolated attrib) + * register or an s (sampler) + * register. */ +#define D0_SAMPLE_TYPE_SHIFT 22 +#define D0_SAMPLE_TYPE_2D (0x0<<22) +#define D0_SAMPLE_TYPE_CUBE (0x1<<22) +#define D0_SAMPLE_TYPE_VOLUME (0x2<<22) +#define D0_SAMPLE_TYPE_MASK (0x3<<22) + +#define D0_TYPE_SHIFT 19 +/* Allow: T, S */ +#define D0_NR_SHIFT 14 +/* Allow T: 0..10, S: 0..15 */ +#define D0_CHANNEL_X (1<<10) +#define D0_CHANNEL_Y (2<<10) +#define D0_CHANNEL_Z (4<<10) +#define D0_CHANNEL_W (8<<10) +#define D0_CHANNEL_ALL (0xf<<10) +#define D0_CHANNEL_NONE (0<<10) + +#define D0_CHANNEL_XY (D0_CHANNEL_X|D0_CHANNEL_Y) +#define D0_CHANNEL_XYZ (D0_CHANNEL_XY|D0_CHANNEL_Z) + +/* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse + * or specular declarations. + * + * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw) + * + * Must be zero for S (sampler) dcls + */ +#define D1_MBZ 0 +#define D2_MBZ 0 + + +/* MASK_* are the unshifted bitmasks of the destination mask in arithmetic + * operations + */ +#define MASK_X 0x1 +#define MASK_Y 0x2 +#define MASK_Z 0x4 +#define MASK_W 0x8 +#define MASK_XYZ (MASK_X | MASK_Y | MASK_Z) +#define MASK_XYZW (MASK_XYZ | MASK_W) +#define MASK_SATURATE 0x10 + +/* Temporary, undeclared regs. Preserved between phases */ +#define FS_R0 ((REG_TYPE_R << REG_TYPE_SHIFT) | 0) +#define FS_R1 ((REG_TYPE_R << REG_TYPE_SHIFT) | 1) +#define FS_R2 ((REG_TYPE_R << REG_TYPE_SHIFT) | 2) +#define FS_R3 ((REG_TYPE_R << REG_TYPE_SHIFT) | 3) + +/* Texture coordinate regs. Must be declared. */ +#define FS_T0 ((REG_TYPE_T << REG_TYPE_SHIFT) | 0) +#define FS_T1 ((REG_TYPE_T << REG_TYPE_SHIFT) | 1) +#define FS_T2 ((REG_TYPE_T << REG_TYPE_SHIFT) | 2) +#define FS_T3 ((REG_TYPE_T << REG_TYPE_SHIFT) | 3) +#define FS_T4 ((REG_TYPE_T << REG_TYPE_SHIFT) | 4) +#define FS_T5 ((REG_TYPE_T << REG_TYPE_SHIFT) | 5) +#define FS_T6 ((REG_TYPE_T << REG_TYPE_SHIFT) | 6) +#define FS_T7 ((REG_TYPE_T << REG_TYPE_SHIFT) | 7) +#define FS_T8 ((REG_TYPE_T << REG_TYPE_SHIFT) | 8) +#define FS_T9 ((REG_TYPE_T << REG_TYPE_SHIFT) | 9) +#define FS_T10 ((REG_TYPE_T << REG_TYPE_SHIFT) | 10) + +/* Constant values */ +#define FS_C0 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 0) +#define FS_C1 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 1) +#define FS_C2 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 2) +#define FS_C3 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 3) +#define FS_C4 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 4) +#define FS_C5 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 5) +#define FS_C6 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 6) +#define FS_C7 ((REG_TYPE_CONST << REG_TYPE_SHIFT) | 7) + +/* Sampler regs */ +#define FS_S0 ((REG_TYPE_S << REG_TYPE_SHIFT) | 0) +#define FS_S1 ((REG_TYPE_S << REG_TYPE_SHIFT) | 1) +#define FS_S2 ((REG_TYPE_S << REG_TYPE_SHIFT) | 2) +#define FS_S3 ((REG_TYPE_S << REG_TYPE_SHIFT) | 3) + +/* Output color */ +#define FS_OC ((REG_TYPE_OC << REG_TYPE_SHIFT) | 0) + +/* Output depth */ +#define FS_OD ((REG_TYPE_OD << REG_TYPE_SHIFT) | 0) + +/* Unpreserved temporary regs */ +#define FS_U0 ((REG_TYPE_U << REG_TYPE_SHIFT) | 0) +#define FS_U1 ((REG_TYPE_U << REG_TYPE_SHIFT) | 1) +#define FS_U2 ((REG_TYPE_U << REG_TYPE_SHIFT) | 2) +#define FS_U3 ((REG_TYPE_U << REG_TYPE_SHIFT) | 3) + +#define X_CHANNEL_SHIFT (REG_TYPE_SHIFT + 3) +#define Y_CHANNEL_SHIFT (X_CHANNEL_SHIFT + 4) +#define Z_CHANNEL_SHIFT (Y_CHANNEL_SHIFT + 4) +#define W_CHANNEL_SHIFT (Z_CHANNEL_SHIFT + 4) + +#define REG_CHANNEL_MASK 0xf + +#define REG_NR(reg) ((reg) & REG_NR_MASK) +#define REG_TYPE(reg) (((reg) >> REG_TYPE_SHIFT) & REG_TYPE_MASK) +#define REG_X(reg) (((reg) >> X_CHANNEL_SHIFT) & REG_CHANNEL_MASK) +#define REG_Y(reg) (((reg) >> Y_CHANNEL_SHIFT) & REG_CHANNEL_MASK) +#define REG_Z(reg) (((reg) >> Z_CHANNEL_SHIFT) & REG_CHANNEL_MASK) +#define REG_W(reg) (((reg) >> W_CHANNEL_SHIFT) & REG_CHANNEL_MASK) + +enum i915_fs_channel { + X_CHANNEL_VAL = 0, + Y_CHANNEL_VAL, + Z_CHANNEL_VAL, + W_CHANNEL_VAL, + ZERO_CHANNEL_VAL, + ONE_CHANNEL_VAL, + + NEG_X_CHANNEL_VAL = X_CHANNEL_VAL | 0x8, + NEG_Y_CHANNEL_VAL = Y_CHANNEL_VAL | 0x8, + NEG_Z_CHANNEL_VAL = Z_CHANNEL_VAL | 0x8, + NEG_W_CHANNEL_VAL = W_CHANNEL_VAL | 0x8, + NEG_ONE_CHANNEL_VAL = ONE_CHANNEL_VAL | 0x8 +}; + +#define i915_fs_operand(reg, x, y, z, w) \ + (reg) | \ + (x##_CHANNEL_VAL << X_CHANNEL_SHIFT) | \ + (y##_CHANNEL_VAL << Y_CHANNEL_SHIFT) | \ + (z##_CHANNEL_VAL << Z_CHANNEL_SHIFT) | \ + (w##_CHANNEL_VAL << W_CHANNEL_SHIFT) + +/* + * Construct an operand description for using a register with no swizzling + */ +#define i915_fs_operand_reg(reg) \ + i915_fs_operand(reg, X, Y, Z, W) + +#define i915_fs_operand_reg_negate(reg) \ + i915_fs_operand(reg, NEG_X, NEG_Y, NEG_Z, NEG_W) + +/* + * Returns an operand containing (0.0, 0.0, 0.0, 0.0). + */ +#define i915_fs_operand_zero() i915_fs_operand(FS_R0, ZERO, ZERO, ZERO, ZERO) + +/* + * Returns an unused operand + */ +#define i915_fs_operand_none() i915_fs_operand_zero() + +/* + * Returns an operand containing (1.0, 1.0, 1.0, 1.0). + */ +#define i915_fs_operand_one() i915_fs_operand(FS_R0, ONE, ONE, ONE, ONE) + +#define i915_get_hardware_channel_val(val, shift, negate) \ + (((val & 0x7) << shift) | ((val & 0x8) ? negate : 0)) + +/* + * Outputs a fragment shader command to declare a sampler or texture register. + */ +#define i915_fs_dcl(reg) \ +do { \ + OUT_DWORD (D0_DCL | \ + (REG_TYPE(reg) << D0_TYPE_SHIFT) | \ + (REG_NR(reg) << D0_NR_SHIFT) | \ + ((REG_TYPE(reg) != REG_TYPE_S) ? D0_CHANNEL_ALL : 0)); \ + OUT_DWORD (0); \ + OUT_DWORD (0); \ +} while (0) + +#define i915_fs_texld(dest_reg, sampler_reg, address_reg) \ +do { \ + OUT_DWORD (T0_TEXLD | \ + (REG_TYPE(dest_reg) << T0_DEST_TYPE_SHIFT) | \ + (REG_NR(dest_reg) << T0_DEST_NR_SHIFT) | \ + (REG_NR(sampler_reg) << T0_SAMPLER_NR_SHIFT)); \ + OUT_DWORD((REG_TYPE(address_reg) << T1_ADDRESS_REG_TYPE_SHIFT) | \ + (REG_NR(address_reg) << T1_ADDRESS_REG_NR_SHIFT)); \ + OUT_DWORD (0); \ +} while (0) + +#define i915_fs_arith_masked(op, dest_reg, dest_mask, operand0, operand1, operand2) \ + _i915_fs_arith_masked(A0_##op, dest_reg, dest_mask, operand0, operand1, operand2) + +#define i915_fs_arith(op, dest_reg, operand0, operand1, operand2) \ + _i915_fs_arith(A0_##op, dest_reg, operand0, operand1, operand2) + +#define _i915_fs_arith_masked(cmd, dest_reg, dest_mask, operand0, operand1, operand2) \ +do { \ + /* Set up destination register and write mask */ \ + OUT_DWORD (cmd | \ + (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \ + (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \ + (((dest_mask) & ~MASK_SATURATE) << A0_DEST_CHANNEL_SHIFT) | \ + (((dest_mask) & MASK_SATURATE) ? A0_DEST_SATURATE : 0) | \ + /* Set up operand 0 */ \ + (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \ + (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \ + OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \ + A1_SRC0_CHANNEL_X_SHIFT, \ + A1_SRC0_CHANNEL_X_NEGATE) | \ + i915_get_hardware_channel_val(REG_Y(operand0), \ + A1_SRC0_CHANNEL_Y_SHIFT, \ + A1_SRC0_CHANNEL_Y_NEGATE) | \ + i915_get_hardware_channel_val(REG_Z(operand0), \ + A1_SRC0_CHANNEL_Z_SHIFT, \ + A1_SRC0_CHANNEL_Z_NEGATE) | \ + i915_get_hardware_channel_val(REG_W(operand0), \ + A1_SRC0_CHANNEL_W_SHIFT, \ + A1_SRC0_CHANNEL_W_NEGATE) | \ + /* Set up operand 1 */ \ + (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \ + (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \ + i915_get_hardware_channel_val(REG_X(operand1), \ + A1_SRC1_CHANNEL_X_SHIFT, \ + A1_SRC1_CHANNEL_X_NEGATE) | \ + i915_get_hardware_channel_val(REG_Y(operand1), \ + A1_SRC1_CHANNEL_Y_SHIFT, \ + A1_SRC1_CHANNEL_Y_NEGATE)); \ + OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \ + A2_SRC1_CHANNEL_Z_SHIFT, \ + A2_SRC1_CHANNEL_Z_NEGATE) | \ + i915_get_hardware_channel_val(REG_W(operand1), \ + A2_SRC1_CHANNEL_W_SHIFT, \ + A2_SRC1_CHANNEL_W_NEGATE) | \ + /* Set up operand 2 */ \ + (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \ + (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \ + i915_get_hardware_channel_val(REG_X(operand2), \ + A2_SRC2_CHANNEL_X_SHIFT, \ + A2_SRC2_CHANNEL_X_NEGATE) | \ + i915_get_hardware_channel_val(REG_Y(operand2), \ + A2_SRC2_CHANNEL_Y_SHIFT, \ + A2_SRC2_CHANNEL_Y_NEGATE) | \ + i915_get_hardware_channel_val(REG_Z(operand2), \ + A2_SRC2_CHANNEL_Z_SHIFT, \ + A2_SRC2_CHANNEL_Z_NEGATE) | \ + i915_get_hardware_channel_val(REG_W(operand2), \ + A2_SRC2_CHANNEL_W_SHIFT, \ + A2_SRC2_CHANNEL_W_NEGATE)); \ +} while (0) + +#define _i915_fs_arith(cmd, dest_reg, operand0, operand1, operand2) do {\ + /* Set up destination register and write mask */ \ + OUT_DWORD (cmd | \ + (REG_TYPE(dest_reg) << A0_DEST_TYPE_SHIFT) | \ + (REG_NR(dest_reg) << A0_DEST_NR_SHIFT) | \ + (A0_DEST_CHANNEL_ALL) | \ + /* Set up operand 0 */ \ + (REG_TYPE(operand0) << A0_SRC0_TYPE_SHIFT) | \ + (REG_NR(operand0) << A0_SRC0_NR_SHIFT)); \ + OUT_DWORD (i915_get_hardware_channel_val(REG_X(operand0), \ + A1_SRC0_CHANNEL_X_SHIFT, \ + A1_SRC0_CHANNEL_X_NEGATE) | \ + i915_get_hardware_channel_val(REG_Y(operand0), \ + A1_SRC0_CHANNEL_Y_SHIFT, \ + A1_SRC0_CHANNEL_Y_NEGATE) | \ + i915_get_hardware_channel_val(REG_Z(operand0), \ + A1_SRC0_CHANNEL_Z_SHIFT, \ + A1_SRC0_CHANNEL_Z_NEGATE) | \ + i915_get_hardware_channel_val(REG_W(operand0), \ + A1_SRC0_CHANNEL_W_SHIFT, \ + A1_SRC0_CHANNEL_W_NEGATE) | \ + /* Set up operand 1 */ \ + (REG_TYPE(operand1) << A1_SRC1_TYPE_SHIFT) | \ + (REG_NR(operand1) << A1_SRC1_NR_SHIFT) | \ + i915_get_hardware_channel_val(REG_X(operand1), \ + A1_SRC1_CHANNEL_X_SHIFT, \ + A1_SRC1_CHANNEL_X_NEGATE) | \ + i915_get_hardware_channel_val(REG_Y(operand1), \ + A1_SRC1_CHANNEL_Y_SHIFT, \ + A1_SRC1_CHANNEL_Y_NEGATE)); \ + OUT_DWORD (i915_get_hardware_channel_val(REG_Z(operand1), \ + A2_SRC1_CHANNEL_Z_SHIFT, \ + A2_SRC1_CHANNEL_Z_NEGATE) | \ + i915_get_hardware_channel_val(REG_W(operand1), \ + A2_SRC1_CHANNEL_W_SHIFT, \ + A2_SRC1_CHANNEL_W_NEGATE) | \ + /* Set up operand 2 */ \ + (REG_TYPE(operand2) << A2_SRC2_TYPE_SHIFT) | \ + (REG_NR(operand2) << A2_SRC2_NR_SHIFT) | \ + i915_get_hardware_channel_val(REG_X(operand2), \ + A2_SRC2_CHANNEL_X_SHIFT, \ + A2_SRC2_CHANNEL_X_NEGATE) | \ + i915_get_hardware_channel_val(REG_Y(operand2), \ + A2_SRC2_CHANNEL_Y_SHIFT, \ + A2_SRC2_CHANNEL_Y_NEGATE) | \ + i915_get_hardware_channel_val(REG_Z(operand2), \ + A2_SRC2_CHANNEL_Z_SHIFT, \ + A2_SRC2_CHANNEL_Z_NEGATE) | \ + i915_get_hardware_channel_val(REG_W(operand2), \ + A2_SRC2_CHANNEL_W_SHIFT, \ + A2_SRC2_CHANNEL_W_NEGATE)); \ +} while (0) + +#define i915_fs_mov(dest_reg, operand0) \ + i915_fs_arith(MOV, dest_reg, \ + operand0, \ + i915_fs_operand_none(), \ + i915_fs_operand_none()) + +#define i915_fs_mov_masked(dest_reg, dest_mask, operand0) \ + i915_fs_arith_masked (MOV, dest_reg, dest_mask, \ + operand0, \ + i915_fs_operand_none(), \ + i915_fs_operand_none()) + + +#define i915_fs_frc(dest_reg, operand0) \ + i915_fs_arith (FRC, dest_reg, \ + operand0, \ + i915_fs_operand_none(), \ + i915_fs_operand_none()) + +/* Add operand0 and operand1 and put the result in dest_reg */ +#define i915_fs_add(dest_reg, operand0, operand1) \ + i915_fs_arith (ADD, dest_reg, \ + operand0, operand1, \ + i915_fs_operand_none()) + +/* Multiply operand0 and operand1 and put the result in dest_reg */ +#define i915_fs_mul(dest_reg, operand0, operand1) \ + i915_fs_arith (MUL, dest_reg, \ + operand0, operand1, \ + i915_fs_operand_none()) + +/* Computes 1/sqrt(operand0.replicate_swizzle) puts the result in dest_reg */ +#define i915_fs_rsq(dest_reg, dest_mask, operand0) \ +do { \ + if (dest_mask) { \ + i915_fs_arith_masked (RSQ, dest_reg, dest_mask, \ + operand0, \ + i915_fs_operand_none (), \ + i915_fs_operand_none ()); \ + } else { \ + i915_fs_arith (RSQ, dest_reg, \ + operand0, \ + i915_fs_operand_none (), \ + i915_fs_operand_none ()); \ + } \ +} while (0) + +/* Puts the minimum of operand0 and operand1 in dest_reg */ +#define i915_fs_min(dest_reg, operand0, operand1) \ + i915_fs_arith (MIN, dest_reg, \ + operand0, operand1, \ + i915_fs_operand_none()) + +/* Puts the maximum of operand0 and operand1 in dest_reg */ +#define i915_fs_max(dest_reg, operand0, operand1) \ + i915_fs_arith (MAX, dest_reg, \ + operand0, operand1, \ + i915_fs_operand_none()) + +#define i915_fs_cmp(dest_reg, operand0, operand1, operand2) \ + i915_fs_arith (CMP, dest_reg, operand0, operand1, operand2) + +/* Perform operand0 * operand1 + operand2 and put the result in dest_reg */ +#define i915_fs_mad(dest_reg, dest_mask, op0, op1, op2) \ +do { \ + if (dest_mask) { \ + i915_fs_arith_masked (MAD, dest_reg, dest_mask, op0, op1, op2); \ + } else { \ + i915_fs_arith (MAD, dest_reg, op0, op1, op2); \ + } \ +} while (0) + +#define i915_fs_dp2add(dest_reg, dest_mask, op0, op1, op2) \ +do { \ + if (dest_mask) { \ + i915_fs_arith_masked (DP2ADD, dest_reg, dest_mask, op0, op1, op2); \ + } else { \ + i915_fs_arith (DP2ADD, dest_reg, op0, op1, op2); \ + } \ +} while (0) + +/* + * Perform a 3-component dot-product of operand0 and operand1 and put the + * resulting scalar in the channels of dest_reg specified by the dest_mask. + */ +#define i915_fs_dp3(dest_reg, dest_mask, op0, op1) \ +do { \ + if (dest_mask) { \ + i915_fs_arith_masked (DP3, dest_reg, dest_mask, \ + op0, op1,\ + i915_fs_operand_none()); \ + } else { \ + i915_fs_arith (DP3, dest_reg, op0, op1,\ + i915_fs_operand_none()); \ + } \ +} while (0) + +static inline uint32_t cairo_const +i915_fs_operand_pure_alpha (int pure) +{ + if (pure & (1 << 3)) + return i915_fs_operand_one (); + else + return i915_fs_operand_zero (); +} + +#define I915_TILING_DEFAULT I915_TILING_Y +#define I915_BO_CACHE_BUCKETS 13 /* cache surfaces up to 16 MiB */ + +typedef struct i915_surface i915_surface_t; +typedef struct i915_device i915_device_t; +typedef struct i915_shader i915_shader_t; + +typedef void (*i915_add_rectangle_func_t) (const i915_shader_t *shader, + int x, int y, + int w, int h); + +#define IMAGE_CACHE_WIDTH 1024 +#define IMAGE_CACHE_HEIGHT 1024 + +typedef struct i915_image_private { + cairo_rtree_node_t node; + intel_buffer_cache_t *container; +} i915_image_private_t; + +#define I915_BATCH_SIZE (128*1024) +#define I915_VBO_SIZE (512*1024) +#define I915_MAX_RELOCS 2048 + +enum { + I915_DEBUG_EXEC = 0x1, + I915_DEBUG_SYNC = 0x2, + I915_DEBUG_BATCH = 0x4, + I915_DEBUG_BUFFER = 0x8, + I915_DEBUG_BUFFER_CACHE = 0x10, + I915_DEBUG_BUFFER_ALLOC = 0x20, + I915_DEBUG_GLYPHS = 0x40, + I915_DEBUG_MAP = 0x80, + I915_DEBUG_THROTTLE = 0x100, +}; + +struct i915_device { + intel_device_t intel; + + cairo_bool_t debug; + + i915_shader_t *shader; /* note: only valid during geometry emission */ + + struct i915_batch { + intel_bo_t *target_bo[I915_MAX_RELOCS]; + size_t gtt_avail_size; + size_t est_gtt_size; + size_t total_gtt_size; + + uint16_t fences; + uint16_t fences_avail; + uint16_t reloc_count; + uint16_t exec_count; + uint16_t used; + + struct drm_i915_gem_exec_object2 exec[I915_MAX_RELOCS]; + struct drm_i915_gem_relocation_entry reloc[I915_MAX_RELOCS]; + } batch; + + uint32_t vbo; + uint32_t vbo_offset; + uint32_t vbo_used; + uint32_t vbo_max_index; + uint32_t vertex_index; + uint32_t vertex_count; + uint32_t floats_per_vertex; + uint32_t rectangle_size; + intel_bo_t *last_vbo; + uint32_t last_vbo_offset; + uint32_t last_vbo_space; + + i915_surface_t *current_target; + uint32_t current_size; + uint32_t current_diffuse; + uint32_t current_colorbuf; + uint32_t *current_source; + uint32_t *current_mask; + uint32_t *current_clip; + uint32_t current_program; + uint32_t current_texcoords; + uint32_t current_blend; + uint32_t current_constants[8*4]; + uint32_t current_n_constants; + uint32_t current_samplers[2*4]; + uint32_t current_maps[4*4]; + uint32_t current_n_samplers; + uint32_t current_n_maps; + uint32_t last_source_fragment; + uint32_t clear_alpha; + + cairo_list_t image_caches[2]; + + uint32_t batch_header[13]; + uint32_t batch_base[I915_BATCH_SIZE / sizeof (uint32_t)]; + uint8_t vbo_base[I915_VBO_SIZE]; +}; + +enum { + CURRENT_SOURCE = 0x1, + CURRENT_MASK = 0x2, + CURRENT_CLIP = 0x4 +}; + +typedef enum { + VS_ZERO, + VS_CONSTANT, + VS_LINEAR, + VS_TEXTURE, + VS_TEXTURE_16, +} i915_vertex_shader_t; + +typedef enum { + FS_ZERO, + FS_ONE, + FS_PURE, + FS_CONSTANT, + FS_DIFFUSE, + FS_LINEAR, + FS_RADIAL, + FS_TEXTURE, + FS_YUV, + FS_SPANS, +} i915_fragment_shader_t; + +#define FS_DETAILS_SHIFT 4 + +typedef enum { + PATTERN_BASE, + PATTERN_CONSTANT, + PATTERN_LINEAR, + PATTERN_RADIAL, + PATTERN_TEXTURE, +} i915_shader_channel_t; + +struct i915_surface { + intel_surface_t intel; + + uint32_t map0, map1; + uint32_t colorbuf; + + cairo_bool_t deferred_clear; + uint32_t offset; + uint32_t is_current_texture; + + i915_image_private_t *cache; + + intel_bo_t *stencil; + uint32_t stencil_stride; + uint32_t stencil_offset; +}; + +typedef enum { + NONE = 0, + YUV_I420, + /* XXX */ + YUV_YV12, + YUV_YUY2, + YUV_UYVY, +} i915_packed_pixel_t; + +/* read-only container */ +#define I915_PACKED_PIXEL_SURFACE_TYPE 0x1000 +typedef struct i915_packed_pixel_surface { + cairo_surface_t base; + + i915_packed_pixel_t pixel; + + i915_device_t *device; + intel_bo_t *bo; + uint32_t is_current_texture; + + uint32_t offset[4]; + uint32_t stride[4]; + uint32_t width[4]; + uint32_t height[4]; + uint32_t map0[4], map1[4]; +} i915_packed_pixel_surface_t; + +struct i915_shader { + i915_device_t *device; + i915_surface_t *target; + + cairo_operator_t op; + uint32_t blend; + float opacity; + cairo_content_t content; + + cairo_bool_t committed; + cairo_bool_t need_combine; + + i915_add_rectangle_func_t add_rectangle; + + union i915_shader_channel { + struct { + i915_vertex_shader_t vertex; + i915_fragment_shader_t fragment; + i915_shader_channel_t pattern; + } type; + struct i915_shader_base { + i915_vertex_shader_t vertex; + i915_fragment_shader_t fragment; + i915_shader_channel_t pattern; + uint32_t texfmt; + cairo_content_t content; + uint32_t mode; + intel_bo_t *bo; + uint32_t n_samplers; + uint32_t offset[4]; + uint32_t map[2*4]; + uint32_t sampler[2]; + cairo_matrix_t matrix; + } base; + struct i915_shader_solid { + struct i915_shader_base base; + cairo_color_t color; + int pure; + } solid; + struct i915_shader_linear { + struct i915_shader_base base; + struct { + float red, green, blue, alpha; + } color0, color1; + float dx, dy, offset; + } linear; + struct i915_shader_radial { + struct i915_shader_base base; + float constants[8]; + } radial; + struct i915_shader_surface { + struct i915_shader_base base; + i915_packed_pixel_t pixel; + } surface; + } source, mask, clip, dst; + + jmp_buf unwind; +}; + +enum i915_shader_linear_mode { + /* XXX REFLECT */ + LINEAR_TEXTURE, + LINEAR_NONE, + LINEAR_REPEAT, + LINEAR_PAD, +}; + +enum i915_shader_radial_mode { + RADIAL_ONE, + RADIAL_TWO +}; + +typedef cairo_status_t +(*i915_spans_func_t) (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +i915_clip_and_composite_spans (i915_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + i915_spans_func_t draw_func, + void *draw_closure, + const cairo_composite_rectangles_t*extents, + cairo_clip_t *clip, + double opacity); + +cairo_private cairo_surface_t * +i915_surface_create_internal (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height, + uint32_t tiling, + cairo_bool_t gpu_target); + +cairo_private i915_surface_t * +i915_surface_create_from_cacheable_image_internal (i915_device_t *device, + cairo_image_surface_t *image); + +cairo_private void +i915_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_int_status_t +i915_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, + cairo_clip_t *clip, + int *num_remaining); + +static inline int cairo_const +i915_tiling_height (uint32_t tiling, int height) +{ + switch (tiling) { + default: + case I915_TILING_NONE: return (height + 1) & -2; + case I915_TILING_X: return (height + 7) & -8; + case I915_TILING_Y: return (height + 31) & -32; + } +} + +static inline uint32_t cairo_const +i915_tiling_stride (int format, uint32_t stride) +{ + uint32_t tile_width; + + /* use 64B alignment so that the buffer may be used as a scanout */ + if (format == I915_TILING_NONE) + return (stride + 63) & -64; + + tile_width = 512; + /* XXX Currently the kernel enforces a tile_width of 512 for TILING_Y. + + the docs are a bit confused on that front + once we enable it on 915 we'll find out what the tile width size should be in the fence setup + it could be that 915 has y tiling but that the minimum width is 512 or something + yeah it's probably 128 on 915 also + it's just that we haven't tested + but I wasn't thinking that the tile widths were the same + only that in order to fence y tiles on 915 you needed pitch to be a multiple of 4 y tiles (or something like that) + + tile_width = format == I915_TILING_Y ? 128 : 512; + */ + + /* needs a pot tile width */ + while (tile_width < stride) + tile_width <<= 1; + + return tile_width; +} + +static inline uint32_t cairo_const +i915_tiling_size (uint32_t tiling, uint32_t size) +{ + uint32_t fence; + + if (tiling == I915_TILING_NONE) + return (size + 4095) & -4096; + + fence = 1024 * 1024; /* 1 MiB */ + while (fence < size) + fence <<= 1; + + return fence; +} + +static inline cairo_bool_t cairo_const +i915_texture_filter_is_nearest (cairo_filter_t filter) +{ + switch (filter) { + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + return FALSE; + default: + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return TRUE; + } +} + +static inline uint32_t cairo_const +i915_texture_filter (cairo_filter_t filter) +{ + switch (filter) { + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + return + (FILTER_LINEAR << SS2_MAG_FILTER_SHIFT) | + (FILTER_LINEAR << SS2_MIN_FILTER_SHIFT); + default: + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return + (FILTER_NEAREST << SS2_MAG_FILTER_SHIFT) | + (FILTER_NEAREST << SS2_MIN_FILTER_SHIFT); + } +} + +static inline uint32_t cairo_const +i915_texture_extend (cairo_extend_t extend) +{ + switch (extend) { + default: + case CAIRO_EXTEND_NONE: + return + (TEXCOORDMODE_CLAMP_BORDER << SS3_TCX_ADDR_MODE_SHIFT) | + (TEXCOORDMODE_CLAMP_BORDER << SS3_TCY_ADDR_MODE_SHIFT); + case CAIRO_EXTEND_REPEAT: + return + (TEXCOORDMODE_WRAP << SS3_TCX_ADDR_MODE_SHIFT) | + (TEXCOORDMODE_WRAP << SS3_TCY_ADDR_MODE_SHIFT); + case CAIRO_EXTEND_PAD: + return + (TEXCOORDMODE_CLAMP_EDGE << SS3_TCX_ADDR_MODE_SHIFT) | + (TEXCOORDMODE_CLAMP_EDGE << SS3_TCY_ADDR_MODE_SHIFT); + case CAIRO_EXTEND_REFLECT: + return + (TEXCOORDMODE_MIRROR << SS3_TCX_ADDR_MODE_SHIFT) | + (TEXCOORDMODE_MIRROR << SS3_TCY_ADDR_MODE_SHIFT); + } +} + +static inline uint32_t cairo_const +BUF_tiling (uint32_t tiling) +{ + switch (tiling) { + default: + case I915_TILING_NONE: return 0; + case I915_TILING_X: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_X; + case I915_TILING_Y: return BUF_3D_TILED_SURFACE | BUF_3D_TILE_WALK_Y; + } +} + +#define OUT_DWORD(dword) i915_batch_emit_dword (device, dword) +#define OUT_RELOC(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, FALSE) +#define OUT_RELOC_FENCED(surface, read, write) i915_batch_emit_reloc (device, to_intel_bo (surface->intel.drm.bo), surface->offset, read, write, TRUE) + +#define FS_LOCALS \ + uint32_t *_shader_start + +#define FS_BEGIN() \ +do { \ + _shader_start = BATCH_PTR (device); \ + OUT_DWORD (_3DSTATE_PIXEL_SHADER_PROGRAM); \ +} while (0) + +#define FS_END() \ +do { \ + *_shader_start |= BATCH_PTR (device) - _shader_start - 2; \ +} while (0); + +static inline int32_t +i915_batch_space (i915_device_t *device) +{ + /* leave room for RECTLIST(4) + MI_BUFFER_END + MI_NOOP */ + return sizeof (device->batch_base) - (device->batch.used << 2) - 32; +} + +static inline cairo_bool_t +i915_check_aperture_size (const i915_device_t *device, int relocs, size_t est_size, size_t size) +{ + return device->batch.reloc_count + relocs < I915_MAX_RELOCS - 2 && + device->batch.est_gtt_size + est_size <= device->batch.gtt_avail_size && + device->batch.total_gtt_size + size <= device->intel.gtt_avail_size; +} + +static inline cairo_bool_t +i915_check_aperture (const i915_device_t *device, intel_bo_t **bo_array, int count) +{ + uint32_t relocs = 0, est_size = 0, size = 0; + + while (count--) { + const intel_bo_t *bo = *bo_array++; + if (bo->exec == NULL) { + relocs++; + size += bo->base.size; + if (!bo->busy) + est_size += bo->base.size; + } + } + + return i915_check_aperture_size (device, relocs, est_size, size); +} + +static inline cairo_bool_t +i915_check_aperture_and_fences (const i915_device_t *device, intel_bo_t **bo_array, int count) +{ + uint32_t relocs = 0, est_size = 0, size = 0; + uint32_t fences = 0; + + while (count--) { + const intel_bo_t *bo = *bo_array++; + if (bo->exec == NULL) { + relocs++; + size += bo->base.size; + if (!bo->busy) + est_size += bo->base.size; + if (bo->tiling != I915_TILING_NONE) + fences++; + } else if (bo->tiling != I915_TILING_NONE) { + if ((bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) + fences++; + } + } + + return i915_check_aperture_size (device, relocs, est_size, size) && + device->batch.fences + fences <= device->batch.fences_avail; +} + +#define BATCH_PTR(device) &(device)->batch_base[(device)->batch.used] +static inline void +i915_batch_emit_dword (i915_device_t *device, uint32_t dword) +{ + device->batch_base[device->batch.used++] = dword; +} + +cairo_private void +i915_batch_add_reloc (i915_device_t *device, uint32_t pos, + intel_bo_t *bo, + uint32_t offset, + uint32_t read_domains, + uint32_t write_domain, + cairo_bool_t needs_fence); + +static inline void +i915_batch_fill_reloc (i915_device_t *device, uint32_t pos, + intel_bo_t *bo, + uint32_t offset, + uint32_t read_domains, + uint32_t write_domain) +{ + i915_batch_add_reloc (device, pos, + bo, offset, + read_domains, write_domain, + FALSE); + device->batch_base[pos] = bo->offset + offset; +} + +static inline void +i915_batch_emit_reloc (i915_device_t *device, + intel_bo_t *bo, + uint32_t offset, + uint32_t read_domains, + uint32_t write_domain, + cairo_bool_t needs_fence) +{ + i915_batch_add_reloc (device, device->batch.used, + bo, offset, + read_domains, write_domain, + needs_fence); + i915_batch_emit_dword (device, bo->offset + offset); +} + +cairo_private void +i915_vbo_flush (i915_device_t *device); + +cairo_private void +i915_vbo_finish (i915_device_t *device); + +cairo_private cairo_status_t +i915_batch_flush (i915_device_t *device); + +static inline float * +i915_add_rectangle (i915_device_t *device) +{ + float *vertices; + uint32_t size; + + assert (device->floats_per_vertex); + assert (device->rectangle_size == 3*device->floats_per_vertex*sizeof(float)); + + size = device->rectangle_size; + if (unlikely (device->vbo_offset + size > I915_VBO_SIZE)) + i915_vbo_finish (device); + + vertices = (float *) (device->vbo_base + device->vbo_offset); + device->vbo_used = device->vbo_offset += size; + device->vertex_count += 3; + return vertices; +} + +static inline i915_device_t * +i915_device (i915_surface_t *surface) +{ + return (i915_device_t *) surface->intel.drm.base.device; +} + +cairo_private cairo_status_t +i915_surface_clear (i915_surface_t *dst); + +cairo_private void +i915_set_dst (i915_device_t *device, i915_surface_t *dst); + +cairo_private void +i915_shader_init (i915_shader_t *shader, + i915_surface_t *dst, + cairo_operator_t op, + double opacity); + +cairo_private cairo_status_t +i915_shader_acquire_pattern (i915_shader_t *shader, + union i915_shader_channel *src, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents); + +cairo_private void +i915_shader_set_clip (i915_shader_t *shader, + cairo_clip_t *clip); + +cairo_private int +i915_shader_num_texcoords (const i915_shader_t *shader); + +static inline double cairo_const +i915_shader_linear_texcoord (const struct i915_shader_linear *l, + double src_x, double src_y) +{ + return l->dx * src_x + l->dy * src_y + l->offset; +} + +cairo_private cairo_status_t +i915_shader_commit (i915_shader_t *shader, + i915_device_t *device); + +cairo_private void +i915_shader_fini (i915_shader_t *shader); + +cairo_private cairo_status_t +i915_fixup_unbounded (i915_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip); + +static inline cairo_bool_t +i915_surface_needs_tiling (i915_surface_t *dst) +{ + return dst->intel.drm.width > 2048 || dst->intel.drm.height > 2048; +} + +cairo_private cairo_status_t +i915_surface_copy_subimage (i915_device_t *device, + i915_surface_t *src, + const cairo_rectangle_int_t *extents, + cairo_bool_t flush, + i915_surface_t **clone_out); + +static inline uint32_t +pack_float (float f) +{ + union { + float f; + uint32_t ui; + } t; + t.f = f; + return t.ui; +} + +static inline cairo_status_t +i915_surface_fallback_flush (i915_surface_t *surface) +{ + cairo_status_t status; + + if (unlikely (surface->intel.drm.fallback != NULL)) + return intel_surface_flush (&surface->intel, 0); + + status = CAIRO_STATUS_SUCCESS; + if (unlikely (surface->deferred_clear)) + status = i915_surface_clear (surface); + + return status; +} + +#endif /* CAIRO_DRM_I915_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i915-shader.c b/gfx/cairo/cairo/src/drm/cairo-drm-i915-shader.c new file mode 100644 index 000000000000..a3f01fdf2224 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i915-shader.c @@ -0,0 +1,2859 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-drm-i915-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-snapshot-private.h" +#include "cairo-image-surface-private.h" + +#if 0 +static cairo_status_t +i915_packed_pixel_surface_finish (void *abstract_surface) +{ + i915_packed_pixel_surface_t *surface = abstract_surface; + i915_device_t *device; + + device = i915_device_acquire (&surface->device->intel.base); + + intel_bo_destroy (&device->intel, surface->bo); + + if (surface->is_current_texture) { + if (surface->is_current_texture & CURRENT_SOURCE) + device->current_source = NULL; + if (surface->is_current_texture & CURRENT_MASK) + device->current_mask = NULL; + device->current_n_samplers = 0; + } + + i915_device_release (device); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t i915_packed_pixel_surface_backend = { + I915_PACKED_PIXEL_SURFACE_TYPE, + i915_packed_pixel_surface_finish, +}; + +static cairo_surface_t * +i915_packed_pixel_surface_create (i915_device_t *device, + i915_packed_pixel_t pixel, + const uint8_t *data, + uint32_t length, + uint32_t width, uint32_t height) +{ + i915_packed_pixel_surface_t *surface; + cairo_content_t content; + uint32_t tiling, size; + uint32_t stride, half_stride; + uint32_t i; + + if (width > 2048 || height > 2048) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + surface = _cairo_malloc (sizeof (i915_packed_pixel_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + tiling = I915_TILING_NONE; /* XXX */ + half_stride = stride = i915_tiling_stride (tiling, width/2); + if (stride < width) + stride *= 2 ; + height = i915_tiling_height (tiling, height); + + switch (surface->pixel = pixel) { + case YUV_I420: + content = CAIRO_CONTENT_COLOR; + + surface->offset[0] = 0; + surface->width[0] = width; + surface->height[0] = height; + surface->stride[0] = stride; + surface->map0[0] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling); + surface->map0[0] |= ((height - 1) << MS3_HEIGHT_SHIFT) | + ((width - 1) << MS3_WIDTH_SHIFT); + surface->map1[0] = (stride / 4 - 1) << MS4_PITCH_SHIFT; + + surface->offset[1] = stride * height; + surface->width[1] = width / 2; + surface->height[1] = height / 2; + surface->stride[1] = half_stride; + surface->map0[1] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling); + surface->map0[1] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) | + ((width/2 - 1) << MS3_WIDTH_SHIFT); + surface->map1[1] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT; + + if (width < half_stride) { + surface->offset[2] = stride * height + half_stride / 2; + size = stride * height + half_stride * height / 2; + } else { + surface->offset[2] = stride * height + half_stride * height / 2; + size = stride * height + half_stride * height; + } + surface->width[2] = width / 2; + surface->height[2] = height / 2; + surface->stride[2] = half_stride; + surface->map0[2] = MAPSURF_8BIT | MT_8BIT_I8 | MS3_tiling (tiling); + surface->map0[2] |= ((height/2 - 1) << MS3_HEIGHT_SHIFT) | + ((width/2 - 1) << MS3_WIDTH_SHIFT); + surface->map1[2] = (half_stride / 4 - 1) << MS4_PITCH_SHIFT; + break; + + case NONE: + case YUV_YV12: + case YUV_YUY2: + case YUV_UYVY: + ASSERT_NOT_REACHED; + break; + } + + _cairo_surface_init (&surface->base, + &i915_packed_pixel_surface_backend, + content); + + surface->bo = intel_bo_create (&device->intel, size, FALSE); + assert (surface->bo->tiling == I915_TILING_NONE); + if (unlikely (surface->bo == NULL)) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + if (tiling == I915_TILING_NONE) { + intel_bo_t *bo = surface->bo; + uint32_t dst; + int uv; + + dst = surface->offset[0]; + if (width == stride) { + size = stride * height; + intel_bo_write (&device->intel, bo, dst, size, data); + data += size; + } else { + for (i = 0; i < height; i++) { + intel_bo_write (&device->intel, bo, dst, width, data); + dst += stride; + data += width; + } + } + + for (uv = 1; uv <= 2; uv++) { + dst = surface->offset[uv]; + if (width / 2 == half_stride) { + size = half_stride * height / 2; + intel_bo_write (&device->intel, bo, dst, size, data); + data += size; + } else { + size = width / 2; + for (i = 0; i < height / 2; i++) { + intel_bo_write (&device->intel, bo, dst, size, data); + dst += half_stride; + data += size; + } + } + } + } else { + uint8_t *dst, *base; + + base = intel_bo_map (&device->intel, surface->bo); + + dst = base + surface->offset[0]; + if (width == stride) { + size = stride * height; + memcpy (dst, data, size); + data += size; + } else { + for (i = 0; i < height; i++) { + memcpy (dst, data, width); + dst += stride; + data += width; + } + } + + dst = base + surface->offset[1]; + if (width / 2 == half_stride) { + size = half_stride * height / 2; + memcpy (dst, data, size); + data += size; + } else { + size = width / 2; + for (i = 0; i < height / 2; i++) { + memcpy (dst, data, size); + dst += half_stride; + data += size; + } + } + + dst = base + surface->offset[2]; + if (width / 2 == half_stride) { + size = half_stride * height / 2; + memcpy (dst, data, size); + data += size; + } else { + size = width / 2; + for (i = 0; i < height / 2; i++) { + memcpy (dst, data, size); + dst += half_stride; + data += size; + } + } + } + + surface->device = device; + surface->is_current_texture = 0; + + return &surface->base; +} + +static cairo_int_status_t +i915_clone_yuv (i915_surface_t *surface, + cairo_surface_t *source, + int width, int height, + cairo_surface_t **clone_out) +{ + const uint8_t *mime_data = NULL; + unsigned int mime_data_length; + cairo_surface_t *clone; + + cairo_surface_get_mime_data (source, "video/x-raw-yuv/i420", + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + clone = + i915_packed_pixel_surface_create ((i915_device_t *) surface->base.device, + YUV_I420, + mime_data, mime_data_length, + width, height); + if (clone == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (clone->status)) + return clone->status; + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} +#endif + +/* Max instruction count: 4 */ +static void +i915_shader_linear_color (i915_device_t *device, + enum i915_shader_linear_mode mode, + int in, int c0, int c1, int out) +{ + int tmp = FS_U0; + + switch (mode) { + case LINEAR_TEXTURE: + ASSERT_NOT_REACHED; + case LINEAR_NONE: + tmp = in; + break; + + case LINEAR_REPEAT: + i915_fs_frc (tmp, i915_fs_operand (in, X, X, X, X)); + break; +#if 0 + case LINEAR_REFLECT: + /* XXX needs an extra constant: C2 [0.5, 2.0, x, x] */ + i915_fs_mul (tmp, in, 0.5); + i915_fs_frc (tmp, i915_fs_operand_reg (tmp)); + i915_fs_mul (tmp, tmp, 2.0); + i915_fs_add (tmp, i915_fs_operand_one (), + i915_fs_operand_reg_negate (tmp)); + i915_fs_cmp (tmp, + i915_fs_operand_reg (tmp), + i915_fs_operand_reg (tmp), + i915_fs_operand_reg_negate (tmp)); + i915_fs_add (tmp, i915_fs_operand_one (), + i915_fs_operand_reg_negate (tmp)); +#endif + case LINEAR_PAD: + i915_fs_max (tmp, + i915_fs_operand_zero (), + i915_fs_operand (in, X, X, X, X)); + i915_fs_min (tmp, + i915_fs_operand_one (), + i915_fs_operand_reg (tmp)); + break; + } + + /* interpolate */ + i915_fs_mad (out, 0, + i915_fs_operand (tmp, NEG_X, NEG_X, NEG_X, NEG_X), + i915_fs_operand_reg (c0), + i915_fs_operand_reg (c0)); + i915_fs_mad (out, 0, + i915_fs_operand (tmp, X, X, X, X), + i915_fs_operand_reg (c1), + i915_fs_operand_reg (out)); +} + +static void +i915_shader_radial_init (struct i915_shader_radial *r, + const cairo_radial_pattern_t *radial) +{ + double dx, dy, dr, r1; + + dx = radial->cd2.center.x - radial->cd1.center.x; + dy = radial->cd2.center.y - radial->cd1.center.y; + dr = radial->cd2.radius - radial->cd1.radius; + + r1 = radial->cd1.radius; + + if (radial->cd2.center.x == radial->cd1.center.x && + radial->cd2.center.y == radial->cd1.center.y) + { + /* XXX dr == 0, meaningless with anything other than PAD */ + r->constants[0] = radial->cd1.center.x / dr; + r->constants[1] = radial->cd1.center.y / dr; + r->constants[2] = 1. / dr; + r->constants[3] = -r1 / dr; + + r->constants[4] = 0; + r->constants[5] = 0; + r->constants[6] = 0; + r->constants[7] = 0; + + r->base.mode = RADIAL_ONE; + } else { + r->constants[0] = -radial->cd1.center.x; + r->constants[1] = -radial->cd1.center.y; + r->constants[2] = r1; + r->constants[3] = -4 * (dx*dx + dy*dy - dr*dr); + + r->constants[4] = -2 * dx; + r->constants[5] = -2 * dy; + r->constants[6] = -2 * r1 * dr; + r->constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr)); + + r->base.mode = RADIAL_TWO; + } + + r->base.matrix = radial->base.base.matrix; +} + +/* Max instruction count: 10 */ +static void +i915_shader_radial_coord (i915_device_t *device, + enum i915_shader_radial_mode mode, + int in, int g0, int g1, int out) +{ + switch (mode) { + case RADIAL_ONE: + /* + pdx = (x - c1x) / dr, pdy = (y - c1y) / dr; + r² = pdx*pdx + pdy*pdy + t = r²/sqrt(r²) - r1/dr; + */ + i915_fs_mad (FS_U0, MASK_X | MASK_Y, + i915_fs_operand (in, X, Y, ZERO, ZERO), + i915_fs_operand (g0, Z, Z, ZERO, ZERO), + i915_fs_operand (g0, NEG_X, NEG_Y, ZERO, ZERO)); + i915_fs_dp2add (FS_U0, MASK_X, + i915_fs_operand (FS_U0, X, Y, ZERO, ZERO), + i915_fs_operand (FS_U0, X, Y, ZERO, ZERO), + i915_fs_operand_zero ()); + i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U0, X, X, X, X)); + i915_fs_mad (out, MASK_X, + i915_fs_operand (FS_U0, X, ZERO, ZERO, ZERO), + i915_fs_operand (out, X, ZERO, ZERO, ZERO), + i915_fs_operand (g0, W, ZERO, ZERO, ZERO)); + break; + + case RADIAL_TWO: + /* + pdx = x - c1x, pdy = y - c1y; + A = dx² + dy² - dr² + B = -2*(pdx*dx + pdy*dy + r1*dr); + C = pdx² + pdy² - r1²; + det = B*B - 4*A*C; + t = (-B + sqrt (det)) / (2 * A) + */ + + /* u0.x = pdx, u0.y = pdy, u[0].z = r1; */ + i915_fs_add (FS_U0, + i915_fs_operand (in, X, Y, ZERO, ZERO), + i915_fs_operand (g0, X, Y, Z, ZERO)); + /* u0.x = pdx, u0.y = pdy, u[0].z = r1, u[0].w = B; */ + i915_fs_dp3 (FS_U0, MASK_W, + i915_fs_operand (FS_U0, X, Y, ONE, ZERO), + i915_fs_operand (g1, X, Y, Z, ZERO)); + /* u1.x = pdx² + pdy² - r1²; [C] */ + i915_fs_dp3 (FS_U1, MASK_X, + i915_fs_operand (FS_U0, X, Y, Z, ZERO), + i915_fs_operand (FS_U0, X, Y, NEG_Z, ZERO)); + /* u1.x = C, u1.y = B, u1.z=-4*A; */ + i915_fs_mov_masked (FS_U1, MASK_Y, i915_fs_operand (FS_U0, W, W, W, W)); + i915_fs_mov_masked (FS_U1, MASK_Z, i915_fs_operand (g0, W, W, W, W)); + /* u1.x = B² - 4*A*C */ + i915_fs_dp2add (FS_U1, MASK_X, + i915_fs_operand (FS_U1, X, Y, ZERO, ZERO), + i915_fs_operand (FS_U1, Z, Y, ZERO, ZERO), + i915_fs_operand_zero ()); + /* out.x = -B + sqrt (B² - 4*A*C), + * out.y = -B - sqrt (B² - 4*A*C), + */ + i915_fs_rsq (out, MASK_X, i915_fs_operand (FS_U1, X, X, X, X)); + i915_fs_mad (out, MASK_X | MASK_Y, + i915_fs_operand (out, X, X, ZERO, ZERO), + i915_fs_operand (FS_U1, X, NEG_X, ZERO, ZERO), + i915_fs_operand (FS_U0, NEG_W, NEG_W, ZERO, ZERO)); + /* out.x = (-B + sqrt (B² - 4*A*C)) / (2 * A), + * out.y = (-B - sqrt (B² - 4*A*C)) / (2 * A) + */ + i915_fs_mul (out, + i915_fs_operand (out, X, Y, ZERO, ZERO), + i915_fs_operand (g1, W, W, ZERO, ZERO)); + /* if (A > 0) + * out = (-B + sqrt (B² - 4*A*C)) / (2 * A), + * else + * out = (-B - sqrt (B² - 4*A*C)) / (2 * A) + */ + i915_fs_cmp (out, + i915_fs_operand (g1, W, ZERO, ZERO, ZERO), + i915_fs_operand (out, X, ZERO, ZERO, ZERO), + i915_fs_operand (out, Y, ZERO, ZERO, ZERO)); + break; + } +} + +/* Max instruction count: 7 */ +static inline void +i915_shader_yuv_color (i915_device_t *device, + int y, int u, int v, + int c0, int c1, int c2, + int out) +{ + i915_fs_mov_masked (FS_U0, MASK_X, i915_fs_operand_reg (y)); + i915_fs_mov_masked (FS_U0, MASK_Y, i915_fs_operand_reg (u)); + i915_fs_mov_masked (FS_U0, MASK_Z, i915_fs_operand_reg (v)); + + i915_fs_add (FS_U0, + i915_fs_operand_reg (FS_U0), + i915_fs_operand_reg (c0)); + i915_fs_dp3 (out, MASK_X, + i915_fs_operand_reg (FS_U0), + i915_fs_operand (c1, X, ZERO, Y, ZERO)); + i915_fs_dp3 (out, MASK_Z, + i915_fs_operand_reg (FS_U0), + i915_fs_operand (c1, Z, W, ZERO, ZERO)); + i915_fs_dp3 (out, MASK_Y, + i915_fs_operand_reg (FS_U0), + i915_fs_operand_reg (c2)); +} + +static inline uint32_t +i915_shader_channel_key (const union i915_shader_channel *channel) +{ + return (channel->type.fragment & 0x0f) | (channel->base.mode << FS_DETAILS_SHIFT); +} + +static uint32_t +i915_shader_channel_get_num_tex_coords (const union i915_shader_channel *channel) +{ + switch (channel->type.fragment) { + default: + case FS_ZERO: + case FS_ONE: + case FS_CONSTANT: + case FS_PURE: + case FS_DIFFUSE: + return 0; + + case FS_LINEAR: + case FS_RADIAL: + case FS_TEXTURE: + case FS_SPANS: + case FS_YUV: + return 1; + } +} + +static uint32_t +i915_shader_get_num_tex_coords (const i915_shader_t *shader) +{ + uint32_t num_tex_coords; + + num_tex_coords = 0; + + num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->source); + num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->mask); + num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->clip); + num_tex_coords += i915_shader_channel_get_num_tex_coords (&shader->dst); + + return num_tex_coords; +} + +#define i915_fs_operand_impure(reg, channel, pure) \ + (reg | \ + (((pure & (1 << 0)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \ + (((pure & (1 << 1)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \ + (((pure & (1 << 2)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \ + (((pure & (1 << 3)) ? channel##_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT)) + +#define i915_fs_operand_pure(pure) \ + (FS_R0 | \ + (((pure & (1 << 0)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << X_CHANNEL_SHIFT) | \ + (((pure & (1 << 1)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Y_CHANNEL_SHIFT) | \ + (((pure & (1 << 2)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << Z_CHANNEL_SHIFT) | \ + (((pure & (1 << 3)) ? ONE_CHANNEL_VAL : ZERO_CHANNEL_VAL) << W_CHANNEL_SHIFT)) + +static void +i915_set_shader_program (i915_device_t *device, + const i915_shader_t *shader) +{ + uint32_t num_tex_coords; + uint32_t num_samplers; + uint32_t n; + uint32_t texture_offset = 0; + uint32_t constant_offset = 0; + uint32_t sampler_offset = 0; + uint32_t source_reg; + uint32_t source_pure; + uint32_t mask_reg; + uint32_t out_reg; + uint32_t dest_reg; + FS_LOCALS; + + n = (i915_shader_channel_key (&shader->source) << 0) | + (i915_shader_channel_key (&shader->mask) << 8) | + (i915_shader_channel_key (&shader->clip) << 16) | + (shader->op << 24) | + ((shader->opacity < 1.) << 30) | + (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31); + if (n == device->current_program) + return; + device->current_program = n; + + FS_BEGIN (); + + if (shader->source.type.fragment == FS_ZERO) { + if (shader->clip.type.fragment == FS_TEXTURE) { + /* XXX need_combine */ + assert (shader->mask.type.fragment == (i915_fragment_shader_t) -1); + i915_fs_dcl (FS_T0); + i915_fs_texld (FS_U0, FS_S0, FS_T0); + if ((shader->content & CAIRO_CONTENT_COLOR) == 0) + i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, W, W, W, W)); + else + i915_fs_mov (FS_OC, i915_fs_operand (FS_U0, ZERO, ZERO, ZERO, W)); + } else { + i915_fs_mov (FS_OC, i915_fs_operand_zero ()); + } + + FS_END (); + return; + } + + num_tex_coords = i915_shader_get_num_tex_coords (shader); + for (n = 0; n < num_tex_coords; n++) + i915_fs_dcl (FS_T0 + n); + + num_samplers = + shader->source.base.n_samplers + + shader->mask.base.n_samplers + + shader->clip.base.n_samplers + + shader->dst.base.n_samplers; + for (n = 0; n < num_samplers; n++) + i915_fs_dcl (FS_S0 + n); + + source_reg = ~0; + source_pure = 0; + out_reg = FS_R0; + if (! shader->need_combine && + shader->mask.type.fragment == (i915_fragment_shader_t) -1 && + shader->clip.type.fragment != FS_TEXTURE && + shader->content != CAIRO_CONTENT_ALPHA) + { + out_reg = FS_OC; + } + + switch (shader->source.type.fragment) { + default: + case FS_ZERO: + case FS_SPANS: + ASSERT_NOT_REACHED; + + case FS_PURE: + source_pure = shader->source.solid.pure; + case FS_ONE: + break; + + case FS_CONSTANT: + source_reg = FS_C0; + constant_offset += 1; + break; + + case FS_DIFFUSE: + i915_fs_dcl (FS_T8); + source_reg = FS_T8; + break; + + case FS_LINEAR: + i915_shader_linear_color (device, shader->source.base.mode, + FS_T0, /* input */ + FS_C0, FS_C1, /* colour ramp */ + FS_U3); /* unpremultiplied output */ + /* XXX can we defer premultiplication? */ + i915_fs_mul (out_reg, + i915_fs_operand_reg (FS_U3), + i915_fs_operand (FS_U3, W, W, W, ONE)); + + constant_offset += 2; + texture_offset += 1; + source_reg = out_reg; + break; + + case FS_RADIAL: + i915_shader_radial_coord (device, shader->source.base.mode, + FS_T0, /* input */ + FS_C0, FS_C1, /* gradient constants */ + FS_R0); /* coordinate */ + + i915_fs_texld (out_reg, FS_S0, FS_R0); + constant_offset += 2; + texture_offset += 1; + sampler_offset += 1; + source_reg = out_reg; + break; + + case FS_TEXTURE: + i915_fs_texld (out_reg, FS_S0, FS_T0); + texture_offset += 1; + sampler_offset += 1; + source_reg = out_reg; + break; + + case FS_YUV: + /* Load samplers to temporaries. */ + i915_fs_texld (FS_R0, FS_S0, FS_T0); + i915_fs_texld (FS_R1, FS_S1, FS_T0); + i915_fs_texld (FS_R2, FS_S2, FS_T0); + + i915_shader_yuv_color (device, + FS_R0, FS_R1, FS_R2, /* y, u, v */ + FS_C0, FS_C1, FS_C2, /* coefficients */ + out_reg); + + constant_offset += 3; + texture_offset += 1; + sampler_offset += 3; + source_reg = out_reg; + break; + } + + mask_reg = ~0; + switch (shader->mask.type.fragment) { + case FS_PURE: + case FS_ZERO: + case FS_YUV: + case FS_DIFFUSE: + ASSERT_NOT_REACHED; + case FS_ONE: + default: + break; + + case FS_SPANS: + mask_reg = FS_T0 + texture_offset; + texture_offset += 1; + break; + + case FS_CONSTANT: + mask_reg = FS_C0 + constant_offset; + constant_offset += 1; + break; + + case FS_LINEAR: + i915_shader_linear_color (device, shader->mask.base.mode, + FS_T0 + texture_offset, /* input */ + FS_C0 + constant_offset, + FS_C0 + constant_offset + 1, /* colour ramp */ + FS_R1); /* unpremultiplied output */ + constant_offset += 2; + texture_offset += 1; + mask_reg = FS_R1; + break; + + case FS_RADIAL: + i915_shader_radial_coord (device, shader->mask.base.mode, + FS_T0 + texture_offset, /* input */ + FS_C0 + constant_offset, + FS_C0 + constant_offset + 1, /* gradient constants */ + FS_R1); /* coordinate */ + + i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_R1); + constant_offset += 2; + texture_offset += 1; + sampler_offset += 1; + mask_reg = FS_R1; + break; + + case FS_TEXTURE: + i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset); + texture_offset += 1; + sampler_offset += 1; + mask_reg = FS_R1; + break; + } + + if (mask_reg != ~0U) { + if (! shader->need_combine && + shader->clip.type.fragment != FS_TEXTURE && + (shader->content != CAIRO_CONTENT_ALPHA || source_reg == ~0U)) + { + out_reg = FS_OC; + } + if (source_reg == ~0U) { + if (source_pure) { + if (shader->mask.type.fragment == FS_SPANS) { + if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { + if (source_pure & (1 << 3)) + i915_fs_mov (out_reg, i915_fs_operand (mask_reg, X, X, X, X)); + else + i915_fs_mov (out_reg, i915_fs_operand_zero ()); + } else { + i915_fs_mov (out_reg, + i915_fs_operand_impure (mask_reg, X, source_pure)); + } + } else { + /* XXX ComponentAlpha + i915_fs_mov (out_reg, + i915_fs_operand_pure (mask_reg, + shader->source.solid.pure)); + */ + if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { + if (source_pure & (1 << 3)) + i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W)); + else + i915_fs_mov (out_reg, i915_fs_operand_zero ()); + } else { + i915_fs_mov (out_reg, + i915_fs_operand_impure (mask_reg, W, source_pure)); + } + } + source_reg = out_reg; + } else if (shader->mask.type.fragment == FS_SPANS) { + i915_fs_mov (out_reg, + i915_fs_operand (mask_reg, X, X, X, X)); + source_reg = out_reg; + } else { + source_reg = mask_reg; + } + } else { + if (shader->mask.type.fragment == FS_SPANS) { + if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { + i915_fs_mul (out_reg, + i915_fs_operand (source_reg, W, W, W, W), + i915_fs_operand (mask_reg, X, X, X, X)); + } else { + i915_fs_mul (out_reg, + i915_fs_operand_reg (source_reg), + i915_fs_operand (mask_reg, X, X, X, X)); + } + } else { + /* XXX ComponentAlpha + i915_fs_mul (FS_R0, + i915_fs_operand_reg (source_reg), + i915_fs_operand_reg (mask_reg)); + */ + if (out_reg == FS_OC && shader->content == CAIRO_CONTENT_ALPHA) { + i915_fs_mul (out_reg, + i915_fs_operand (source_reg, W, W, W, W), + i915_fs_operand (mask_reg, W, W, W, W)); + } else { + i915_fs_mul (out_reg, + i915_fs_operand_reg (source_reg), + i915_fs_operand (mask_reg, W, W, W, W)); + } + } + + source_reg = out_reg; + } + } + + if (shader->opacity < 1.) { + i915_fs_mul (source_reg, + i915_fs_operand_reg (source_reg), + i915_fs_operand_reg (FS_C0 + constant_offset)); + constant_offset++; + } + + /* need to preserve order of src, mask, clip, dst */ + mask_reg = ~0; + if (shader->clip.type.fragment == FS_TEXTURE) { + i915_fs_texld (FS_R1, FS_S0 + sampler_offset, FS_T0 + texture_offset); + texture_offset += 1; + sampler_offset += 1; + mask_reg = FS_R1; + } + + if (shader->need_combine) { + assert (shader->dst.type.fragment == FS_TEXTURE); + + i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset); + texture_offset += 1; + sampler_offset += 1; + dest_reg = FS_R2; + + switch (shader->op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + ASSERT_NOT_REACHED; + + case CAIRO_OPERATOR_OVER: + if (source_reg == ~0U) { + /* XXX shader->source.type.fragment == FS_PURE */ + dest_reg = FS_OC; + } else { + i915_fs_add (FS_U0, + i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W), + i915_fs_operand_one ()); + i915_fs_mul (FS_U0, + i915_fs_operand_reg (FS_U0), + dest_reg); + i915_fs_add (FS_R3, + i915_fs_operand_reg (source_reg), + i915_fs_operand_reg (FS_U0)); + source_reg = FS_R3; + } + break; + + case CAIRO_OPERATOR_IN: + if (source_reg == ~0U) { + /* XXX shader->source.type.fragment == FS_PURE */ + source_reg = dest_reg; + } else { + i915_fs_mul (FS_R3, + i915_fs_operand_reg (source_reg), + dest_reg); + source_reg = FS_R3; + } + break; + + case CAIRO_OPERATOR_OUT: + if (source_reg == ~0U) { + /* XXX shader->source.type.fragment == FS_PURE */ + i915_fs_mov (FS_R3, i915_fs_operand_zero ()); + source_reg = FS_R3; + } else { + i915_fs_add (FS_U0, + i915_fs_operand (source_reg, NEG_W, NEG_W, NEG_W, NEG_W), + i915_fs_operand_one ()); + i915_fs_mul (FS_R3, + i915_fs_operand_reg (FS_U0), + dest_reg); + source_reg = FS_R3; + } + break; + + case CAIRO_OPERATOR_ATOP: + + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_OUT: + case CAIRO_OPERATOR_DEST_ATOP: + + case CAIRO_OPERATOR_XOR: + case CAIRO_OPERATOR_ADD: + case CAIRO_OPERATOR_SATURATE: + + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + ASSERT_NOT_REACHED; + break; + } + } + + if (shader->clip.type.fragment == FS_TEXTURE) { + assert (mask_reg != ~0U); + + if (! shader->need_combine) { + /* (source IN clip) */ + if (source_reg == ~0U) { + if (source_pure == 0) { + source_reg = mask_reg; + } else { + out_reg = FS_OC; + if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { + if (source_pure & (1 << 3)) + i915_fs_mov (out_reg, i915_fs_operand (mask_reg, W, W, W, W)); + else + i915_fs_mov (out_reg, i915_fs_operand_zero ()); + } else { + i915_fs_mov (out_reg, + i915_fs_operand_impure (mask_reg, W, source_pure)); + } + source_reg = out_reg; + } + } else if (mask_reg) { + out_reg = FS_OC; + if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { + i915_fs_mul (out_reg, + i915_fs_operand (source_reg, W, W, W, W), + i915_fs_operand (mask_reg, W, W, W, W)); + } else { + i915_fs_mul (out_reg, + i915_fs_operand_reg (source_reg), + i915_fs_operand (mask_reg, W, W, W, W)); + } + + source_reg = out_reg; + } + } else { + /* (source OP dest) LERP_clip dest */ + if (source_reg == ~0U) { + if (source_pure == 0) { + i915_fs_mov (FS_R3, + i915_fs_operand (mask_reg, W, W, W, W)); + } else { + i915_fs_mov (FS_R3, + i915_fs_operand_impure (mask_reg, W, source_pure)); + } + } else { + i915_fs_mul (FS_R3, + i915_fs_operand_reg (source_reg), + i915_fs_operand (mask_reg, W, W, W, W)); + } + + i915_fs_add (mask_reg, + i915_fs_operand_one (), + i915_fs_operand (mask_reg, NEG_W, NEG_W, NEG_W, NEG_W)); + + if (dest_reg != FS_OC) { + if (dest_reg == ~0U) { + assert (shader->dst.type.fragment == FS_TEXTURE); + + i915_fs_texld (FS_R2, FS_S0 + sampler_offset, FS_T0 + texture_offset); + texture_offset += 1; + sampler_offset += 1; + dest_reg = FS_R2; + } + + i915_fs_mul (FS_U1, + i915_fs_operand_reg (dest_reg), + i915_fs_operand_reg (mask_reg)); + mask_reg = FS_U1; + } + + source_reg = FS_OC; + if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { + i915_fs_add (source_reg, + i915_fs_operand (FS_R3, W, W, W, W), + i915_fs_operand (mask_reg, W, W, W, W)); + } else { + i915_fs_add (source_reg, + i915_fs_operand_reg (FS_R3), + i915_fs_operand_reg (mask_reg)); + } + } + } + + if (source_reg != FS_OC) { + if (source_reg == ~0U) { + if (source_pure) { + if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { + if (source_pure & (1 << 3)) + i915_fs_mov (FS_OC, i915_fs_operand_one ()); + else + i915_fs_mov (FS_OC, i915_fs_operand_zero ()); + } else + i915_fs_mov (FS_OC, i915_fs_operand_pure (source_pure)); + } else { + i915_fs_mov (FS_OC, i915_fs_operand_one ()); + } + } else if ((shader->content & CAIRO_CONTENT_COLOR) == 0) { + i915_fs_mov (FS_OC, i915_fs_operand (source_reg, W, W, W, W)); + } else { + i915_fs_mov (FS_OC, i915_fs_operand_reg (source_reg)); + } + } + + FS_END (); +} + +static cairo_bool_t +i915_shader_linear_init (struct i915_shader_linear *l, + const cairo_linear_pattern_t *linear) +{ + double x0, y0, sf; + double dx, dy, offset; + + dx = linear->pd2.x - linear->pd1.x; + dy = linear->pd2.y - linear->pd1.y; + sf = dx * dx + dy * dy; + if (sf <= 1e-5) + return FALSE; + + dx /= sf; + dy /= sf; + + x0 = linear->pd1.x; + y0 = linear->pd1.y; + offset = dx*x0 + dy*y0; + + if (_cairo_matrix_is_identity (&linear->base.base.matrix)) { + l->dx = dx; + l->dy = dy; + l->offset = -offset; + } else { + cairo_matrix_t m; + + cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0); + cairo_matrix_multiply (&m, &linear->base.base.matrix, &m); + l->dx = m.xx; + l->dy = m.xy; + l->offset = m.x0; + } + + return TRUE; +} + +static cairo_bool_t +i915_shader_linear_contains_rectangle (struct i915_shader_linear *l, + const cairo_rectangle_int_t *extents) +{ + double v; + + v = i915_shader_linear_texcoord (l, + extents->x, + extents->y); + if (v < 0.) + return FALSE; + if (v > 1.) + return FALSE; + + v = i915_shader_linear_texcoord (l, + extents->x + extents->width, + extents->y); + if (v < 0.) + return FALSE; + if (v > 1.) + return FALSE; + + v = i915_shader_linear_texcoord (l, + extents->x, + extents->y + extents->height); + if (v < 0.) + return FALSE; + if (v > 1.) + return FALSE; + + v = i915_shader_linear_texcoord (l, + extents->x + extents->width, + extents->y + extents->height); + if (v < 0.) + return FALSE; + if (v > 1.) + return FALSE; + + return TRUE; +} + +#define is_pure(C,mask) (((mask) == 0) || (C) <= 0x00ff || (C) >= 0xff00) +#define is_one(C,mask) (((mask) != 0) && (C) >= 0xff00) +#define is_zero(C,mask) (((mask) != 0) && (C) <= 0x00ff) + +static cairo_status_t +i915_shader_acquire_solid (i915_shader_t *shader, + union i915_shader_channel *src, + const cairo_solid_pattern_t *solid, + const cairo_rectangle_int_t *extents) +{ + cairo_content_t content; + + content = CAIRO_CONTENT_COLOR_ALPHA; + src->solid.color = solid->color; + if (content == 0 || solid->color.alpha_short <= 0x00ff) + { + src->base.content = CAIRO_CONTENT_ALPHA; + src->type.fragment = FS_ZERO; + } + else if ((((content & CAIRO_CONTENT_COLOR) == 0) || + (solid->color.red_short >= 0xff00 && + solid->color.green_short >= 0xff00 && + solid->color.blue_short >= 0xff00)) && + ((content & CAIRO_CONTENT_ALPHA) == 0 || + solid->color.alpha_short >= 0xff00)) + { + src->base.content = CAIRO_CONTENT_ALPHA; + src->type.fragment = FS_ONE; + } + else if (is_pure (solid->color.red_short, content & CAIRO_CONTENT_COLOR) && + is_pure (solid->color.green_short, content & CAIRO_CONTENT_COLOR) && + is_pure (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) && + is_pure (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA)) + { + src->solid.pure = 0; + src->solid.pure |= is_one (solid->color.red_short, content & CAIRO_CONTENT_COLOR) << 0; + src->solid.pure |= is_one (solid->color.green_short, content & CAIRO_CONTENT_COLOR) << 1; + src->solid.pure |= is_one (solid->color.blue_short, content & CAIRO_CONTENT_COLOR) << 2; + src->solid.pure |= (! is_zero (solid->color.alpha_short, content & CAIRO_CONTENT_ALPHA)) << 3; + + if (src->solid.pure == 0) { + src->base.content = CAIRO_CONTENT_ALPHA; + src->type.fragment = FS_ZERO; + } else if (src->solid.pure == 0x7) { + src->base.content = CAIRO_CONTENT_ALPHA; + src->type.fragment = FS_ONE; + } else { + src->base.content = content; + src->type.fragment = FS_PURE; + src->base.mode = src->solid.pure; + } + } + else + { + src->base.content = content; + src->type.fragment = src == &shader->source ? FS_DIFFUSE : FS_CONSTANT; + } + src->type.vertex = src->type.fragment == FS_ZERO ? VS_ZERO : VS_CONSTANT; + src->type.pattern = PATTERN_CONSTANT; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_shader_acquire_linear (i915_shader_t *shader, + union i915_shader_channel *src, + const cairo_linear_pattern_t *linear, + const cairo_rectangle_int_t *extents) +{ + cairo_bool_t mode = LINEAR_TEXTURE; + cairo_status_t status; + + if (i915_shader_linear_init (&src->linear, linear) && + linear->base.n_stops == 2 && + linear->base.stops[0].offset == 0.0 && + linear->base.stops[1].offset == 1.0) + { + if (i915_shader_linear_contains_rectangle (&src->linear, + extents)) + { + /* XXX can also lerp if contained within offset range */ + mode = LINEAR_NONE; + } + else switch (linear->base.base.extend) { + case CAIRO_EXTEND_REPEAT: + mode = LINEAR_REPEAT; + break; + case CAIRO_EXTEND_PAD: + mode = LINEAR_PAD; + break; + case CAIRO_EXTEND_NONE: + break; + case CAIRO_EXTEND_REFLECT: + break; + default: + ASSERT_NOT_REACHED; + break; + } + } + + src->type.vertex = VS_LINEAR; + src->type.pattern = PATTERN_LINEAR; + src->base.texfmt = TEXCOORDFMT_1D; + src->base.content = CAIRO_CONTENT_COLOR_ALPHA; + src->base.mode = mode; + if (mode == LINEAR_TEXTURE) { + intel_buffer_t buffer; + + status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device, + &linear->base, &buffer); + if (unlikely (status)) + return status; + + src->type.fragment = FS_TEXTURE; + src->base.bo = intel_bo_reference (buffer.bo); + src->base.n_samplers = 1; + src->base.offset[0] = buffer.offset; + src->base.map[0] = buffer.map0; + src->base.map[1] = buffer.map1; + src->base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (CAIRO_FILTER_BILINEAR); + src->base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (linear->base.base.extend); + } else { + src->type.fragment = FS_LINEAR; + src->linear.color0.red = linear->base.stops[0].color.red; + src->linear.color0.green = linear->base.stops[0].color.green; + src->linear.color0.blue = linear->base.stops[0].color.blue; + src->linear.color0.alpha = linear->base.stops[0].color.alpha; + + src->linear.color1.red = linear->base.stops[1].color.red; + src->linear.color1.green = linear->base.stops[1].color.green; + src->linear.color1.blue = linear->base.stops[1].color.blue; + src->linear.color1.alpha = linear->base.stops[1].color.alpha; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_shader_acquire_radial (i915_shader_t *shader, + union i915_shader_channel *src, + const cairo_radial_pattern_t *radial, + const cairo_rectangle_int_t *extents) +{ + intel_buffer_t buffer; + cairo_status_t status; + + status = intel_gradient_render ((intel_device_t *) shader->target->intel.drm.base.device, + &radial->base, &buffer); + if (unlikely (status)) + return status; + + i915_shader_radial_init (&src->radial, radial); + + src->type.vertex = VS_TEXTURE; + src->type.fragment = FS_RADIAL; + src->type.pattern = PATTERN_RADIAL; + src->base.texfmt = TEXCOORDFMT_2D; + + src->base.content = CAIRO_CONTENT_COLOR_ALPHA; + src->base.bo = intel_bo_reference (buffer.bo); + src->base.n_samplers = 1; + src->base.offset[0] = buffer.offset; + src->base.map[0] = buffer.map0; + src->base.map[1] = buffer.map1; + src->base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (CAIRO_FILTER_BILINEAR); + src->base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (radial->base.base.extend); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_surface_clone (i915_device_t *device, + cairo_image_surface_t *image, + i915_surface_t **clone_out) +{ + i915_surface_t *clone; + cairo_status_t status; + +#if 0 + clone = + i915_surface_create_from_cacheable_image_internal (device, image); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; +#else + cairo_format_t format; + + format = image->format; + if (format == CAIRO_FORMAT_A1) + format = CAIRO_FORMAT_A8; + + clone = (i915_surface_t *) + i915_surface_create_internal (&device->intel.base, + format, + image->width, + image->height, + I915_TILING_DEFAULT, + FALSE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + status = intel_bo_put_image (&device->intel, + to_intel_bo (clone->intel.drm.bo), + image, + 0, 0, + image->width, image->height, + 0, 0); + + if (unlikely (status)) + return status; +#endif + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_surface_clone_subimage (i915_device_t *device, + cairo_image_surface_t *image, + const cairo_rectangle_int_t *extents, + i915_surface_t **clone_out) +{ + i915_surface_t *clone; + cairo_status_t status; + cairo_format_t format; + + format = image->format; + if (format == CAIRO_FORMAT_A1) + format = CAIRO_FORMAT_A8; + + clone = (i915_surface_t *) + i915_surface_create_internal (&device->intel.base, + format, + extents->width, + extents->height, + I915_TILING_NONE, + FALSE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + status = intel_bo_put_image (&device->intel, + to_intel_bo (clone->intel.drm.bo), + image, + extents->x, extents->y, + extents->width, extents->height, + 0, 0); + + if (unlikely (status)) + return status; + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_surface_render_pattern (i915_device_t *device, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + i915_surface_t **clone_out) +{ + i915_surface_t *clone; + cairo_surface_t *image; + cairo_status_t status; + void *ptr; + + clone = (i915_surface_t *) + i915_surface_create_internal (&device->intel.base, + _cairo_format_from_content (pattern->surface->content), + extents->width, + extents->height, + I915_TILING_NONE, + FALSE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + ptr = intel_bo_map (&device->intel, + to_intel_bo (clone->intel.drm.bo)); + if (unlikely (ptr == NULL)) { + cairo_surface_destroy (&clone->intel.drm.base); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + image = cairo_image_surface_create_for_data (ptr, + clone->intel.drm.format, + clone->intel.drm.width, + clone->intel.drm.height, + clone->intel.drm.stride); + if (unlikely (image->status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return image->status; + } + + status = _cairo_surface_offset_paint (image, + extents->x, extents->y, + CAIRO_OPERATOR_SOURCE, + &pattern->base, + NULL); + cairo_surface_destroy (image); + + if (unlikely (status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return status; + } + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_shader_acquire_solid_surface (i915_shader_t *shader, + union i915_shader_channel *src, + cairo_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t pattern; + cairo_surface_t *pixel; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + uint32_t argb; + + status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); + if (unlikely (status)) + return status; + + /* extract the pixel as argb32 */ + pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + _cairo_pattern_init_for_surface (&pattern, &image->base); + cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + + _cairo_surface_release_source_image (surface, image, image_extra); + + if (unlikely (status)) { + cairo_surface_destroy (pixel); + return status; + } + + image = (cairo_image_surface_t *) pixel; + argb = *(uint32_t *) image->data; + cairo_surface_destroy (pixel); + + if (argb >> 24 == 0) { + _cairo_color_init_rgba (&src->solid.color, 0, 0, 0, 0); + } else { + uint8_t alpha = argb >> 24; + + _cairo_color_init_rgba (&src->solid.color, + ((((argb >> 16) & 0xff) * 255 + alpha / 2) / alpha) / 255., + ((((argb >> 8) & 0xff) * 255 + alpha / 2) / alpha) / 255., + ((((argb >> 0) & 0xff) * 255 + alpha / 2) / alpha) / 255., + alpha / 255.); + } + + src->base.content = CAIRO_CONTENT_COLOR_ALPHA; + src->type.fragment = FS_CONSTANT; + src->type.vertex = VS_CONSTANT; + src->type.pattern = PATTERN_CONSTANT; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_shader_acquire_surface (i915_shader_t *shader, + union i915_shader_channel *src, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + int surface_width, surface_height; + cairo_surface_t *surface, *drm; + cairo_extend_t extend; + cairo_filter_t filter; + cairo_matrix_t m; + int src_x = 0, src_y = 0; + cairo_surface_t *free_me = NULL; + cairo_status_t status; + cairo_rectangle_int_t sample; + + assert (src->type.fragment == (i915_fragment_shader_t) -1); + drm = surface = pattern->surface; + + extend = pattern->base.extend; + src->base.matrix = pattern->base.matrix; + filter = pattern->base.filter; + _cairo_pattern_sampled_area(&pattern->base, extents, sample); + + if (surface->type == CAIRO_SURFACE_TYPE_DRM) { + if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + drm = ((cairo_surface_subsurface_t *) surface)->target; + } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { + drm = ((cairo_surface_snapshot_t *) surface)->target; + } + } + + if (drm->type == CAIRO_SURFACE_TYPE_DRM) { + i915_surface_t *s = (i915_surface_t *) drm; + + if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + if (s->intel.drm.base.device == shader->target->intel.drm.base.device && + s != shader->target) + { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface; + int x; + + status = i915_surface_fallback_flush (s); + if (unlikely (status)) + return status; + + /* XXX blt subimage and cache snapshot */ + + if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) { + /* XXX pipelined flush of RENDER/TEXTURE cache */ + } + + src->type.fragment = FS_TEXTURE; + src->surface.pixel = NONE; + surface_width = sub->extents.width; + surface_height = sub->extents.height; + + src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo)); + src->base.n_samplers = 1; + + x = sub->extents.x; + if (s->intel.drm.format != CAIRO_FORMAT_A8) + x *= 4; + + /* XXX tiling restrictions upon offset? */ + src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x; + src->base.map[0] = s->map0; + src->base.map[0] &= ~((2047 << MS3_HEIGHT_SHIFT) | (2047 << MS3_WIDTH_SHIFT)); + src->base.map[0] |= + ((sub->extents.height - 1) << MS3_HEIGHT_SHIFT) | + ((sub->extents.width - 1) << MS3_WIDTH_SHIFT); + src->base.map[1] = (s->intel.drm.stride / 4 - 1) << MS4_PITCH_SHIFT; + } + } else { + /* XXX if s == shader->dst allow if FILTER_NEAREST, EXTEND_NONE? */ + if (s->intel.drm.base.device == shader->target->intel.drm.base.device) { + status = i915_surface_fallback_flush (s); + if (unlikely (status)) + return status; + + if (s == shader->target || i915_surface_needs_tiling (s)) { + status = i915_surface_copy_subimage (i915_device (shader->target), + s, &sample, TRUE, &s); + if (unlikely (status)) + return status; + + free_me = drm = &s->intel.drm.base; + } + + src->type.fragment = FS_TEXTURE; + src->surface.pixel = NONE; + + surface_width = s->intel.drm.width; + surface_height = s->intel.drm.height; + + src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo)); + src->base.n_samplers = 1; + src->base.offset[0] = s->offset; + src->base.map[0] = s->map0; + src->base.map[1] = s->map1; + } + } + } + + if (src->type.fragment == (i915_fragment_shader_t) -1) { + i915_surface_t *s; + + if (extents->width == 1 && extents->height == 1) { + return i915_shader_acquire_solid_surface (shader, src, + surface, extents); + } + + s = (i915_surface_t *) + _cairo_surface_has_snapshot (surface, + shader->target->intel.drm.base.backend); + if (s == NULL) { + cairo_status_t status; + +#if 0 + /* XXX hackity hack hack */ + status = i915_clone_yuv (surface, src, + image->width, image->height, + clone_out); +#endif + + if (sample.width > 2048 || sample.height > 2048) { + status = i915_surface_render_pattern (i915_device (shader->target), + pattern, extents, + &s); + if (unlikely (status)) + return status; + + extend = CAIRO_EXTEND_NONE; + filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&src->base.matrix, + -extents->x, -extents->y); + } else { + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->width < 2048 && + image->height < 2048 && + sample.width >= image->width / 4 && + sample.height >= image->height /4) + { + + status = i915_surface_clone (i915_device (shader->target), + image, &s); + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + _cairo_surface_attach_snapshot (surface, + &s->intel.drm.base, + intel_surface_detach_snapshot); + + status = intel_snapshot_cache_insert (&i915_device (shader->target)->intel, + &s->intel); + if (unlikely (status)) { + cairo_surface_finish (&s->intel.drm.base); + cairo_surface_destroy (&s->intel.drm.base); + } + } + } + else + { + status = i915_surface_clone_subimage (i915_device (shader->target), + image, &sample, &s); + src_x = -extents->x; + src_y = -extents->y; + } + + _cairo_surface_release_source_image (surface, image, image_extra); + if (unlikely (status)) + return status; + } + + free_me = &s->intel.drm.base; + } + + src->type.fragment = FS_TEXTURE; + src->surface.pixel = NONE; + + src->base.bo = intel_bo_reference (to_intel_bo (s->intel.drm.bo)); + src->base.n_samplers = 1; + src->base.offset[0] = s->offset; + src->base.map[0] = s->map0; + src->base.map[1] = s->map1; + + drm = &s->intel.drm.base; + + surface_width = s->intel.drm.width; + surface_height = s->intel.drm.height; + } + + /* XXX transform nx1 or 1xn surfaces to 1D */ + + src->type.pattern = PATTERN_TEXTURE; + if (extend != CAIRO_EXTEND_NONE && + sample.x >= 0 && sample.y >= 0 && + sample.x + sample.width <= surface_width && + sample.y + sample.height <= surface_height) + { + extend = CAIRO_EXTEND_NONE; + } + if (extend == CAIRO_EXTEND_NONE) { + src->type.vertex = VS_TEXTURE_16; + src->base.texfmt = TEXCOORDFMT_2D_16; + } else { + src->type.vertex = VS_TEXTURE; + src->base.texfmt = TEXCOORDFMT_2D; + } + src->base.content = drm->content; + + src->base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (filter); + src->base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (extend); + + /* tweak the src matrix to map from dst to texture coordinates */ + if (src_x | src_y) + cairo_matrix_translate (&src->base.matrix, src_x, src_x); + cairo_matrix_init_scale (&m, 1. / surface_width, 1. / surface_height); + cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m); + + if (free_me != NULL) + cairo_surface_destroy (free_me); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +i915_shader_acquire_pattern (i915_shader_t *shader, + union i915_shader_channel *src, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return i915_shader_acquire_solid (shader, src, + (cairo_solid_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_LINEAR: + return i915_shader_acquire_linear (shader, src, + (cairo_linear_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_RADIAL: + return i915_shader_acquire_radial (shader, src, + (cairo_radial_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_SURFACE: + return i915_shader_acquire_surface (shader, src, + (cairo_surface_pattern_t *) pattern, + extents); + + default: + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; + } +} + +static uint32_t +i915_get_blend (cairo_operator_t op, + i915_surface_t *dst) +{ +#define SBLEND(X) ((BLENDFACT_##X) << S6_CBUF_SRC_BLEND_FACT_SHIFT) +#define DBLEND(X) ((BLENDFACT_##X) << S6_CBUF_DST_BLEND_FACT_SHIFT) + static const struct blendinfo { + cairo_bool_t dst_alpha; + uint32_t src_blend; + uint32_t dst_blend; + enum { + BOUNDED, + SIMPLE, + XRENDER, + } kind; + } i915_blend_op[] = { + {0, SBLEND (ZERO), DBLEND (ZERO), BOUNDED}, /* Clear */ + {0, SBLEND (ONE), DBLEND (ZERO), BOUNDED}, /* Src */ + + {0, SBLEND (ONE), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Over */ + {1, SBLEND (DST_ALPHA), DBLEND (ZERO), XRENDER}, /* In */ + {1, SBLEND (INV_DST_ALPHA), DBLEND (ZERO), XRENDER}, /* Out */ + {1, SBLEND (DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Atop */ + + {0, SBLEND (ZERO), DBLEND (ONE), SIMPLE}, /* Dst */ + {1, SBLEND (INV_DST_ALPHA), DBLEND (ONE), SIMPLE}, /* OverReverse */ + {0, SBLEND (ZERO), DBLEND (SRC_ALPHA), XRENDER}, /* InReverse */ + {0, SBLEND (ZERO), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* OutReverse */ + {1, SBLEND (INV_DST_ALPHA), DBLEND (SRC_ALPHA), XRENDER}, /* AtopReverse */ + + {1, SBLEND (INV_DST_ALPHA), DBLEND (INV_SRC_ALPHA), SIMPLE}, /* Xor */ + {0, SBLEND (ONE), DBLEND (ONE), SIMPLE}, /* Add */ + //{0, 0, SBLEND (SRC_ALPHA_SATURATE), DBLEND (ONE), SIMPLE}, /* XXX Saturate */ + }; + uint32_t sblend, dblend; + + if (op >= ARRAY_LENGTH (i915_blend_op)) + return 0; + + if (i915_blend_op[op].kind == BOUNDED) + return 0; + + sblend = i915_blend_op[op].src_blend; + dblend = i915_blend_op[op].dst_blend; + + /* If there's no dst alpha channel, adjust the blend op so that we'll treat + * it as always 1. + */ + if ((dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA) == 0 && + i915_blend_op[op].dst_alpha) + { + if (sblend == SBLEND (DST_ALPHA)) + sblend = SBLEND (ONE); + else if (sblend == SBLEND (INV_DST_ALPHA)) + sblend = SBLEND (ZERO); + } + + /* i915 engine reads 8bit color buffer into green channel in cases + like color buffer blending etc., and also writes back green channel. + So with dst_alpha blend we should use color factor. See spec on + "8-bit rendering" */ + if (dst->intel.drm.format == CAIRO_FORMAT_A8 && i915_blend_op[op].dst_alpha) { + if (sblend == SBLEND (DST_ALPHA)) + sblend = SBLEND (DST_COLR); + else if (sblend == SBLEND (INV_DST_ALPHA)) + sblend = SBLEND (INV_DST_COLR); + } + + return sblend | dblend; +#undef SBLEND +#undef DBLEND +} + +static void +i915_shader_channel_init (union i915_shader_channel *channel) +{ + channel->type.vertex = (i915_vertex_shader_t) -1; + channel->type.fragment = (i915_fragment_shader_t) -1; + channel->type.pattern = (i915_shader_channel_t) -1; + channel->base.texfmt = TEXCOORDFMT_NOT_PRESENT; + channel->base.bo = NULL; + channel->base.n_samplers = 0; + channel->base.mode = 0; +} + +static void +i915_shader_channel_fini (i915_device_t *device, + union i915_shader_channel *channel) +{ + switch (channel->type.pattern) { + case PATTERN_TEXTURE: + case PATTERN_BASE: + case PATTERN_LINEAR: + case PATTERN_RADIAL: + if (channel->base.bo != NULL) + intel_bo_destroy (&device->intel, channel->base.bo); + break; + + default: + case PATTERN_CONSTANT: + break; + } +} + +static void +i915_shader_channel_reset (i915_device_t *device, + union i915_shader_channel *channel) +{ + i915_shader_channel_fini (device, channel); + i915_shader_channel_init (channel); +} + +void +i915_shader_init (i915_shader_t *shader, + i915_surface_t *dst, + cairo_operator_t op, + double opacity) +{ + shader->committed = FALSE; + shader->device = i915_device (dst); + shader->target = dst; + shader->op = op; + shader->opacity = opacity; + + shader->blend = i915_get_blend (op, dst); + shader->need_combine = FALSE; + + shader->content = dst->intel.drm.base.content; + + i915_shader_channel_init (&shader->source); + i915_shader_channel_init (&shader->mask); + i915_shader_channel_init (&shader->clip); + i915_shader_channel_init (&shader->dst); +} + +static void +i915_set_shader_samplers (i915_device_t *device, + const i915_shader_t *shader) +{ + uint32_t n_samplers, n_maps, n; + uint32_t samplers[2*4]; + uint32_t maps[4*4]; + uint32_t mask, s, m; + + n_maps = + shader->source.base.n_samplers + + shader->mask.base.n_samplers + + shader->clip.base.n_samplers + + shader->dst.base.n_samplers; + assert (n_maps <= 4); + + if (n_maps == 0) + return; + + n_samplers = + !! shader->source.base.bo + + !! shader->mask.base.bo + + !! shader->clip.base.bo + + !! shader->dst.base.bo; + + mask = (1 << n_maps) - 1; + + /* We check for repeated setting of sample state mainly to catch + * continuation of text strings across multiple show-glyphs. + */ + s = m = 0; + if (shader->source.base.bo != NULL) { + samplers[s++] = shader->source.base.sampler[0]; + samplers[s++] = shader->source.base.sampler[1]; + maps[m++] = shader->source.base.bo->base.handle; + for (n = 0; n < shader->source.base.n_samplers; n++) { + maps[m++] = shader->source.base.offset[n]; + maps[m++] = shader->source.base.map[2*n+0]; + maps[m++] = shader->source.base.map[2*n+1]; + } + } + if (shader->mask.base.bo != NULL) { + samplers[s++] = shader->mask.base.sampler[0]; + samplers[s++] = shader->mask.base.sampler[1]; + maps[m++] = shader->mask.base.bo->base.handle; + for (n = 0; n < shader->mask.base.n_samplers; n++) { + maps[m++] = shader->mask.base.offset[n]; + maps[m++] = shader->mask.base.map[2*n+0]; + maps[m++] = shader->mask.base.map[2*n+1]; + } + } + if (shader->clip.base.bo != NULL) { + samplers[s++] = shader->clip.base.sampler[0]; + samplers[s++] = shader->clip.base.sampler[1]; + maps[m++] = shader->clip.base.bo->base.handle; + for (n = 0; n < shader->clip.base.n_samplers; n++) { + maps[m++] = shader->clip.base.offset[n]; + maps[m++] = shader->clip.base.map[2*n+0]; + maps[m++] = shader->clip.base.map[2*n+1]; + } + } + if (shader->dst.base.bo != NULL) { + samplers[s++] = shader->dst.base.sampler[0]; + samplers[s++] = shader->dst.base.sampler[1]; + maps[m++] = shader->dst.base.bo->base.handle; + for (n = 0; n < shader->dst.base.n_samplers; n++) { + maps[m++] = shader->dst.base.offset[n]; + maps[m++] = shader->dst.base.map[2*n+0]; + maps[m++] = shader->dst.base.map[2*n+1]; + } + } + + if (n_maps > device->current_n_maps || + memcmp (device->current_maps, + maps, + m * sizeof (uint32_t))) + { + memcpy (device->current_maps, maps, m * sizeof (uint32_t)); + device->current_n_maps = n_maps; + + if (device->current_source != NULL) + *device->current_source = 0; + if (device->current_mask != NULL) + *device->current_mask = 0; + if (device->current_clip != NULL) + *device->current_clip = 0; + +#if 0 + if (shader->source.type.pattern == PATTERN_TEXTURE) { + switch ((int) shader->source.surface.surface->type) { + case CAIRO_SURFACE_TYPE_DRM: + { + i915_surface_t *surface = + (i915_surface_t *) shader->source.surface.surface; + device->current_source = &surface->is_current_texture; + surface->is_current_texture |= CURRENT_SOURCE; + break; + } + + case I915_PACKED_PIXEL_SURFACE_TYPE: + { + i915_packed_pixel_surface_t *surface = + (i915_packed_pixel_surface_t *) shader->source.surface.surface; + device->current_source = &surface->is_current_texture; + surface->is_current_texture |= CURRENT_SOURCE; + break; + } + + default: + device->current_source = NULL; + break; + } + } else + device->current_source = NULL; + + if (shader->mask.type.pattern == PATTERN_TEXTURE) { + switch ((int) shader->mask.surface.surface->type) { + case CAIRO_SURFACE_TYPE_DRM: + { + i915_surface_t *surface = + (i915_surface_t *) shader->mask.surface.surface; + device->current_mask = &surface->is_current_texture; + surface->is_current_texture |= CURRENT_MASK; + break; + } + + case I915_PACKED_PIXEL_SURFACE_TYPE: + { + i915_packed_pixel_surface_t *surface = + (i915_packed_pixel_surface_t *) shader->mask.surface.surface; + device->current_mask = &surface->is_current_texture; + surface->is_current_texture |= CURRENT_MASK; + break; + } + + default: + device->current_mask = NULL; + break; + } + } else + device->current_mask = NULL; +#endif + + OUT_DWORD (_3DSTATE_MAP_STATE | (3 * n_maps)); + OUT_DWORD (mask); + for (n = 0; n < shader->source.base.n_samplers; n++) { + i915_batch_emit_reloc (device, shader->source.base.bo, + shader->source.base.offset[n], + I915_GEM_DOMAIN_SAMPLER, 0, + FALSE); + OUT_DWORD (shader->source.base.map[2*n+0]); + OUT_DWORD (shader->source.base.map[2*n+1]); + } + for (n = 0; n < shader->mask.base.n_samplers; n++) { + i915_batch_emit_reloc (device, shader->mask.base.bo, + shader->mask.base.offset[n], + I915_GEM_DOMAIN_SAMPLER, 0, + FALSE); + OUT_DWORD (shader->mask.base.map[2*n+0]); + OUT_DWORD (shader->mask.base.map[2*n+1]); + } + for (n = 0; n < shader->clip.base.n_samplers; n++) { + i915_batch_emit_reloc (device, shader->clip.base.bo, + shader->clip.base.offset[n], + I915_GEM_DOMAIN_SAMPLER, 0, + FALSE); + OUT_DWORD (shader->clip.base.map[2*n+0]); + OUT_DWORD (shader->clip.base.map[2*n+1]); + } + for (n = 0; n < shader->dst.base.n_samplers; n++) { + i915_batch_emit_reloc (device, shader->dst.base.bo, + shader->dst.base.offset[n], + I915_GEM_DOMAIN_SAMPLER, 0, + FALSE); + OUT_DWORD (shader->dst.base.map[2*n+0]); + OUT_DWORD (shader->dst.base.map[2*n+1]); + } + } + + if (n_samplers > device->current_n_samplers || + memcmp (device->current_samplers, + samplers, + s * sizeof (uint32_t))) + { + device->current_n_samplers = s; + memcpy (device->current_samplers, samplers, s * sizeof (uint32_t)); + + OUT_DWORD (_3DSTATE_SAMPLER_STATE | (3 * n_maps)); + OUT_DWORD (mask); + s = 0; + for (n = 0; n < shader->source.base.n_samplers; n++) { + OUT_DWORD (shader->source.base.sampler[0]); + OUT_DWORD (shader->source.base.sampler[1] | + (s << SS3_TEXTUREMAP_INDEX_SHIFT)); + OUT_DWORD (0x0); + s++; + } + for (n = 0; n < shader->mask.base.n_samplers; n++) { + OUT_DWORD (shader->mask.base.sampler[0]); + OUT_DWORD (shader->mask.base.sampler[1] | + (s << SS3_TEXTUREMAP_INDEX_SHIFT)); + OUT_DWORD (0x0); + s++; + } + for (n = 0; n < shader->clip.base.n_samplers; n++) { + OUT_DWORD (shader->clip.base.sampler[0]); + OUT_DWORD (shader->clip.base.sampler[1] | + (s << SS3_TEXTUREMAP_INDEX_SHIFT)); + OUT_DWORD (0x0); + s++; + } + for (n = 0; n < shader->dst.base.n_samplers; n++) { + OUT_DWORD (shader->dst.base.sampler[0]); + OUT_DWORD (shader->dst.base.sampler[1] | + (s << SS3_TEXTUREMAP_INDEX_SHIFT)); + OUT_DWORD (0x0); + s++; + } + } +} + +static uint32_t +i915_shader_get_texcoords (const i915_shader_t *shader) +{ + uint32_t texcoords; + uint32_t tu; + + texcoords = S2_TEXCOORD_NONE; + tu = 0; + if (shader->source.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { + texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); + texcoords |= S2_TEXCOORD_FMT (tu, shader->source.base.texfmt); + tu++; + } + if (shader->mask.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { + texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); + texcoords |= S2_TEXCOORD_FMT (tu, shader->mask.base.texfmt); + tu++; + } + if (shader->clip.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { + texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); + texcoords |= S2_TEXCOORD_FMT (tu, shader->clip.base.texfmt); + tu++; + } + if (shader->dst.base.texfmt != TEXCOORDFMT_NOT_PRESENT) { + texcoords &= ~S2_TEXCOORD_FMT (tu, S2_TEXCOORD_FMT0_MASK); + texcoords |= S2_TEXCOORD_FMT (tu, shader->dst.base.texfmt); + tu++; + } + + return texcoords; +} + +static void +i915_set_shader_mode (i915_device_t *device, + const i915_shader_t *shader) +{ + uint32_t texcoords; + uint32_t mask, cnt; + + texcoords = i915_shader_get_texcoords (shader); + + mask = cnt = 0; + + if (device->current_texcoords != texcoords) + mask |= I1_LOAD_S (2), cnt++; + + if (device->current_blend != shader->blend) + mask |= I1_LOAD_S (6), cnt++; + + if (cnt == 0) + return; + + OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | mask | (cnt-1)); + + if (device->current_texcoords != texcoords) { + OUT_DWORD (texcoords); + device->current_texcoords = texcoords; + } + + if (device->current_blend != shader->blend) { + if (shader->blend) { + OUT_DWORD (S6_CBUF_BLEND_ENABLE | S6_COLOR_WRITE_ENABLE | + (BLENDFUNC_ADD << S6_CBUF_BLEND_FUNC_SHIFT) | + shader->blend); + } else { + OUT_DWORD (S6_COLOR_WRITE_ENABLE); + } + + device->current_blend = shader->blend; + } +} + +static void +i915_set_constants (i915_device_t *device, + const uint32_t *constants, + uint32_t n_constants) +{ + uint32_t n; + + OUT_DWORD (_3DSTATE_PIXEL_SHADER_CONSTANTS | n_constants); + OUT_DWORD ((1 << (n_constants >> 2)) - 1); + + for (n = 0; n < n_constants; n++) + OUT_DWORD (constants[n]); + + device->current_n_constants = n_constants; + memcpy (device->current_constants, constants, n_constants*4); +} + +static uint32_t +pack_constants (const union i915_shader_channel *channel, + uint32_t *constants) +{ + uint32_t count = 0, n; + + switch (channel->type.fragment) { + case FS_ZERO: + case FS_ONE: + case FS_PURE: + case FS_DIFFUSE: + break; + + case FS_CONSTANT: + constants[count++] = pack_float (channel->solid.color.red); + constants[count++] = pack_float (channel->solid.color.green); + constants[count++] = pack_float (channel->solid.color.blue); + constants[count++] = pack_float (channel->solid.color.alpha); + break; + + case FS_LINEAR: + constants[count++] = pack_float (channel->linear.color0.red); + constants[count++] = pack_float (channel->linear.color0.green); + constants[count++] = pack_float (channel->linear.color0.blue); + constants[count++] = pack_float (channel->linear.color0.alpha); + + constants[count++] = pack_float (channel->linear.color1.red); + constants[count++] = pack_float (channel->linear.color1.green); + constants[count++] = pack_float (channel->linear.color1.blue); + constants[count++] = pack_float (channel->linear.color1.alpha); + break; + + case FS_RADIAL: + for (n = 0; n < ARRAY_LENGTH (channel->radial.constants); n++) + constants[count++] = pack_float (channel->radial.constants[n]); + break; + + case FS_TEXTURE: + case FS_YUV: + case FS_SPANS: + break; + } + + return count; +} + +static void +i915_set_shader_constants (i915_device_t *device, + const i915_shader_t *shader) +{ + uint32_t constants[4*4*3+4]; + unsigned n_constants; + + n_constants = 0; + if (shader->source.type.fragment == FS_DIFFUSE) { + uint32_t diffuse; + + diffuse = + ((uint32_t)(shader->source.solid.color.alpha_short >> 8) << 24) | + ((shader->source.solid.color.red_short >> 8) << 16) | + ((shader->source.solid.color.green_short >> 8) << 8) | + ((shader->source.solid.color.blue_short >> 8) << 0); + + if (diffuse != device->current_diffuse) { + OUT_DWORD (_3DSTATE_DFLT_DIFFUSE_CMD); + OUT_DWORD (diffuse); + device->current_diffuse = diffuse; + } + } else { + n_constants += pack_constants (&shader->source, constants + n_constants); + } + n_constants += pack_constants (&shader->mask, constants + n_constants); + + if (shader->opacity < 1.) { + constants[n_constants+0] = + constants[n_constants+1] = + constants[n_constants+2] = + constants[n_constants+3] = pack_float (shader->opacity); + n_constants += 4; + } + + if (n_constants != 0 && + (device->current_n_constants != n_constants || + memcmp (device->current_constants, constants, n_constants*4))) + { + i915_set_constants (device, constants, n_constants); + } +} + +static cairo_bool_t +i915_shader_needs_update (const i915_shader_t *shader, + const i915_device_t *device) +{ + uint32_t count, n; + uint32_t buf[64]; + + if (device->current_target != shader->target) + return TRUE; + + count = + !! shader->source.base.bo + + !! shader->mask.base.bo + + !! shader->clip.base.bo + + !! shader->dst.base.bo; + if (count > device->current_n_samplers) + return TRUE; + + count = + shader->source.base.n_samplers + + shader->mask.base.n_samplers + + shader->clip.base.n_samplers + + shader->dst.base.n_samplers; + if (count > device->current_n_maps) + return TRUE; + + if (count) { + count = 0; + if (shader->source.base.bo != NULL) { + buf[count++] = shader->source.base.sampler[0]; + buf[count++] = shader->source.base.sampler[1]; + } + if (shader->mask.base.bo != NULL) { + buf[count++] = shader->mask.base.sampler[0]; + buf[count++] = shader->mask.base.sampler[1]; + } + if (shader->clip.base.bo != NULL) { + buf[count++] = shader->clip.base.sampler[0]; + buf[count++] = shader->clip.base.sampler[1]; + } + if (shader->dst.base.bo != NULL) { + buf[count++] = shader->dst.base.sampler[0]; + buf[count++] = shader->dst.base.sampler[1]; + } + if (memcmp (device->current_samplers, buf, count * sizeof (uint32_t))) + return TRUE; + + count = 0; + if (shader->source.base.bo != NULL) { + buf[count++] = shader->source.base.bo->base.handle; + for (n = 0; n < shader->source.base.n_samplers; n++) { + buf[count++] = shader->source.base.offset[n]; + buf[count++] = shader->source.base.map[2*n+0]; + buf[count++] = shader->source.base.map[2*n+1]; + } + } + if (shader->mask.base.bo != NULL) { + buf[count++] = shader->mask.base.bo->base.handle; + for (n = 0; n < shader->mask.base.n_samplers; n++) { + buf[count++] = shader->mask.base.offset[n]; + buf[count++] = shader->mask.base.map[2*n+0]; + buf[count++] = shader->mask.base.map[2*n+1]; + } + } + if (shader->clip.base.bo != NULL) { + buf[count++] = shader->clip.base.bo->base.handle; + for (n = 0; n < shader->clip.base.n_samplers; n++) { + buf[count++] = shader->clip.base.offset[n]; + buf[count++] = shader->clip.base.map[2*n+0]; + buf[count++] = shader->clip.base.map[2*n+1]; + } + } + if (shader->dst.base.bo != NULL) { + buf[count++] = shader->dst.base.bo->base.handle; + for (n = 0; n < shader->dst.base.n_samplers; n++) { + buf[count++] = shader->dst.base.offset[n]; + buf[count++] = shader->dst.base.map[2*n+0]; + buf[count++] = shader->dst.base.map[2*n+1]; + } + } + if (memcmp (device->current_maps, buf, count * sizeof (uint32_t))) + return TRUE; + } + + if (i915_shader_get_texcoords (shader) != device->current_texcoords) + return TRUE; + if (device->current_blend != shader->blend) + return TRUE; + + count = 0; + if (shader->source.type.fragment == FS_DIFFUSE) { + uint32_t diffuse; + + diffuse = + ((uint32_t)(shader->source.solid.color.alpha_short >> 8) << 24) | + ((shader->source.solid.color.red_short >> 8) << 16) | + ((shader->source.solid.color.green_short >> 8) << 8) | + ((shader->source.solid.color.blue_short >> 8) << 0); + + if (diffuse != device->current_diffuse) + return TRUE; + } else { + count += pack_constants (&shader->source, buf + count); + } + count += pack_constants (&shader->mask, buf + count); + + if (count && + (device->current_n_constants != count || + memcmp (device->current_constants, buf, count*4))) + { + return TRUE; + } + + n = (i915_shader_channel_key (&shader->source) << 0) | + (i915_shader_channel_key (&shader->mask) << 8) | + (i915_shader_channel_key (&shader->clip) << 16) | + (shader->op << 24) | + ((shader->opacity < 1.) << 30) | + (((shader->content & CAIRO_CONTENT_ALPHA) == CAIRO_CONTENT_ALPHA) << 31); + return n != device->current_program; +} + +void +i915_set_dst (i915_device_t *device, i915_surface_t *dst) +{ + uint32_t size; + + if (device->current_target != dst) { + intel_bo_t *bo; + + bo = to_intel_bo (dst->intel.drm.bo); + assert (bo != NULL); + + OUT_DWORD (_3DSTATE_BUF_INFO_CMD); + OUT_DWORD (BUF_3D_ID_COLOR_BACK | + BUF_tiling (bo->tiling) | + BUF_3D_PITCH (dst->intel.drm.stride)); + OUT_RELOC (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); + + device->current_target = dst; + } + + if (dst->colorbuf != device->current_colorbuf) { + OUT_DWORD (_3DSTATE_DST_BUF_VARS_CMD); + OUT_DWORD (dst->colorbuf); + device->current_colorbuf = dst->colorbuf; + } + + size = DRAW_YMAX (dst->intel.drm.height) | DRAW_XMAX (dst->intel.drm.width); + if (size != device->current_size) { + OUT_DWORD (_3DSTATE_DRAW_RECT_CMD); + OUT_DWORD (0); /* dither */ + OUT_DWORD (0); /* top-left */ + OUT_DWORD (size); + OUT_DWORD (0); /* origin */ + device->current_size = size; + } +} + +static void +i915_set_shader_target (i915_device_t *device, + const i915_shader_t *shader) +{ + i915_set_dst (device, shader->target); +} + +int +i915_shader_num_texcoords (const i915_shader_t *shader) +{ + int cnt = 0; + + switch (shader->source.base.texfmt) { + default: + ASSERT_NOT_REACHED; + case TEXCOORDFMT_NOT_PRESENT: break; + case TEXCOORDFMT_2D: cnt += 2; break; + case TEXCOORDFMT_3D: cnt += 3; break; + case TEXCOORDFMT_4D: cnt += 4; break; + case TEXCOORDFMT_1D: cnt += 1; break; + case TEXCOORDFMT_2D_16: cnt += 1; break; + } + + switch (shader->mask.base.texfmt) { + default: + ASSERT_NOT_REACHED; + case TEXCOORDFMT_NOT_PRESENT: break; + case TEXCOORDFMT_2D: cnt += 2; break; + case TEXCOORDFMT_3D: cnt += 3; break; + case TEXCOORDFMT_4D: cnt += 4; break; + case TEXCOORDFMT_1D: cnt += 1; break; + case TEXCOORDFMT_2D_16: cnt += 1; break; + } + + switch (shader->clip.base.texfmt) { + default: + ASSERT_NOT_REACHED; + case TEXCOORDFMT_NOT_PRESENT: break; + case TEXCOORDFMT_2D: cnt += 2; break; + case TEXCOORDFMT_3D: cnt += 3; break; + case TEXCOORDFMT_4D: cnt += 4; break; + case TEXCOORDFMT_1D: cnt += 1; break; + case TEXCOORDFMT_2D_16: cnt += 1; break; + } + + switch (shader->dst.base.texfmt) { + default: + ASSERT_NOT_REACHED; + case TEXCOORDFMT_NOT_PRESENT: break; + case TEXCOORDFMT_2D: cnt += 2; break; + case TEXCOORDFMT_3D: cnt += 3; break; + case TEXCOORDFMT_4D: cnt += 4; break; + case TEXCOORDFMT_1D: cnt += 1; break; + case TEXCOORDFMT_2D_16: cnt += 1; break; + } + + return cnt; +} + +void +i915_shader_fini (i915_shader_t *shader) +{ + i915_device_t *device = i915_device (shader->target); + + i915_shader_channel_fini (device, &shader->source); + i915_shader_channel_fini (device, &shader->mask); + i915_shader_channel_fini (device, &shader->clip); +} + +void +i915_shader_set_clip (i915_shader_t *shader, + cairo_clip_t *clip) +{ + cairo_surface_t *clip_surface; + int clip_x, clip_y; + union i915_shader_channel *channel; + i915_surface_t *s; + + clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y); + assert (clip_surface->status == CAIRO_STATUS_SUCCESS); + assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM); + + channel = &shader->clip; + channel->type.vertex = VS_TEXTURE_16; + channel->base.texfmt = TEXCOORDFMT_2D_16; + channel->base.content = CAIRO_CONTENT_ALPHA; + + channel->type.fragment = FS_TEXTURE; + channel->surface.pixel = NONE; + + s = (i915_surface_t *) clip_surface; + channel->base.bo = to_intel_bo (s->intel.drm.bo); + channel->base.n_samplers = 1; + channel->base.offset[0] = s->offset; + channel->base.map[0] = s->map0; + channel->base.map[1] = s->map1; + + channel->base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (CAIRO_FILTER_NEAREST); + channel->base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (CAIRO_EXTEND_NONE); + + cairo_matrix_init_scale (&shader->clip.base.matrix, + 1. / s->intel.drm.width, + 1. / s->intel.drm.height); + cairo_matrix_translate (&shader->clip.base.matrix, + -clip_x, -clip_y); +} + +static cairo_status_t +i915_shader_check_aperture (i915_shader_t *shader, + i915_device_t *device) +{ + cairo_status_t status; + intel_bo_t *bo_array[4]; + uint32_t n = 0; + + if (shader->target != device->current_target) + bo_array[n++] = to_intel_bo (shader->target->intel.drm.bo); + + if (shader->source.base.bo != NULL) + bo_array[n++] = shader->source.base.bo; + + if (shader->mask.base.bo != NULL) + bo_array[n++] = shader->mask.base.bo; + + if (shader->clip.base.bo != NULL) + bo_array[n++] = shader->clip.base.bo; + + if (n == 0 || i915_check_aperture (device, bo_array, n)) + return CAIRO_STATUS_SUCCESS; + + status = i915_batch_flush (device); + if (unlikely (status)) + return status; + + assert (i915_check_aperture (device, bo_array, n)); + return CAIRO_STATUS_SUCCESS; +} + +static void +i915_shader_combine_mask (i915_shader_t *shader, i915_device_t *device) +{ + if (shader->mask.type.fragment == (i915_fragment_shader_t) -1 || + shader->mask.type.fragment == FS_CONSTANT) + { + return; + } + + if (shader->mask.type.fragment == FS_PURE) { + if (shader->mask.solid.pure & (1<<3)) { + shader->mask.type.fragment = FS_ONE; + } else { + shader->mask.type.fragment = FS_ZERO; + } + } + + if (shader->mask.type.fragment == FS_ONE || + (shader->mask.base.content & CAIRO_CONTENT_ALPHA) == 0) + { + i915_shader_channel_reset (device, &shader->mask); + } + + if (shader->mask.type.fragment == FS_ZERO) { + i915_shader_channel_fini (device, &shader->source); + + shader->source.type.fragment = FS_ZERO; + shader->source.type.vertex = VS_ZERO; + shader->source.base.texfmt = TEXCOORDFMT_NOT_PRESENT; + shader->source.base.mode = 0; + shader->source.base.n_samplers = 0; + } + + if (shader->source.type.fragment == FS_ZERO) { + i915_shader_channel_reset (device, &shader->mask); + i915_shader_channel_reset (device, &shader->clip); + } +} + +static void +i915_shader_setup_dst (i915_shader_t *shader) +{ + union i915_shader_channel *channel; + i915_surface_t *s; + + /* We need to manual blending if we have a clip surface and an unbounded op, + * or an extended blend mode. + */ + if (shader->need_combine || + (shader->op < CAIRO_OPERATOR_SATURATE && + (shader->clip.type.fragment == (i915_fragment_shader_t) -1 || + _cairo_operator_bounded_by_mask (shader->op)))) + { + return; + } + + shader->need_combine = TRUE; + + channel = &shader->dst; + channel->type.vertex = VS_TEXTURE_16; + channel->base.texfmt = TEXCOORDFMT_2D_16; + channel->base.content = shader->content; + + channel->type.fragment = FS_TEXTURE; + channel->surface.pixel = NONE; + + s = shader->target; + channel->base.bo = to_intel_bo (s->intel.drm.bo); + channel->base.n_samplers = 1; + channel->base.offset[0] = s->offset; + channel->base.map[0] = s->map0; + channel->base.map[1] = s->map1; + + channel->base.sampler[0] = + (MIPFILTER_NONE << SS2_MIP_FILTER_SHIFT) | + i915_texture_filter (CAIRO_FILTER_NEAREST); + channel->base.sampler[1] = + SS3_NORMALIZED_COORDS | + i915_texture_extend (CAIRO_EXTEND_NONE); + + cairo_matrix_init_scale (&shader->dst.base.matrix, + 1. / s->intel.drm.width, + 1. / s->intel.drm.height); +} + +static void +i915_shader_combine_source (i915_shader_t *shader, + i915_device_t *device) +{ + if (device->last_source_fragment == shader->source.type.fragment) + return; + + if (device->last_source_fragment == FS_DIFFUSE) { + switch (shader->source.type.fragment) { + case FS_ONE: + case FS_PURE: + case FS_CONSTANT: + case FS_DIFFUSE: + shader->source.type.fragment = FS_DIFFUSE; + shader->source.base.mode = 0; + break; + case FS_ZERO: + case FS_LINEAR: + case FS_RADIAL: + case FS_TEXTURE: + case FS_YUV: + case FS_SPANS: + default: + break; + } + } + + device->last_source_fragment = shader->source.type.fragment; +} + +static inline float * +i915_composite_vertex (float *v, + const i915_shader_t *shader, + double x, double y) +{ + double s, t; + + /* Each vertex is: + * 2 vertex coordinates + * [0-2] source texture coordinates + * [0-2] mask texture coordinates + */ + + *v++ = x; *v++ = y; + switch (shader->source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *v++ = i915_shader_linear_texcoord (&shader->source.linear, x, y); + break; + case VS_TEXTURE: + s = x, t = y; + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = s; *v++ = t; + break; + case VS_TEXTURE_16: + s = x, t = y; + cairo_matrix_transform_point (&shader->source.base.matrix, &s, &t); + *v++ = texcoord_2d_16 (s, t); + break; + } + switch (shader->mask.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *v++ = i915_shader_linear_texcoord (&shader->mask.linear, x, y); + break; + case VS_TEXTURE: + s = x, t = y; + cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t); + *v++ = s; *v++ = t; + break; + case VS_TEXTURE_16: + s = x, t = y; + cairo_matrix_transform_point (&shader->mask.base.matrix, &s, &t); + *v++ = texcoord_2d_16 (s, t); + break; + } + + return v; +} + +static inline void +i915_shader_add_rectangle_general (const i915_shader_t *shader, + int x, int y, + int w, int h) +{ + float *vertices; + + vertices = i915_add_rectangle (shader->device); + vertices = i915_composite_vertex (vertices, shader, x + w, y + h); + vertices = i915_composite_vertex (vertices, shader, x, y + h); + vertices = i915_composite_vertex (vertices, shader, x, y); + /* XXX overflow! */ +} + +void +i915_vbo_flush (i915_device_t *device) +{ + assert (device->floats_per_vertex); + assert (device->vertex_count); + + if (device->vbo == 0) { + OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | + I1_LOAD_S (0) | + I1_LOAD_S (1) | + 1); + device->vbo = device->batch.used++; + device->vbo_max_index = device->batch.used; + OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | + (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); + } + + OUT_DWORD (PRIM3D_RECTLIST | + PRIM3D_INDIRECT_SEQUENTIAL | + device->vertex_count); + OUT_DWORD (device->vertex_index); + + device->vertex_index += device->vertex_count; + device->vertex_count = 0; +} + +cairo_status_t +i915_shader_commit (i915_shader_t *shader, + i915_device_t *device) +{ + unsigned floats_per_vertex; + cairo_status_t status; + + assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); + + if (! shader->committed) { + device->shader = shader; + + i915_shader_combine_mask (shader, device); + i915_shader_combine_source (shader, device); + i915_shader_setup_dst (shader); + + shader->add_rectangle = i915_shader_add_rectangle_general; + + if ((status = setjmp (shader->unwind))) + return status; + + shader->committed = TRUE; + } + + if (i915_shader_needs_update (shader, device)) { + if (i915_batch_space (device) < 256) { + status = i915_batch_flush (device); + if (unlikely (status)) + return status; + } + + if (device->vertex_count) + i915_vbo_flush (device); + + status = i915_shader_check_aperture (shader, device); + if (unlikely (status)) + return status; + + update_shader: + i915_set_shader_target (device, shader); + i915_set_shader_mode (device, shader); + i915_set_shader_samplers (device, shader); + i915_set_shader_constants (device, shader); + i915_set_shader_program (device, shader); + } + + floats_per_vertex = 2 + i915_shader_num_texcoords (shader); + if (device->floats_per_vertex == floats_per_vertex) + return CAIRO_STATUS_SUCCESS; + + if (i915_batch_space (device) < 8) { + status = i915_batch_flush (device); + if (unlikely (status)) + return status; + + goto update_shader; + } + + if (device->vertex_count) + i915_vbo_flush (device); + + if (device->vbo) { + device->batch_base[device->vbo_max_index] |= device->vertex_index; + OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (1) | 0); + device->vbo_max_index = device->batch.used; + OUT_DWORD ((floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | + (floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); + } + + device->floats_per_vertex = floats_per_vertex; + device->rectangle_size = floats_per_vertex * 3 * sizeof (float); + device->vertex_index = + (device->vbo_used + 4*floats_per_vertex - 1) / (4 * floats_per_vertex); + device->vbo_offset = 4 * device->vertex_index * floats_per_vertex; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i915-spans.c b/gfx/cairo/cairo/src/drm/cairo-drm-i915-spans.c new file mode 100644 index 000000000000..f40bb2f2ed64 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i915-spans.c @@ -0,0 +1,799 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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 + */ + +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-drm-i915-private.h" + +/* Operates in either immediate or retained mode. + * When given a clip region we record the sequence of vbo and then + * replay them for each clip rectangle, otherwise we simply emit + * the vbo straight into the command stream. + */ + +typedef struct _i915_spans i915_spans_t; + +typedef float * +(*i915_get_rectangle_func_t) (i915_spans_t *spans); + +typedef void +(*i915_span_func_t) (i915_spans_t *spans, + int x0, int x1, int y0, int y1, + int alpha); + +struct _i915_spans { + cairo_span_renderer_t renderer; + + i915_device_t *device; + + int xmin, xmax; + cairo_bool_t is_bounded; + const cairo_rectangle_int_t *extents; + + i915_get_rectangle_func_t get_rectangle; + i915_span_func_t span; + i915_shader_t shader; + + cairo_region_t *clip_region; + cairo_bool_t need_clip_surface; + + struct vbo { + struct vbo *next; + intel_bo_t *bo; + unsigned int count; + } head, *tail; + + unsigned int vbo_offset; + float *vbo_base; +}; + +static float * +i915_emit_rectangle (i915_spans_t *spans) +{ + return i915_add_rectangle (spans->device); +} + +static float * +i915_accumulate_rectangle (i915_spans_t *spans) +{ + float *vertices; + uint32_t size; + + size = spans->device->rectangle_size; + if (unlikely (spans->vbo_offset + size > I915_VBO_SIZE)) { + struct vbo *vbo; + + vbo = _cairo_malloc (sizeof (struct vbo)); + if (unlikely (vbo == NULL)) { + /* throw error! */ + } + + spans->tail->next = vbo; + spans->tail = vbo; + + vbo->next = NULL; + vbo->bo = intel_bo_create (&spans->device->intel, + I915_VBO_SIZE, I915_VBO_SIZE, + FALSE, I915_TILING_NONE, 0); + vbo->count = 0; + + spans->vbo_offset = 0; + spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo); + } + + vertices = spans->vbo_base + spans->vbo_offset; + spans->vbo_offset += size; + spans->tail->count += 3; + + return vertices; +} + +static void +i915_span_zero (i915_spans_t *spans, + int x0, int x1, int y0, int y1, + int alpha) +{ + float *vertices; + + vertices = spans->get_rectangle (spans); + + *vertices++ = x1; + *vertices++ = y1; + + *vertices++ = x0; + *vertices++ = y1; + + *vertices++ = x0; + *vertices++ = y0; +} + +static void +i915_span_constant (i915_spans_t *spans, + int x0, int x1, int y0, int y1, + int alpha) +{ + float *vertices; + float a = alpha / 255.; + + vertices = spans->get_rectangle (spans); + + *vertices++ = x1; + *vertices++ = y1; + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y1; + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y0; + *vertices++ = a; +} + +static void +i915_span_linear (i915_spans_t *spans, + int x0, int x1, int y0, int y1, + int alpha) +{ + float *vertices; + float a = alpha / 255.; + double s, t; + + vertices = spans->get_rectangle (spans); + + *vertices++ = x1; + *vertices++ = y1; + s = x0, t = y0; + *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y1; + s = x1, t = y0; + *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y0; + s = x1, t = y1; + *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); + *vertices++ = a; +} + +static void +i915_span_texture (i915_spans_t *spans, + int x0, int x1, int y0, int y1, + int alpha) +{ + float *vertices; + float a = alpha / 255.; + double s, t; + + vertices = spans->get_rectangle (spans); + + *vertices++ = x1; + *vertices++ = y1; + s = x0, t = y0; + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = s; *vertices++ = t; + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y1; + s = x1, t = y0; + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = s; *vertices++ = t; + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y0; + s = x1, t = y1; + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = s; *vertices++ = t; + *vertices++ = a; +} + +static void +i915_span_texture16 (i915_spans_t *spans, + int x0, int x1, int y0, int y1, int alpha) +{ + float *vertices; + float a = alpha / 255.; + double s, t; + + vertices = spans->get_rectangle (spans); + + *vertices++ = x1; + *vertices++ = y1; + s = x0, t = y0; + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y1; + s = x1, t = y0; + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y0; + s = x1, t = y1; + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + *vertices++ = a; +} + +static void +i915_span_generic (i915_spans_t *spans, + int x0, int x1, int y0, int y1, int alpha) +{ + double s, t; + float *vertices; + float a = alpha / 255.; + + /* Each vertex is: + * 2 vertex coordinates + * [0-2] source texture coordinates + * 1 alpha value. + * [0,2] clip mask coordinates + */ + + vertices = spans->get_rectangle (spans); + + /* bottom right */ + *vertices++ = x1; *vertices++ = y1; + s = x1, t = y1; + switch (spans->shader.source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); + break; + case VS_TEXTURE: + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = s; *vertices++ = t; + break; + case VS_TEXTURE_16: + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + break; + } + *vertices++ = a; + if (spans->need_clip_surface) { + s = x1, t = y1; + cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + } + if (spans->shader.need_combine) { + s = x1, t = y1; + cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + } + + /* bottom left */ + *vertices++ = x0; *vertices++ = y1; + s = x0, t = y1; + switch (spans->shader.source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); + break; + case VS_TEXTURE: + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = s; *vertices++ = t; + break; + case VS_TEXTURE_16: + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + break; + } + *vertices++ = a; + if (spans->need_clip_surface) { + s = x0, t = y1; + cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + } + if (spans->shader.need_combine) { + s = x0, t = y1; + cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + } + + /* top left */ + *vertices++ = x0; *vertices++ = y0; + s = x0, t = y0; + switch (spans->shader.source.type.vertex) { + case VS_ZERO: + case VS_CONSTANT: + break; + case VS_LINEAR: + *vertices++ = i915_shader_linear_texcoord (&spans->shader.source.linear, s, t); + break; + case VS_TEXTURE: + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = s; *vertices++ = t; + break; + case VS_TEXTURE_16: + cairo_matrix_transform_point (&spans->shader.source.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + break; + } + *vertices++ = a; + if (spans->need_clip_surface) { + s = x0, t = y0; + cairo_matrix_transform_point (&spans->shader.clip.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + } + if (spans->shader.need_combine) { + s = x0, t = y0; + cairo_matrix_transform_point (&spans->shader.dst.base.matrix, &s, &t); + *vertices++ = texcoord_2d_16 (s, t); + } +} + +static cairo_status_t +i915_zero_spans_mono (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i915_spans_t *spans = abstract_renderer; + int x0, x1; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + while (num_spans && half[0].coverage < 128) + half++, num_spans--; + if (num_spans == 0) + break; + + x0 = x1 = half[0].x; + while (num_spans--) { + half++; + + x1 = half[0].x; + if (half[0].coverage < 128) + break; + } + + i915_span_zero (spans, + x0, x1, + y, y + height, + 0); + } while (num_spans); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_zero_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i915_spans_t *spans = abstract_renderer; + int x0, x1; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + while (num_spans && half[0].coverage == 0) + half++, num_spans--; + if (num_spans == 0) + break; + + x0 = x1 = half[0].x; + while (num_spans--) { + half++; + + x1 = half[0].x; + if (half[0].coverage == 0) + break; + } + + i915_span_zero (spans, + x0, x1, + y, y + height, + 0); + } while (num_spans); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_bounded_spans_mono (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i915_spans_t *spans = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (half[0].coverage >= 128) { + spans->span (spans, + half[0].x, half[1].x, + y, y + height, + 255); + } + half++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_bounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i915_spans_t *spans = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (half[0].coverage) { + spans->span (spans, + half[0].x, half[1].x, + y, y + height, + half[0].coverage); + } + half++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_unbounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i915_spans_t *spans = abstract_renderer; + + if (num_spans == 0) { + spans->span (spans, + spans->xmin, spans->xmax, + y, y + height, + 0); + return CAIRO_STATUS_SUCCESS; + } + + if (half[0].x != spans->xmin) { + spans->span (spans, + spans->xmin, half[0].x, + y, y + height, + 0); + } + + do { + spans->span (spans, + half[0].x, half[1].x, + y, y + height, + half[0].coverage); + half++; + } while (--num_spans > 1); + + if (half[0].x != spans->xmax) { + spans->span (spans, + half[0].x, spans->xmax, + y, y + height, + 0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_unbounded_spans_mono (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i915_spans_t *spans = abstract_renderer; + + if (num_spans == 0) { + spans->span (spans, + spans->xmin, spans->xmax, + y, y + height, + 0); + return CAIRO_STATUS_SUCCESS; + } + + if (half[0].x != spans->xmin) { + spans->span (spans, + spans->xmin, half[0].x, + y, y + height, + 0); + } + + do { + int alpha = 0; + if (half[0].coverage >= 128) + alpha = 255; + spans->span (spans, + half[0].x, half[1].x, + y, y + height, + alpha); + half++; + } while (--num_spans > 1); + + if (half[0].x != spans->xmax) { + spans->span (spans, + half[0].x, spans->xmax, + y, y + height, + 0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_spans_init (i915_spans_t *spans, + i915_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + cairo_clip_t *clip, + double opacity, + const cairo_composite_rectangles_t *extents) +{ + cairo_status_t status; + + spans->device = (i915_device_t *) dst->intel.drm.base.device; + + spans->is_bounded = extents->is_bounded; + if (extents->is_bounded) { + if (antialias == CAIRO_ANTIALIAS_NONE) + spans->renderer.render_rows = i915_bounded_spans_mono; + else + spans->renderer.render_rows = i915_bounded_spans; + + spans->extents = &extents->bounded; + } else { + if (antialias == CAIRO_ANTIALIAS_NONE) + spans->renderer.render_rows = i915_unbounded_spans_mono; + else + spans->renderer.render_rows = i915_unbounded_spans; + + spans->extents = &extents->unbounded; + } + spans->xmin = spans->extents->x; + spans->xmax = spans->extents->x + spans->extents->width; + + spans->clip_region = NULL; + spans->need_clip_surface = FALSE; + if (clip != NULL) { + cairo_region_t *clip_region = NULL; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + + spans->clip_region = clip_region; + spans->need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + } + + spans->head.next = NULL; + spans->head.bo = NULL; + spans->head.count = 0; + spans->tail = &spans->head; + + if (spans->clip_region == NULL) { + spans->get_rectangle = i915_emit_rectangle; + } else { + assert (! extents->is_bounded); + spans->get_rectangle = i915_accumulate_rectangle; + spans->head.bo = intel_bo_create (&spans->device->intel, + I915_VBO_SIZE, I915_VBO_SIZE, + FALSE, I915_TILING_NONE, 0); + if (unlikely (spans->head.bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo); + } + spans->vbo_offset = 0; + + i915_shader_init (&spans->shader, dst, op, opacity); + if (spans->need_clip_surface) + i915_shader_set_clip (&spans->shader, clip); + + status = i915_shader_acquire_pattern (&spans->shader, &spans->shader.source, + pattern, &extents->bounded); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static void +i915_spans_fini (i915_spans_t *spans) +{ + i915_shader_fini (&spans->shader); + + if (spans->head.bo != NULL) { + struct vbo *vbo, *next; + + intel_bo_destroy (&spans->device->intel, spans->head.bo); + for (vbo = spans->head.next; vbo != NULL; vbo = next) { + next = vbo->next; + intel_bo_destroy (&spans->device->intel, vbo->bo); + free (vbo); + } + } +} + +cairo_status_t +i915_clip_and_composite_spans (i915_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + i915_spans_func_t draw_func, + void *draw_closure, + const cairo_composite_rectangles_t*extents, + cairo_clip_t *clip, + double opacity) +{ + i915_spans_t spans; + i915_device_t *device; + cairo_status_t status; + struct vbo *vbo; + + if (i915_surface_needs_tiling (dst)) { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + status = i915_spans_init (&spans, dst, op, pattern, antialias, clip, opacity, extents); + if (unlikely (status)) + return status; + + spans.shader.mask.base.texfmt = TEXCOORDFMT_1D; + spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA; + spans.shader.mask.type.fragment = FS_SPANS; + + status = cairo_device_acquire (dst->intel.drm.base.device); + if (unlikely (status)) + goto CLEANUP_SPANS; + + if (dst->deferred_clear) { + status = i915_surface_clear (dst); + if (unlikely (status)) + goto CLEANUP_SPANS; + } + + device = i915_device (dst); + status = i915_shader_commit (&spans.shader, device); + if (unlikely (status)) + goto CLEANUP_DEVICE; + + if (! spans.shader.need_combine && ! spans.need_clip_surface) { + switch (spans.shader.source.type.vertex) { + case VS_ZERO: + spans.span = i915_span_zero; + if (extents->is_bounded) { + if (antialias == CAIRO_ANTIALIAS_NONE) + spans.renderer.render_rows = i915_zero_spans_mono; + else + spans.renderer.render_rows = i915_zero_spans; + } + break; + case VS_CONSTANT: + spans.span = i915_span_constant; + break; + case VS_LINEAR: + spans.span = i915_span_linear; + break; + case VS_TEXTURE: + spans.span = i915_span_texture; + break; + case VS_TEXTURE_16: + spans.span = i915_span_texture16; + break; + default: + spans.span = i915_span_generic; + break; + } + } else { + spans.span = i915_span_generic; + } + + status = draw_func (draw_closure, &spans.renderer, spans.extents); + if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) { + i915_vbo_finish (device); + + OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | ENABLE_SCISSOR_RECT); + for (vbo = &spans.head; vbo != NULL; vbo = vbo->next) { + int i, num_rectangles; + + /* XXX require_space & batch_flush */ + + OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | I1_LOAD_S (0) | I1_LOAD_S (1) | 1); + i915_batch_emit_reloc (device, vbo->bo, 0, + I915_GEM_DOMAIN_VERTEX, 0, + FALSE); + OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | + (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT) | + vbo->count); + + num_rectangles = cairo_region_num_rectangles (spans.clip_region); + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (spans.clip_region, i, &rect); + + OUT_DWORD (_3DSTATE_SCISSOR_RECT_0_CMD); + OUT_DWORD (SCISSOR_RECT_0_XMIN (rect.x) | + SCISSOR_RECT_0_YMIN (rect.y)); + OUT_DWORD (SCISSOR_RECT_0_XMAX (rect.x + rect.width) | + SCISSOR_RECT_0_YMAX (rect.y + rect.height)); + + OUT_DWORD (PRIM3D_RECTLIST | PRIM3D_INDIRECT_SEQUENTIAL | vbo->count); + OUT_DWORD (0); + } + } + OUT_DWORD (_3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT); + } + +CLEANUP_DEVICE: + cairo_device_release (dst->intel.drm.base.device); +CLEANUP_SPANS: + i915_spans_fini (&spans); + + return status; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i915-surface.c b/gfx/cairo/cairo/src/drm/cairo-drm-i915-surface.c new file mode 100644 index 000000000000..8fc5f7a9dd85 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i915-surface.c @@ -0,0 +1,2942 @@ +/* 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. + * + * ************************************************************************** + * This work was initially based upon xf86-video-intel/src/i915_render.c: + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Wang Zhenyu + * Eric Anholt + * + * ************************************************************************** + * and also upon libdrm/intel/intel_bufmgr_gem.c: + * Copyright © 2007 Red Hat Inc. + * Copyright © 2007 Intel Corporation + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: Thomas Hellström + * Keith Whitwell + * Eric Anholt + * Dave Airlie + */ + +/* XXX + * + * - Per thread context? Would it actually avoid many locks? + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-intel-private.h" +#include "cairo-drm-intel-command-private.h" +#include "cairo-drm-intel-ioctl-private.h" +#include "cairo-drm-i915-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-cache-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-region-private.h" +#include "cairo-surface-offset-private.h" +#include "cairo-image-surface-private.h" + +#include +#include +#include + +static const uint32_t i915_batch_setup[] = { + /* Disable line anti-aliasing */ + _3DSTATE_AA_CMD, + + /* Disable independent alpha blend */ + _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD | + IAB_MODIFY_ENABLE | + IAB_MODIFY_FUNC | (BLENDFUNC_ADD << IAB_FUNC_SHIFT) | + IAB_MODIFY_SRC_FACTOR | (BLENDFACT_ONE << IAB_SRC_FACTOR_SHIFT) | + IAB_MODIFY_DST_FACTOR | (BLENDFACT_ZERO << IAB_DST_FACTOR_SHIFT), + + /* Disable texture crossbar */ + _3DSTATE_COORD_SET_BINDINGS | + CSB_TCB (0, 0) | + CSB_TCB (1, 1) | + CSB_TCB (2, 2) | + CSB_TCB (3, 3) | + CSB_TCB (4, 4) | + CSB_TCB (5, 5) | + CSB_TCB (6, 6) | + CSB_TCB (7, 7), + + _3DSTATE_MODES_4_CMD | ENABLE_LOGIC_OP_FUNC | LOGIC_OP_FUNC (LOGICOP_COPY), + + _3DSTATE_LOAD_STATE_IMMEDIATE_1 | + I1_LOAD_S (2) | + I1_LOAD_S (3) | + I1_LOAD_S (4) | + I1_LOAD_S (5) | + I1_LOAD_S (6) | + 4, + S2_TEXCOORD_NONE, + 0, /* Disable texture coordinate wrap-shortest */ + (1 << S4_POINT_WIDTH_SHIFT) | + S4_LINE_WIDTH_ONE | + S4_FLATSHADE_ALPHA | + S4_FLATSHADE_FOG | + S4_FLATSHADE_SPECULAR | + S4_FLATSHADE_COLOR | + S4_CULLMODE_NONE | + S4_VFMT_XY, + 0, /* Disable stencil buffer */ + S6_COLOR_WRITE_ENABLE, + + _3DSTATE_SCISSOR_ENABLE_CMD | DISABLE_SCISSOR_RECT, + + /* disable indirect state */ + _3DSTATE_LOAD_INDIRECT, + 0, +}; + +static const cairo_surface_backend_t i915_surface_backend; + +static cairo_surface_t * +i915_surface_create_from_cacheable_image (cairo_drm_device_t *base_dev, + cairo_surface_t *source); + +static cairo_status_t +i915_bo_exec (i915_device_t *device, intel_bo_t *bo, uint32_t offset) +{ + struct drm_i915_gem_execbuffer2 execbuf; + int ret, cnt, i; + + /* Add the batch buffer to the validation list. */ + cnt = device->batch.exec_count; + if (cnt > 0 && bo->base.handle == device->batch.exec[cnt-1].handle) + i = cnt - 1; + else + i = device->batch.exec_count++; + device->batch.exec[i].handle = bo->base.handle; + device->batch.exec[i].relocation_count = device->batch.reloc_count; + device->batch.exec[i].relocs_ptr = (uintptr_t) device->batch.reloc; + device->batch.exec[i].alignment = 0; + device->batch.exec[i].offset = 0; + device->batch.exec[i].flags = 0; + device->batch.exec[i].rsvd1 = 0; + device->batch.exec[i].rsvd2 = 0; + + execbuf.buffers_ptr = (uintptr_t) device->batch.exec; + execbuf.buffer_count = device->batch.exec_count; + execbuf.batch_start_offset = offset; + execbuf.batch_len = (device->batch.used << 2) + sizeof (device->batch_header); + execbuf.DR1 = 0; + execbuf.DR4 = 0; + execbuf.num_cliprects = 0; + execbuf.cliprects_ptr = 0; + execbuf.flags = 0; + execbuf.rsvd1 = 0; + execbuf.rsvd2 = 0; + + do { + ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + } while (ret != 0 && errno == EINTR); + + if (device->debug & I915_DEBUG_SYNC && ret == 0) + ret = ! intel_bo_wait (&device->intel, bo); + + if (0 && ret) { + int n, m; + + fprintf (stderr, "Batch submission failed: %d\n", errno); + fprintf (stderr, " relocation entries: %d/%d\n", + device->batch.reloc_count, I915_MAX_RELOCS); + fprintf (stderr, " gtt size: (%zd/%zd), (%zd/%zd)\n", + device->batch.est_gtt_size, device->batch.gtt_avail_size, + device->batch.total_gtt_size, device->intel.gtt_avail_size); + + fprintf (stderr, " buffers:\n"); + for (n = 0; n < device->batch.exec_count; n++) { + fprintf (stderr, " exec[%d] = %d, %d/%d bytes, gtt = %qx\n", + n, + device->batch.exec[n].handle, + n == device->batch.exec_count - 1 ? bo->base.size : device->batch.target_bo[n]->base.size, + n == device->batch.exec_count - 1 ? bo->full_size : device->batch.target_bo[n]->full_size, + device->batch.exec[n].offset); + } + for (n = 0; n < device->batch.reloc_count; n++) { + for (m = 0; m < device->batch.exec_count; m++) + if (device->batch.exec[m].handle == device->batch.reloc[n].target_handle) + break; + + fprintf (stderr, " reloc[%d] = %d @ %qx -> %qx + %qx\n", n, + device->batch.reloc[n].target_handle, + device->batch.reloc[n].offset, + (unsigned long long) device->batch.exec[m].offset, + (unsigned long long) device->batch.reloc[n].delta); + + device->batch_base[(device->batch.reloc[n].offset - sizeof (device->batch_header)) / 4] = + device->batch.exec[m].offset + device->batch.reloc[n].delta; + } + + intel_dump_batchbuffer (device->batch_header, + execbuf.batch_len, + device->intel.base.chip_id); + } + assert (ret == 0); + + VG (VALGRIND_MAKE_MEM_DEFINED (device->batch.exec, sizeof (device->batch.exec[0]) * i)); + + bo->offset = device->batch.exec[i].offset; + bo->busy = TRUE; + if (bo->virtual) + intel_bo_unmap (bo); + bo->cpu = FALSE; + + while (cnt--) { + intel_bo_t *bo = device->batch.target_bo[cnt]; + + bo->offset = device->batch.exec[cnt].offset; + bo->exec = NULL; + bo->busy = TRUE; + bo->batch_read_domains = 0; + bo->batch_write_domain = 0; + cairo_list_del (&bo->cache_list); + + if (bo->virtual) + intel_bo_unmap (bo); + bo->cpu = FALSE; + + intel_bo_destroy (&device->intel, bo); + } + assert (cairo_list_is_empty (&device->intel.bo_in_flight)); + + device->batch.exec_count = 0; + device->batch.reloc_count = 0; + device->batch.fences = 0; + + device->batch.est_gtt_size = I915_BATCH_SIZE; + device->batch.total_gtt_size = I915_BATCH_SIZE; + + return ret == 0 ? CAIRO_STATUS_SUCCESS : _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + +void +i915_batch_add_reloc (i915_device_t *device, + uint32_t pos, + intel_bo_t *bo, + uint32_t offset, + uint32_t read_domains, + uint32_t write_domain, + cairo_bool_t needs_fence) +{ + int index; + + assert (offset < bo->base.size); + + if (bo->exec == NULL) { + device->batch.total_gtt_size += bo->base.size; + + if (! bo->busy) + device->batch.est_gtt_size += bo->base.size; + + assert (device->batch.exec_count < ARRAY_LENGTH (device->batch.exec)); + + index = device->batch.exec_count++; + device->batch.exec[index].handle = bo->base.handle; + device->batch.exec[index].relocation_count = 0; + device->batch.exec[index].relocs_ptr = 0; + device->batch.exec[index].alignment = 0; + device->batch.exec[index].offset = 0; + device->batch.exec[index].flags = 0; + device->batch.exec[index].rsvd1 = 0; + device->batch.exec[index].rsvd2 = 0; + + device->batch.target_bo[index] = intel_bo_reference (bo); + + bo->exec = &device->batch.exec[index]; + } + + if (bo->tiling != I915_TILING_NONE) { + uint32_t alignment; + +#if 0 + /* We presume that we will want to use a fence with X tiled objects... */ + if (needs_fence || bo->tiling == I915_TILING_X) + alignment = bo->full_size; + else + alignment = 2*((bo->stride + 4095) & -4096); +#else + alignment = bo->full_size; +#endif + if (bo->exec->alignment < alignment) + bo->exec->alignment = alignment; + + if (needs_fence && (bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) { + bo->exec->flags |= EXEC_OBJECT_NEEDS_FENCE; + device->batch.fences++; + + intel_bo_set_tiling (&device->intel, bo); + } + } + + assert (device->batch.reloc_count < ARRAY_LENGTH (device->batch.reloc)); + + index = device->batch.reloc_count++; + device->batch.reloc[index].offset = (pos << 2) + sizeof (device->batch_header); + device->batch.reloc[index].delta = offset; + device->batch.reloc[index].target_handle = bo->base.handle; + device->batch.reloc[index].read_domains = read_domains; + device->batch.reloc[index].write_domain = write_domain; + device->batch.reloc[index].presumed_offset = bo->offset; + + assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain); + bo->batch_read_domains |= read_domains; + bo->batch_write_domain |= write_domain; +} + +void +i915_vbo_finish (i915_device_t *device) +{ + intel_bo_t *vbo; + + assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); + assert (device->vbo_used); + + if (device->vertex_count) { + if (device->vbo == 0) { + OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | + I1_LOAD_S (0) | + I1_LOAD_S (1) | + 1); + device->vbo = device->batch.used++; + device->vbo_max_index = device->batch.used; + OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | + (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); + } + + OUT_DWORD (PRIM3D_RECTLIST | + PRIM3D_INDIRECT_SEQUENTIAL | + device->vertex_count); + OUT_DWORD (device->vertex_index); + } + + if (device->last_vbo != NULL) { + intel_bo_in_flight_add (&device->intel, device->last_vbo); + intel_bo_destroy (&device->intel, device->last_vbo); + } + + device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count; + + /* will include a few bytes of inter-array padding */ + vbo = intel_bo_create (&device->intel, + device->vbo_used, device->vbo_used, + FALSE, I915_TILING_NONE, 0); + i915_batch_fill_reloc (device, device->vbo, vbo, 0, + I915_GEM_DOMAIN_VERTEX, 0); + intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base); + device->last_vbo = vbo; + device->last_vbo_offset = (device->vbo_used+7)&-8; + device->last_vbo_space = vbo->base.size - device->last_vbo_offset; + + device->vbo = 0; + + device->vbo_used = device->vbo_offset = 0; + device->vertex_index = device->vertex_count = 0; + + if (! i915_check_aperture_size (device, 1, I915_VBO_SIZE, I915_VBO_SIZE)) { + cairo_status_t status; + + status = i915_batch_flush (device); + if (unlikely (status)) + longjmp (device->shader->unwind, status); + + status = i915_shader_commit (device->shader, device); + if (unlikely (status)) + longjmp (device->shader->unwind, status); + } +} + +/* XXX improve state tracker/difference and flush state on vertex emission */ +static void +i915_device_reset (i915_device_t *device) +{ + if (device->current_source != NULL) + *device->current_source = 0; + if (device->current_mask != NULL) + *device->current_mask = 0; + if (device->current_clip != NULL) + *device->current_clip = 0; + + device->current_target = NULL; + device->current_size = 0; + device->current_source = NULL; + device->current_mask = NULL; + device->current_clip = NULL; + device->current_texcoords = ~0; + device->current_blend = 0; + device->current_n_constants = 0; + device->current_n_samplers = 0; + device->current_n_maps = 0; + device->current_colorbuf = 0; + device->current_diffuse = 0; + device->current_program = ~0; + device->clear_alpha = ~0; + + device->last_source_fragment = ~0; +} + +static void +i915_batch_cleanup (i915_device_t *device) +{ + int i; + + for (i = 0; i < device->batch.exec_count; i++) { + intel_bo_t *bo = device->batch.target_bo[i]; + + bo->exec = NULL; + bo->batch_read_domains = 0; + bo->batch_write_domain = 0; + cairo_list_del (&bo->cache_list); + + intel_bo_destroy (&device->intel, bo); + } + + device->batch.exec_count = 0; + device->batch.reloc_count = 0; +} + +static void +i915_batch_vbo_finish (i915_device_t *device) +{ + assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); + + if (device->vbo || i915_batch_space (device) < (int32_t) device->vbo_used) { + intel_bo_t *vbo; + + if (device->vertex_count) { + if (device->vbo == 0) { + OUT_DWORD (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | + I1_LOAD_S (0) | + I1_LOAD_S (1) | + 1); + device->vbo = device->batch.used++; + device->vbo_max_index = device->batch.used; + OUT_DWORD ((device->floats_per_vertex << S1_VERTEX_WIDTH_SHIFT) | + (device->floats_per_vertex << S1_VERTEX_PITCH_SHIFT)); + } + + OUT_DWORD (PRIM3D_RECTLIST | + PRIM3D_INDIRECT_SEQUENTIAL | + device->vertex_count); + OUT_DWORD (device->vertex_index); + } + + if (device->last_vbo != NULL) + intel_bo_destroy (&device->intel, device->last_vbo); + + device->batch_base[device->vbo_max_index] |= device->vertex_index + device->vertex_count; + + /* will include a few bytes of inter-array padding */ + vbo = intel_bo_create (&device->intel, + device->vbo_used, device->vbo_used, + FALSE, I915_TILING_NONE, 0); + i915_batch_fill_reloc (device, device->vbo, + vbo, 0, + I915_GEM_DOMAIN_VERTEX, 0); + intel_bo_write (&device->intel, vbo, 0, device->vbo_used, device->vbo_base); + device->last_vbo = vbo; + device->last_vbo_offset = (device->vbo_used+7)&-8; + device->last_vbo_space = vbo->base.size - device->last_vbo_offset; + + device->vbo = 0; + } + else + { + /* Only a single rectlist in this batch, and no active vertex buffer. */ + OUT_DWORD (PRIM3D_RECTLIST | (device->vbo_used / 4 - 1)); + + memcpy (BATCH_PTR (device), device->vbo_base, device->vbo_used); + device->batch.used += device->vbo_used >> 2; + } + + device->vbo_used = device->vbo_offset = 0; + device->vertex_index = device->vertex_count = 0; +} + +cairo_status_t +i915_batch_flush (i915_device_t *device) +{ + intel_bo_t *batch; + cairo_status_t status; + uint32_t length, offset; + int n; + + assert (CAIRO_MUTEX_IS_LOCKED (device->intel.base.base.mutex)); + + if (device->vbo_used) + i915_batch_vbo_finish (device); + + if (device->batch.used == 0) + return CAIRO_STATUS_SUCCESS; + + i915_batch_emit_dword (device, MI_BATCH_BUFFER_END); + if ((device->batch.used & 1) != ((sizeof (device->batch_header)>>2) & 1)) + i915_batch_emit_dword (device, MI_NOOP); + + length = (device->batch.used << 2) + sizeof (device->batch_header); + + /* NB: it is faster to copy the data then map/unmap the batch, + * presumably because we frequently only use a small part of the buffer. + */ + batch = NULL; + if (device->last_vbo) { + if (length <= device->last_vbo_space) { + batch = device->last_vbo; + offset = device->last_vbo_offset; + + /* fixup the relocations */ + for (n = 0; n < device->batch.reloc_count; n++) + device->batch.reloc[n].offset += offset; + } else + intel_bo_destroy (&device->intel, device->last_vbo); + device->last_vbo = NULL; + } + if (batch == NULL) { + batch = intel_bo_create (&device->intel, + length, length, + FALSE, I915_TILING_NONE, 0); + if (unlikely (batch == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + i915_batch_cleanup (device); + goto BAIL; + } + + offset = 0; + } + intel_bo_write (&device->intel, batch, offset, length, device->batch_header); + status = i915_bo_exec (device, batch, offset); + intel_bo_destroy (&device->intel, batch); + +BAIL: + device->batch.used = 0; + + intel_glyph_cache_unpin (&device->intel); + intel_snapshot_cache_thaw (&device->intel); + + i915_device_reset (device); + + return status; +} + +#if 0 +static float * +i915_add_rectangles (i915_device_t *device, int num_rects, int *count) +{ + float *vertices; + uint32_t size; + int cnt; + + assert (device->floats_per_vertex); + + size = device->rectangle_size; + if (unlikely (device->vbo_offset + size > I915_VBO_SIZE)) + i915_vbo_finish (device); + + vertices = (float *) (device->vbo_base + device->vbo_offset); + cnt = (I915_VBO_SIZE - device->vbo_offset) / size; + if (cnt > num_rects) + cnt = num_rects; + device->vbo_used = device->vbo_offset += size * cnt; + device->vertex_count += 3 * cnt; + *count = cnt; + return vertices; +} +#endif + +static cairo_surface_t * +i915_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, int height) +{ + i915_surface_t *other; + cairo_format_t format; + uint32_t tiling = I915_TILING_DEFAULT; + + other = abstract_other; + if (content == other->intel.drm.base.content) + format = other->intel.drm.format; + else + format = _cairo_format_from_content (content); + + if (width * _cairo_format_bits_per_pixel (format) > 8 * 32*1024 || height > 64*1024) + return NULL; + + /* we presume that a similar surface will be used for blitting */ + if (i915_surface_needs_tiling (other)) + tiling = I915_TILING_X; + + return i915_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device, + format, + width, height, + tiling, TRUE); +} + +static cairo_status_t +i915_surface_finish (void *abstract_surface) +{ + i915_surface_t *surface = abstract_surface; + i915_device_t *device = i915_device (surface); + + if (surface->stencil != NULL) { + intel_bo_in_flight_add (&device->intel, surface->stencil); + intel_bo_destroy (&device->intel, surface->stencil); + } + + if (surface->is_current_texture) { + if (surface->is_current_texture & CURRENT_SOURCE) + device->current_source = NULL; + if (surface->is_current_texture & CURRENT_MASK) + device->current_mask = NULL; + if (surface->is_current_texture & CURRENT_CLIP) + device->current_clip = NULL; + device->current_n_samplers = 0; + } + + if (surface == device->current_target) + device->current_target = NULL; + + if (surface->cache != NULL) { + i915_image_private_t *node = surface->cache; + intel_buffer_cache_t *cache = node->container; + + if (--cache->ref_count == 0) { + intel_bo_in_flight_add (&device->intel, cache->buffer.bo); + intel_bo_destroy (&device->intel, cache->buffer.bo); + _cairo_rtree_fini (&cache->rtree); + cairo_list_del (&cache->link); + free (cache); + } else { + node->node.state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->node.link, &cache->rtree.available); + _cairo_rtree_node_collapse (&cache->rtree, node->node.parent); + } + } + + return intel_surface_finish (&surface->intel); +} + +static cairo_status_t +i915_surface_batch_flush (i915_surface_t *surface) +{ + cairo_status_t status; + intel_bo_t *bo; + + assert (surface->intel.drm.fallback == NULL); + + bo = to_intel_bo (surface->intel.drm.bo); + if (bo == NULL || bo->batch_write_domain == 0) + return CAIRO_STATUS_SUCCESS; + + status = cairo_device_acquire (surface->intel.drm.base.device); + if (unlikely (status)) + return status; + + status = i915_batch_flush (i915_device (surface)); + cairo_device_release (surface->intel.drm.base.device); + + return status; +} + +static cairo_status_t +i915_surface_flush (void *abstract_surface, + unsigned flags) +{ + i915_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->intel.drm.fallback == NULL) { + if (surface->intel.drm.base.finished) { + /* Forgo flushing on finish as the user cannot access the surface directly. */ + return CAIRO_STATUS_SUCCESS; + } + + if (surface->deferred_clear) { + status = i915_surface_clear (surface); + if (unlikely (status)) + return status; + } + + return i915_surface_batch_flush (surface); + } + + return intel_surface_flush (abstract_surface, flags); +} + +/* rasterisation */ + +static cairo_status_t +_composite_boxes_spans (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents) +{ + cairo_boxes_t *boxes = closure; + cairo_rectangular_scan_converter_t converter; + struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + int i; + + _cairo_rectangular_scan_converter_init (&converter, extents); + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP; + } + } + + status = converter.base.generate (&converter.base, renderer); + +CLEANUP: + converter.base.destroy (&converter.base); + return status; +} + +cairo_status_t +i915_fixup_unbounded (i915_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + i915_shader_t shader; + i915_device_t *device; + cairo_status_t status; + + if (clip != NULL) { + cairo_region_t *clip_region = NULL; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + assert (clip_region == NULL); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + clip = NULL; + } else { + if (extents->bounded.width == extents->unbounded.width && + extents->bounded.height == extents->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + if (clip != NULL) { + i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.); + i915_shader_set_clip (&shader, clip); + status = i915_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_white.base, + &extents->unbounded); + assert (status == CAIRO_STATUS_SUCCESS); + } else { + i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.); + status = i915_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_clear.base, + &extents->unbounded); + assert (status == CAIRO_STATUS_SUCCESS); + } + + device = i915_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return status; + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto BAIL; + + if (extents->bounded.width == 0 || extents->bounded.height == 0) { + shader.add_rectangle (&shader, + extents->unbounded.x, + extents->unbounded.y, + extents->unbounded.width, + extents->unbounded.height); + } else { + /* top */ + if (extents->bounded.y != extents->unbounded.y) { + shader.add_rectangle (&shader, + extents->unbounded.x, + extents->unbounded.y, + extents->unbounded.width, + extents->bounded.y - extents->unbounded.y); + } + + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + shader.add_rectangle (&shader, + extents->unbounded.x, + extents->bounded.y, + extents->bounded.x - extents->unbounded.x, + extents->bounded.height); + } + + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + shader.add_rectangle (&shader, + extents->bounded.x + extents->bounded.width, + extents->bounded.y, + extents->unbounded.x + extents->unbounded.width - (extents->bounded.x + extents->bounded.width), + extents->bounded.height); + } + + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + shader.add_rectangle (&shader, + extents->unbounded.x, + extents->bounded.y + extents->bounded.height, + extents->unbounded.width, + extents->unbounded.y + extents->unbounded.height - (extents->bounded.y + extents->bounded.height)); + } + } + + i915_shader_fini (&shader); + BAIL: + cairo_device_release (&device->intel.base.base); + return status; +} + +static cairo_status_t +i915_fixup_unbounded_boxes (i915_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + int i; + + if (boxes->num_boxes <= 1) + return i915_fixup_unbounded (dst, extents, clip); + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + clip = NULL; + } + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) { + i915_shader_t shader; + i915_device_t *device; + + if (clip != NULL) { + i915_shader_init (&shader, dst, CAIRO_OPERATOR_DEST_OVER, 1.); + i915_shader_set_clip (&shader, clip); + status = i915_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_white.base, + &extents->unbounded); + assert (status == CAIRO_STATUS_SUCCESS); + } else { + i915_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR, 1.); + status = i915_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_clear.base, + &extents->unbounded); + assert (status == CAIRO_STATUS_SUCCESS); + } + + device = i915_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + for (chunk = &clear.chunks; chunk != NULL; 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); + + shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); + } + } +err_device: + cairo_device_release (&device->intel.base.base); +err_shader: + i915_shader_fini (&shader); + } + + _cairo_boxes_fini (&clear); + + return status; +} + +static cairo_bool_t +i915_can_blt (i915_surface_t *dst, + const cairo_pattern_t *pattern) +{ + const cairo_surface_pattern_t *spattern; + i915_surface_t *src; + + spattern = (const cairo_surface_pattern_t *) pattern; + src = (i915_surface_t *) spattern->surface; + + if (src->intel.drm.base.device != dst->intel.drm.base.device) + return FALSE; + + if (! i915_surface_needs_tiling (dst)) + return FALSE; + + if (! _cairo_matrix_is_translation (&pattern->matrix)) + return FALSE; + + if (! (pattern->filter == CAIRO_FILTER_NEAREST || + pattern->filter == CAIRO_FILTER_FAST)) + { + if (! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.x0)) || + ! _cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->matrix.y0))) + { + return FALSE; + } + } + + return _cairo_format_bits_per_pixel (src->intel.drm.format) == + _cairo_format_bits_per_pixel (dst->intel.drm.format); +} + +static cairo_status_t +i915_blt (i915_surface_t *src, + i915_surface_t *dst, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y, + cairo_bool_t flush) +{ + i915_device_t *device; + intel_bo_t *bo_array[2]; + cairo_status_t status; + int br13, cmd; + + bo_array[0] = to_intel_bo (dst->intel.drm.bo); + bo_array[1] = to_intel_bo (src->intel.drm.bo); + + status = i915_surface_fallback_flush (src); + if (unlikely (status)) + return status; + + device = i915_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return status; + + if (! i915_check_aperture_and_fences (device, bo_array, 2) || + i915_batch_space (device) < 9) + { + status = i915_batch_flush (device); + if (unlikely (status)) + goto CLEANUP; + } + + cmd = XY_SRC_COPY_BLT_CMD; + br13 = (0xCC << 16) | dst->intel.drm.stride; + switch (dst->intel.drm.format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_RGB16_565: + br13 |= BR13_565; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + br13 |= BR13_8888; + cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; + break; + } + + OUT_DWORD (cmd); + OUT_DWORD (br13); + OUT_DWORD ((dst_y << 16) | dst_x); + OUT_DWORD (((dst_y + height - 1) << 16) | (dst_x + width - 1)); + OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); + OUT_DWORD ((src_y << 16) | src_x); + OUT_DWORD (src->intel.drm.stride); + OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0); + /* require explicit RenderCache flush for 2D -> 3D sampler? */ + if (flush) + OUT_DWORD (MI_FLUSH); + +CLEANUP: + cairo_device_release (&device->intel.base.base); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +i915_surface_copy_subimage (i915_device_t *device, + i915_surface_t *src, + const cairo_rectangle_int_t *extents, + cairo_bool_t flush, + i915_surface_t **clone_out) +{ + i915_surface_t *clone; + cairo_status_t status; + + clone = (i915_surface_t *) + i915_surface_create_internal (&device->intel.base, + src->intel.drm.format, + extents->width, + extents->height, + I915_TILING_X, TRUE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + status = i915_blt (src, clone, + extents->x, extents->y, + extents->width, extents->height, + 0, 0, + flush); + + if (unlikely (status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return status; + } + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i915_clear_boxes (i915_surface_t *dst, + const cairo_boxes_t *boxes) +{ + i915_device_t *device = i915_device (dst); + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) }; + int cmd, br13, clear = 0, i; + + cmd = XY_COLOR_BLT_CMD; + br13 = (0xCC << 16) | dst->intel.drm.stride; + switch (dst->intel.drm.format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_RGB16_565: + br13 |= BR13_565; + break; + case CAIRO_FORMAT_RGB24: + clear = 0xff000000; + case CAIRO_FORMAT_ARGB32: + br13 |= BR13_8888; + cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; + break; + } + + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return status; + + if (! i915_check_aperture_and_fences (device, bo_array, 1) || + i915_batch_space (device) < 6 * boxes->num_boxes) + { + status = i915_batch_flush (device); + if (unlikely (status)) + goto RELEASE; + } + + if (device->vertex_count) + i915_vbo_flush (device); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x2 <= x1 || y2 <= y1) + continue; + + OUT_DWORD (cmd); + OUT_DWORD (br13); + OUT_DWORD ((y1 << 16) | x1); + OUT_DWORD (((y2 - 1) << 16) | (x2 - 1)); + OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); + OUT_DWORD (clear); + } + } + +RELEASE: + cairo_device_release (&device->intel.base.base); + return status; +} + +static cairo_status_t +i915_surface_extract_X_from_Y (i915_device_t *device, + i915_surface_t *src, + const cairo_rectangle_int_t *extents, + i915_surface_t **clone_out) +{ + i915_surface_t *clone; + i915_shader_t shader; + cairo_surface_pattern_t pattern; + cairo_rectangle_int_t rect; + cairo_status_t status; + + status = i915_surface_fallback_flush (src); + if (unlikely (status)) + return status; + + clone = (i915_surface_t *) + i915_surface_create_internal (&device->intel.base, + src->intel.drm.format, + extents->width, + extents->height, + I915_TILING_X, TRUE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + i915_shader_init (&shader, clone, CAIRO_OPERATOR_SOURCE, 1.); + + _cairo_pattern_init_for_surface (&pattern, &src->intel.drm.base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); + + rect.x = rect.y = 0; + rect.width = extents->width; + rect.height = extents->height; + status = i915_shader_acquire_pattern (&shader, &shader.source, &pattern.base, &rect); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) + goto err_shader; + + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + shader.add_rectangle (&shader, 0, 0, extents->width, extents->height); + + cairo_device_release (&device->intel.base.base); + i915_shader_fini (&shader); + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; + +err_device: + cairo_device_release (&device->intel.base.base); +err_shader: + i915_shader_fini (&shader); + cairo_surface_destroy (&clone->intel.drm.base); + return status; +} + +static cairo_status_t +i915_blt_boxes (i915_surface_t *dst, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *spattern; + i915_device_t *device; + i915_surface_t *src; + cairo_surface_t *free_me = NULL; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + int br13, cmd, tx, ty; + intel_bo_t *bo_array[2]; + int i; + + if (! i915_can_blt (dst, pattern)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + spattern = (const cairo_surface_pattern_t *) pattern; + src = (i915_surface_t *) spattern->surface; + + if (src->intel.drm.base.is_clear) + return i915_clear_boxes (dst, boxes); + + if (pattern->extend != CAIRO_EXTEND_NONE && + (extents->x + tx < 0 || + extents->y + ty < 0 || + extents->x + tx + extents->width > src->intel.drm.width || + extents->y + ty + extents->height > src->intel.drm.height)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = i915_surface_fallback_flush (src); + if (unlikely (status)) + return status; + + tx = _cairo_lround (pattern->matrix.x0); + ty = _cairo_lround (pattern->matrix.y0); + + device = i915_device (dst); + if (to_intel_bo (src->intel.drm.bo)->tiling == I915_TILING_Y) { + cairo_rectangle_int_t extents; + + _cairo_boxes_extents (boxes, &extents); + extents.x += tx; + extents.y += ty; + + status = i915_surface_extract_X_from_Y (device, src, &extents, &src); + if (unlikely (status)) + return status; + + free_me = &src->intel.drm.base; + tx = -extents.x; + ty = -extents.y; + } + + bo_array[0] = to_intel_bo (dst->intel.drm.bo); + bo_array[1] = to_intel_bo (src->intel.drm.bo); + + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto CLEANUP_SURFACE; + + if (! i915_check_aperture_and_fences (device, bo_array, 2) || + i915_batch_space (device) < 8 * boxes->num_boxes) + { + status = i915_batch_flush (device); + if (unlikely (status)) + goto CLEANUP_DEVICE; + } + + cmd = XY_SRC_COPY_BLT_CMD; + br13 = (0xCC << 16) | dst->intel.drm.stride; + switch (dst->intel.drm.format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_RGB16_565: + br13 |= BR13_565; + break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + br13 |= BR13_8888; + cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; + break; + } + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x1 + tx < 0) + x1 = -tx; + if (x2 + tx > src->intel.drm.width) + x2 = src->intel.drm.width - tx; + + if (y1 + ty < 0) + y1 = -ty; + if (y2 + ty > src->intel.drm.height) + y2 = src->intel.drm.height - ty; + + if (x2 <= x1 || y2 <= y1) + continue; + if (x2 < 0 || y2 < 0) + continue; + if (x1 >= dst->intel.drm.width || y2 >= dst->intel.drm.height) + continue; + + OUT_DWORD (cmd); + OUT_DWORD (br13); + OUT_DWORD ((y1 << 16) | x1); + OUT_DWORD (((y2 - 1) << 16) | (x2 - 1)); + OUT_RELOC_FENCED (dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); + OUT_DWORD (((y1 + ty) << 16) | (x1 + tx)); + OUT_DWORD (src->intel.drm.stride); + OUT_RELOC_FENCED (src, I915_GEM_DOMAIN_RENDER, 0); + } + } + + /* XXX fixup blank portions */ + +CLEANUP_DEVICE: + cairo_device_release (&device->intel.base.base); +CLEANUP_SURFACE: + cairo_surface_destroy (free_me); + return status; +} + +static cairo_status_t +_upload_image_inplace (i915_surface_t *surface, + const cairo_pattern_t *source, + const cairo_rectangle_int_t *extents, + const cairo_boxes_t *boxes) +{ + i915_device_t *device; + const cairo_surface_pattern_t *pattern; + cairo_image_surface_t *image; + const struct _cairo_boxes_chunk *chunk; + intel_bo_t *bo; + int tx, ty, i; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pattern = (const cairo_surface_pattern_t *) source; + if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + image = (cairo_image_surface_t *) pattern->surface; + if (source->extend != CAIRO_EXTEND_NONE && + (extents->x + tx < 0 || + extents->y + ty < 0 || + extents->x + tx + extents->width > image->width || + extents->y + ty + extents->height > image->height)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + device = i915_device (surface); + bo = to_intel_bo (surface->intel.drm.bo); + if (bo->exec != NULL || ! intel_bo_is_inactive (&device->intel, bo)) { + intel_bo_t *new_bo; + cairo_bool_t need_clear = FALSE; + + if (boxes->num_boxes != 1 || + extents->width < surface->intel.drm.width || + extents->height < surface->intel.drm.height) + { + if (! surface->intel.drm.base.is_clear) + return CAIRO_INT_STATUS_UNSUPPORTED; + + need_clear = TRUE; + } + + new_bo = intel_bo_create (&device->intel, + bo->full_size, bo->base.size, + FALSE, bo->tiling, bo->stride); + if (unlikely (new_bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + intel_bo_in_flight_add (&device->intel, bo); + intel_bo_destroy (&device->intel, bo); + + bo = new_bo; + surface->intel.drm.bo = &bo->base; + + if (need_clear) { + memset (intel_bo_map (&device->intel, bo), 0, + bo->stride * surface->intel.drm.height); + } + } + + if (image->format == surface->intel.drm.format) { + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + cairo_status_t status; + + if (x1 + tx < 0) + x1 = -tx; + if (x2 + tx > image->width) + x2 = image->width - tx; + + if (y1 + ty < 0) + y1 = -ty; + if (y2 + ty > image->height) + y2 = image->height - ty; + + if (x2 <= x1 || y2 <= y1) + continue; + if (x2 < 0 || y2 < 0) + continue; + if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height) + continue; + + status = intel_bo_put_image (&device->intel, + bo, + image, + x1 + tx, y1 + ty, + x2 - x1, y2 - y1, + x1, y1); + if (unlikely (status)) + return status; + } + } + } else { + pixman_image_t *dst; + void *ptr; + + ptr = intel_bo_map (&device->intel, bo); + if (unlikely (ptr == NULL)) + return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + + dst = pixman_image_create_bits (_cairo_format_to_pixman_format_code (surface->intel.drm.format), + surface->intel.drm.width, + surface->intel.drm.height, + ptr, + surface->intel.drm.stride); + if (unlikely (dst == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x1 + tx < 0) + x1 = -tx; + if (x2 + tx > image->width) + x2 = image->width - tx; + + if (y1 + ty < 0) + y1 = -ty; + if (y2 + ty > image->height) + y2 = image->height - ty; + + if (x2 <= x1 || y2 <= y1) + continue; + if (x2 < 0 || y2 < 0) + continue; + if (x1 >= surface->intel.drm.width || y2 >= surface->intel.drm.height) + continue; + + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, dst, + x1 + tx, y1 + ty, + 0, 0, + x1, y1, + x2 - x1, y2 - y1); + } + } + + pixman_image_unref (dst); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_composite_boxes (i915_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + double opacity, + const cairo_composite_rectangles_t *extents) +{ + cairo_bool_t need_clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + i915_shader_t shader; + i915_device_t *device; + int i; + + /* If the boxes are not pixel-aligned, we will need to compute a real mask */ + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (clip == NULL && op == CAIRO_OPERATOR_SOURCE && opacity == 1.) { + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = i915_blt_boxes (dst, pattern, &extents->bounded, boxes); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _upload_image_inplace (dst, pattern, + &extents->bounded, boxes); + } + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + } + + if (i915_surface_needs_tiling (dst)) { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + i915_shader_init (&shader, dst, op, opacity); + + status = i915_shader_acquire_pattern (&shader, + &shader.source, + pattern, + &extents->bounded); + if (unlikely (status)) + return status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_surface) + i915_shader_set_clip (&shader, clip); + } + + device = i915_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x2 > x1 && y2 > y1) + shader.add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); + } + } + + if (! extents->is_bounded) + status = i915_fixup_unbounded_boxes (dst, extents, clip, boxes); + + err_device: + cairo_device_release (&device->intel.base.base); + err_shader: + i915_shader_fini (&shader); + + return status; +} + +cairo_status_t +i915_surface_clear (i915_surface_t *dst) +{ + i915_device_t *device; + cairo_status_t status; + intel_bo_t *bo_array[1] = { to_intel_bo (dst->intel.drm.bo) }; + + device = i915_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return status; + + if (i915_surface_needs_tiling (dst)) { + int cmd, br13, clear = 0; + + if (! i915_check_aperture_and_fences (device, bo_array, 1) || + i915_batch_space (device) < 6) + { + status = i915_batch_flush (device); + if (unlikely (status)) { + cairo_device_release (&device->intel.base.base); + return status; + } + } + + if (device->vertex_count) + i915_vbo_flush (device); + + cmd = XY_COLOR_BLT_CMD; + br13 = (0xCC << 16) | dst->intel.drm.stride; + switch (dst->intel.drm.format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_RGB16_565: + br13 |= BR13_565; + break; + case CAIRO_FORMAT_RGB24: + clear = 0xff000000; + case CAIRO_FORMAT_ARGB32: + br13 |= BR13_8888; + cmd |= XY_BLT_WRITE_ALPHA | XY_BLT_WRITE_RGB; + break; + } + + OUT_DWORD (cmd); + OUT_DWORD (br13); + OUT_DWORD (0); + OUT_DWORD (((dst->intel.drm.height - 1) << 16) | + (dst->intel.drm.width - 1)); + OUT_RELOC_FENCED (dst, + I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER); + OUT_DWORD (clear); + } else { + if (! i915_check_aperture (device, bo_array, 1) || + i915_batch_space (device) < 24) + { + status = i915_batch_flush (device); + if (unlikely (status)) { + cairo_device_release (&device->intel.base.base); + return status; + } + } + + if (device->vertex_count) + i915_vbo_flush (device); + + i915_set_dst (device, dst); + + /* set clear parameters */ + if (device->clear_alpha != (dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA)) { + device->clear_alpha = dst->intel.drm.base.content & CAIRO_CONTENT_ALPHA; + OUT_DWORD (_3DSTATE_CLEAR_PARAMETERS); + OUT_DWORD (CLEARPARAM_CLEAR_RECT | CLEARPARAM_WRITE_COLOR); + /* ZONE_INIT color */ + if (device->clear_alpha) /* XXX depends on pixel format, 16bit needs replication, 8bit? */ + OUT_DWORD (0x00000000); + else + OUT_DWORD (0xff000000); + OUT_DWORD (0); /* ZONE_INIT depth */ + /* CLEAR_RECT color */ + if (device->clear_alpha) + OUT_DWORD (0x00000000); + else + OUT_DWORD (0xff000000); + OUT_DWORD (0); /* CLEAR_RECT depth */ + OUT_DWORD (0); /* CLEAR_RECT stencil */ + } + + OUT_DWORD (PRIM3D_CLEAR_RECT | 5); + OUT_DWORD (pack_float (dst->intel.drm.width)); + OUT_DWORD (pack_float (dst->intel.drm.height)); + OUT_DWORD (0); + OUT_DWORD (pack_float (dst->intel.drm.height)); + OUT_DWORD (0); + OUT_DWORD (0); + } + + cairo_device_release (&device->intel.base.base); + + dst->deferred_clear = FALSE; + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (i915_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip, + double opacity) +{ + cairo_status_t status; + + if (boxes->num_boxes == 0) { + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + return i915_fixup_unbounded (dst, extents, clip); + } + + if (clip == NULL && + (op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && dst->intel.drm.base.is_clear)) && + opacity == 1. && + boxes->num_boxes == 1 && + extents->bounded.width == dst->intel.drm.width && + extents->bounded.height == dst->intel.drm.height) + { + op = CAIRO_OPERATOR_SOURCE; + dst->deferred_clear = FALSE; + + status = _upload_image_inplace (dst, src, + &extents->bounded, boxes); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + if (dst->deferred_clear) { + status = i915_surface_clear (dst); + if (unlikely (status)) + return status; + } + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, antialias, clip, opacity, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Otherwise render the boxes via an implicit mask and composite in the usual + * fashion. + */ + return i915_clip_and_composite_spans (dst, op, src, antialias, + _composite_boxes_spans, boxes, + extents, clip, opacity); +} + +static cairo_clip_path_t * +_clip_get_solitary_path (cairo_clip_t *clip) +{ + cairo_clip_path_t *iter = clip->path; + cairo_clip_path_t *path = NULL; + + do { + if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) { + if (path != NULL) + return FALSE; + + path = iter; + } + iter = iter->prev; + } while (iter != NULL); + + return path; +} + +typedef struct { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_polygon_info_t; + +static cairo_status_t +_composite_polygon_spans (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents) +{ + composite_polygon_info_t *info = closure; + cairo_botor_scan_converter_t converter; + cairo_status_t status; + cairo_box_t box; + + box.p1.x = _cairo_fixed_from_int (extents->x); + box.p1.y = _cairo_fixed_from_int (extents->y); + box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); + box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); + + _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); + + status = converter.base.add_polygon (&converter.base, &info->polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = converter.base.generate (&converter.base, renderer); + + converter.base.destroy (&converter.base); + + return status; +} + +static cairo_int_status_t +i915_surface_fill_with_alpha (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip, + double opacity) +{ + i915_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + composite_polygon_info_t info; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (extents.is_bounded && clip != NULL) { + cairo_clip_path_t *clip_path; + + if (((clip_path = _clip_get_solitary_path (clip)) != NULL) && + _cairo_path_fixed_equal (&clip_path->path, path)) + { + clip = NULL; + } + } + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + assert (! _cairo_path_fixed_fill_is_empty (path)); + + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + &extents, clip, + opacity); + } + + _cairo_boxes_fini (&boxes); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto CLEANUP_BOXES; + } + + _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (extents.is_bounded) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); + if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) + goto CLEANUP_POLYGON; + } + + if (info.polygon.num_edges == 0) { + if (! extents.is_bounded) + status = i915_fixup_unbounded (dst, &extents, clip); + + goto CLEANUP_POLYGON; + } + + info.fill_rule = fill_rule; + info.antialias = antialias; + status = i915_clip_and_composite_spans (dst, op, source, antialias, + _composite_polygon_spans, &info, + &extents, clip, opacity); + +CLEANUP_POLYGON: + _cairo_polygon_fini (&info.polygon); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i915_surface_paint_with_alpha (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip, + double opacity) +{ + i915_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_clip_path_t *clip_path; + cairo_boxes_t boxes; + int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded); + cairo_box_t *clip_boxes = boxes.boxes_embedded; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_paint (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + /* If the clip cannot be reduced to a set of boxes, we will need to + * use a clipmask. Paint is special as it is the only operation that + * does not implicitly use a mask, so we may be able to reduce this + * operation to a fill... + */ + if (clip != NULL && + extents.is_bounded && + (clip_path = _clip_get_solitary_path (clip)) != NULL) + { + status = i915_surface_fill_with_alpha (dst, op, source, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias, + NULL, opacity); + } + else + { + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _clip_and_composite_boxes (dst, op, source, + &boxes, CAIRO_ANTIALIAS_DEFAULT, + &extents, clip, opacity); + } + if (clip_boxes != boxes.boxes_embedded) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i915_surface_paint (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + i915_surface_t *dst = abstract_dst; + + /* XXX unsupported operators? use pixel shader blending, eventually */ + + if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { + dst->deferred_clear = TRUE; + return CAIRO_STATUS_SUCCESS; + } + + return i915_surface_paint_with_alpha (dst, op, source, clip, 1.); +} + +static cairo_int_status_t +i915_surface_mask (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + i915_surface_t *dst = abstract_dst; + i915_device_t *device; + cairo_composite_rectangles_t extents; + i915_shader_t shader; + cairo_clip_t local_clip; + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_surface = FALSE; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; + return i915_surface_paint_with_alpha (dst, op, source, clip, solid->color.alpha); + } + + status = _cairo_composite_rectangles_init_for_mask (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + + have_clip = TRUE; + } + + i915_shader_init (&shader, dst, op, 1.); + + status = i915_shader_acquire_pattern (&shader, + &shader.source, + source, + &extents.bounded); + if (unlikely (status)) + goto err_shader; + + status = i915_shader_acquire_pattern (&shader, + &shader.mask, + mask, + &extents.bounded); + if (unlikely (status)) + goto err_shader; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + if (unlikely (_cairo_status_is_error (status) || + status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + { + goto err_shader; + } + + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_surface) + i915_shader_set_clip (&shader, clip); + + if (clip_region != NULL) { + cairo_rectangle_int_t rect; + cairo_bool_t is_empty; + + status = CAIRO_STATUS_SUCCESS; + cairo_region_get_extents (clip_region, &rect); + is_empty = ! _cairo_rectangle_intersect (&extents.unbounded, &rect); + if (unlikely (is_empty)) + goto err_shader; + + is_empty = ! _cairo_rectangle_intersect (&extents.bounded, &rect); + if (unlikely (is_empty && extents.is_bounded)) + goto err_shader; + + if (cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + } + } + + if (i915_surface_needs_tiling (dst)) { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + device = i915_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + if (dst->deferred_clear) { + status = i915_surface_clear (dst); + if (unlikely (status)) + goto err_shader; + } + + status = i915_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + if (clip_region != NULL) { + unsigned int n, num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rectangles; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + + shader.add_rectangle (&shader, + rect.x, rect.y, + rect.x + rect.width, rect.y + rect.height); + } + } else { + shader.add_rectangle (&shader, + extents.bounded.x, extents.bounded.y, + extents.bounded.x + extents.bounded.width, + extents.bounded.y + extents.bounded.height); + } + + if (! extents.is_bounded) + status = i915_fixup_unbounded (dst, &extents, clip); + + err_device: + cairo_device_release (&device->intel.base.base); + err_shader: + i915_shader_fini (&shader); + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i915_surface_stroke (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + 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, + cairo_clip_t *clip) +{ + i915_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + composite_polygon_info_t info; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, + path, stroke_style, ctm, + clip); + if (unlikely (status)) + return status; + + if (_cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + stroke_style, + ctm, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + &extents, clip, 1.); + } + + _cairo_boxes_fini (&boxes); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto CLEANUP_BOXES; + } + + _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &info.polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (extents.is_bounded) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); + if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) + goto CLEANUP_POLYGON; + } + + if (info.polygon.num_edges == 0) { + if (! extents.is_bounded) + status = i915_fixup_unbounded (dst, &extents, clip); + + goto CLEANUP_POLYGON; + } + + info.fill_rule = CAIRO_FILL_RULE_WINDING; + info.antialias = antialias; + status = i915_clip_and_composite_spans (dst, op, source, antialias, + _composite_polygon_spans, &info, + &extents, clip, 1.); + +CLEANUP_POLYGON: + _cairo_polygon_fini (&info.polygon); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i915_surface_fill (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + return i915_surface_fill_with_alpha (abstract_dst, op, source, path, fill_rule, tolerance, antialias, clip, 1.); +} + +static const cairo_surface_backend_t i915_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + i915_surface_create_similar, + i915_surface_finish, + + NULL, + intel_surface_acquire_source_image, + intel_surface_release_source_image, + + NULL, NULL, NULL, + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_drm_surface_get_font_options, + + i915_surface_flush, + NULL, /* mark_dirty */ + intel_scaled_font_fini, + intel_scaled_glyph_fini, + + i915_surface_paint, + i915_surface_mask, + i915_surface_stroke, + i915_surface_fill, + i915_surface_glyphs, +}; + +static void +i915_surface_init (i915_surface_t *surface, + cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + intel_surface_init (&surface->intel, &i915_surface_backend, device, + format, width, height); + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_ARGB32: + surface->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; + surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER; + break; + case CAIRO_FORMAT_RGB24: + surface->map0 = MAPSURF_32BIT | MT_32BIT_XRGB8888; + surface->colorbuf = COLR_BUF_ARGB8888 | DEPTH_FRMT_24_FIXED_8_OTHER; + break; + case CAIRO_FORMAT_RGB16_565: + surface->map0 = MAPSURF_16BIT | MT_16BIT_RGB565; + surface->colorbuf = COLR_BUF_RGB565; + break; + case CAIRO_FORMAT_A8: + surface->map0 = MAPSURF_8BIT | MT_8BIT_A8; + surface->colorbuf = COLR_BUF_8BIT | DEPTH_FRMT_24_FIXED_8_OTHER; + break; + } + surface->colorbuf |= DSTORG_HORT_BIAS (0x8) | DSTORG_VERT_BIAS (0x8); + surface->map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) | + ((width - 1) << MS3_WIDTH_SHIFT); + surface->map1 = 0; + + surface->is_current_texture = 0; + surface->deferred_clear = FALSE; + + surface->offset = 0; + + surface->stencil = NULL; + surface->cache = NULL; +} + +cairo_surface_t * +i915_surface_create_internal (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height, + uint32_t tiling, + cairo_bool_t gpu_target) +{ + i915_surface_t *surface; + cairo_status_t status_ignored; + + surface = _cairo_malloc (sizeof (i915_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + i915_surface_init (surface, base_dev, format, width, height); + + if (width && height) { + uint32_t size, stride; + intel_bo_t *bo; + + width = (width + 3) & -4; + stride = cairo_format_stride_for_width (surface->intel.drm.format, width); + /* check for tiny surfaces for which tiling is irrelevant */ + if (height * stride <= 4096) + tiling = I915_TILING_NONE; + if (tiling != I915_TILING_NONE && stride <= 512) + tiling = I915_TILING_NONE; + if (tiling != I915_TILING_NONE) { + if (height <= 8) + tiling = I915_TILING_NONE; + else if (height <= 16) + tiling = I915_TILING_X; + } + /* large surfaces we need to blt, so force TILING_X */ + if (height > 2048) + tiling = I915_TILING_X; + /* but there is a maximum limit to the tiling pitch */ + if (tiling != I915_TILING_NONE && stride > 8192) + tiling = I915_TILING_NONE; + + stride = i915_tiling_stride (tiling, stride); + assert (stride >= (uint32_t) cairo_format_stride_for_width (surface->intel.drm.format, width)); + assert (tiling == I915_TILING_NONE || stride <= 8192); + height = i915_tiling_height (tiling, height); + if (height > 64*1024) { + free (surface); + cairo_device_destroy (&base_dev->base); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + size = stride * height; + bo = intel_bo_create (to_intel_device (&base_dev->base), + i915_tiling_size (tiling, size), size, + gpu_target, tiling, stride); + if (bo == NULL) { + status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + assert (bo->base.size >= size); + + surface->intel.drm.bo = &bo->base; + surface->intel.drm.stride = stride; + + surface->map0 |= MS3_tiling (tiling); + surface->map1 = (stride/4 - 1) << MS4_PITCH_SHIFT; + } + + return &surface->intel.drm.base; +} + +static cairo_surface_t * +i915_surface_create (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_INVALID: + default: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + return i915_surface_create_internal (base_dev, format, width, height, + I915_TILING_DEFAULT, TRUE); +} + +static cairo_surface_t * +i915_surface_create_for_name (cairo_drm_device_t *base_dev, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + i915_surface_t *surface; + + /* Vol I, p134: size restrictions for textures */ + /* Vol I, p129: destination surface stride must be a multiple of 32 bytes */ + if (stride < cairo_format_stride_for_width (format, (width + 3) & -4) || + stride & 31) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + } + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + } + + surface = _cairo_malloc (sizeof (i915_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + i915_surface_init (surface, base_dev, format, width, height); + + if (width && height) { + surface->intel.drm.stride = stride; + surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT; + + surface->intel.drm.bo = + &intel_bo_create_for_name (to_intel_device (&base_dev->base), + name)->base; + if (unlikely (surface->intel.drm.bo == NULL)) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + to_intel_bo (surface->intel.drm.bo)->stride = stride; + + surface->map0 |= MS3_tiling (to_intel_bo (surface->intel.drm.bo)->tiling); + } + + return &surface->intel.drm.base; +} + +static cairo_status_t +i915_buffer_cache_init (intel_buffer_cache_t *cache, + i915_device_t *device, + cairo_format_t format, + int width, int height) +{ + const uint32_t tiling = I915_TILING_DEFAULT; + uint32_t stride, size; + + assert ((width & 3) == 0); + assert ((height & 1) == 0); + cache->buffer.width = width; + cache->buffer.height = height; + + switch (format) { + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB16_565: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_ARGB32: + cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; + stride = width * 4; + break; + case CAIRO_FORMAT_A8: + cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8; + stride = width; + break; + } + assert ((stride & 7) == 0); + assert (i915_tiling_stride (tiling, stride) == stride); + assert (i915_tiling_height (tiling, height) == height); + + size = height * stride; + assert (i915_tiling_size (tiling, size) == size); + cache->buffer.bo = intel_bo_create (&device->intel, size, size, FALSE, tiling, stride); + if (unlikely (cache->buffer.bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cache->buffer.stride = cache->buffer.bo->stride; + + cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) | + ((width - 1) << MS3_WIDTH_SHIFT); + cache->buffer.map0 |= MS3_tiling (tiling); + cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT; + + cache->ref_count = 0; + cairo_list_init (&cache->link); + + return CAIRO_STATUS_SUCCESS; +} + +i915_surface_t * +i915_surface_create_from_cacheable_image_internal (i915_device_t *device, + cairo_image_surface_t *image) +{ + i915_surface_t *surface; + cairo_status_t status; + cairo_list_t *caches; + intel_buffer_cache_t *cache; + cairo_rtree_node_t *node; + cairo_format_t format; + int width, height, bpp; + + format = image->format; + if (format == CAIRO_FORMAT_A1) + format = CAIRO_FORMAT_A8; + + width = image->width; + height = image->height; + if (width > IMAGE_CACHE_WIDTH/2 || height > IMAGE_CACHE_HEIGHT/2) { + surface = (i915_surface_t *) + i915_surface_create_internal (&device->intel.base, + format, + width, height, + I915_TILING_NONE, FALSE); + if (unlikely (surface->intel.drm.base.status)) + return surface; + + status = intel_bo_put_image (&device->intel, + to_intel_bo (surface->intel.drm.bo), + image, + 0, 0, + width, height, + 0, 0); + + if (unlikely (status)) { + cairo_surface_destroy (&surface->intel.drm.base); + return (i915_surface_t *) _cairo_surface_create_in_error (status); + } + + return surface; + } + + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return (i915_surface_t *) _cairo_surface_create_in_error (status); + + switch (image->format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB16_565: + caches = &device->image_caches[0]; + format = CAIRO_FORMAT_ARGB32; + bpp = 4; + break; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + caches = &device->image_caches[1]; + format = CAIRO_FORMAT_A8; + bpp = 1; + break; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + goto CLEANUP_DEVICE; + } + + node = NULL; + cairo_list_foreach_entry (cache, intel_buffer_cache_t, caches, link) { + if (! intel_bo_is_inactive (&device->intel, cache->buffer.bo)) + continue; + + status = _cairo_rtree_insert (&cache->rtree, width, height, &node); + if (unlikely (_cairo_status_is_error (status))) + goto CLEANUP_DEVICE; + if (status == CAIRO_STATUS_SUCCESS) + break; + } + if (node == NULL) { + cache = _cairo_malloc (sizeof (intel_buffer_cache_t)); + if (unlikely (cache == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DEVICE; + } + + status = i915_buffer_cache_init (cache, device, format, + IMAGE_CACHE_WIDTH, + IMAGE_CACHE_HEIGHT); + if (unlikely (status)) { + free (cache); + goto CLEANUP_DEVICE; + } + + _cairo_rtree_init (&cache->rtree, + IMAGE_CACHE_WIDTH, + IMAGE_CACHE_HEIGHT, + 4, + sizeof (i915_image_private_t)); + + status = _cairo_rtree_insert (&cache->rtree, width, height, &node); + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_list_init (&cache->link); + } + cairo_list_move (&cache->link, caches); + ((i915_image_private_t *) node)->container = cache; + + status = intel_bo_put_image (&device->intel, + cache->buffer.bo, + image, + 0, 0, + width, height, + node->x, node->y); + if (unlikely (status)) + goto CLEANUP_CACHE; + + surface = _cairo_malloc (sizeof (i915_surface_t)); + if (unlikely (surface == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_CACHE; + } + + i915_surface_init (surface, &device->intel.base, + format, width, height); + + surface->intel.drm.stride = cache->buffer.stride; + + surface->map0 |= MS3_tiling (cache->buffer.bo->tiling); + surface->map1 = (surface->intel.drm.stride/4 - 1) << MS4_PITCH_SHIFT; + + surface->intel.drm.bo = &intel_bo_reference (cache->buffer.bo)->base; + surface->offset = node->y * cache->buffer.stride + bpp * node->x; + + surface->cache = (i915_image_private_t *) node; + cache->ref_count++; + + cairo_device_release (&device->intel.base.base); + + return surface; + +CLEANUP_CACHE: + _cairo_rtree_node_destroy (&cache->rtree, node); + if (cache->ref_count == 0) { + intel_bo_destroy (&device->intel, cache->buffer.bo); + _cairo_rtree_fini (&cache->rtree); + cairo_list_del (&cache->link); + free (cache); + } +CLEANUP_DEVICE: + cairo_device_release (&device->intel.base.base); + return (i915_surface_t *) _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +i915_surface_create_from_cacheable_image (cairo_drm_device_t *device, + cairo_surface_t *source) +{ + i915_surface_t *surface; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = i915_surface_create_from_cacheable_image_internal ((i915_device_t *) device, image); + + _cairo_surface_release_source_image (source, image, image_extra); + + return &surface->intel.drm.base; +} + +static cairo_status_t +i915_surface_enable_scan_out (void *abstract_surface) +{ + i915_surface_t *surface = abstract_surface; + intel_bo_t *bo; + cairo_status_t status; + + if (unlikely (surface->intel.drm.bo == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + + bo = to_intel_bo (surface->intel.drm.bo); + if (bo->tiling == I915_TILING_Y) { + status = i915_surface_batch_flush (surface); + if (unlikely (status)) + return status; + + bo->tiling = I915_TILING_X; + surface->map0 &= ~MS3_tiling (I915_TILING_Y); + surface->map0 |= MS3_tiling (I915_TILING_X); + } + + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +i915_device_flush (cairo_drm_device_t *device) +{ + cairo_status_t status; + + if (unlikely (device->base.finished)) + return CAIRO_STATUS_SUCCESS; + + status = cairo_device_acquire (&device->base); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = i915_batch_flush ((i915_device_t *) device); + cairo_device_release (&device->base); + } + + return status; +} + +static cairo_int_status_t +i915_device_throttle (cairo_drm_device_t *device) +{ + cairo_status_t status; + + status = cairo_device_acquire (&device->base); + if (unlikely (status)) + return status; + + status = i915_batch_flush ((i915_device_t *) device); + intel_throttle ((intel_device_t *) device); + + cairo_device_release (&device->base); + + return status; +} + +static void +i915_device_destroy (void *data) +{ + i915_device_t *device = data; + + if (device->last_vbo) + intel_bo_destroy (&device->intel, device->last_vbo); + + i915_batch_cleanup (device); + + intel_device_fini (&device->intel); + free (device); +} + +COMPILE_TIME_ASSERT (sizeof (i915_batch_setup) == sizeof (((i915_device_t *)0)->batch_header)); +COMPILE_TIME_ASSERT (offsetof (i915_device_t, batch_base) == offsetof (i915_device_t, batch_header) + sizeof (i915_batch_setup)); + +cairo_drm_device_t * +_cairo_drm_i915_device_create (int fd, dev_t dev_id, int vendor_id, int chip_id) +{ + i915_device_t *device; + cairo_status_t status; + uint64_t gtt_size; + int n; + + if (! intel_info (fd, >t_size)) + return NULL; + + device = _cairo_malloc (sizeof (i915_device_t)); + if (device == NULL) + return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + status = intel_device_init (&device->intel, fd); + if (unlikely (status)) { + free (device); + return (cairo_drm_device_t *) _cairo_device_create_in_error (status); + } + + device->debug = 0; + if (getenv ("CAIRO_DEBUG_DRM") != NULL) + device->debug = I915_DEBUG_SYNC; + + n = intel_get (fd, I915_PARAM_NUM_FENCES_AVAIL); + if (n == 0) + n = 8; + device->batch.fences_avail = n - 2; /* conservative */ + + device->batch.gtt_avail_size = device->intel.gtt_avail_size / 4; + device->batch.est_gtt_size = I915_BATCH_SIZE; + device->batch.total_gtt_size = I915_BATCH_SIZE; + device->batch.exec_count = 0; + device->batch.reloc_count = 0; + device->batch.used = 0; + device->batch.fences = 0; + + memcpy (device->batch_header, i915_batch_setup, sizeof (i915_batch_setup)); + device->vbo = 0; + device->vbo_offset = 0; + device->vbo_used = 0; + device->vertex_index = 0; + device->vertex_count = 0; + device->last_vbo = NULL; + + for (n = 0; n < ARRAY_LENGTH (device->image_caches); n++) + cairo_list_init (&device->image_caches[n]); + + device->intel.base.surface.create = i915_surface_create; + device->intel.base.surface.create_for_name = i915_surface_create_for_name; + device->intel.base.surface.create_from_cacheable_image = i915_surface_create_from_cacheable_image; + + device->intel.base.surface.flink = _cairo_drm_surface_flink; + device->intel.base.surface.enable_scan_out = i915_surface_enable_scan_out; + device->intel.base.surface.map_to_image = intel_surface_map_to_image; + + device->intel.base.device.flush = i915_device_flush; + device->intel.base.device.throttle = i915_device_throttle; + device->intel.base.device.destroy = i915_device_destroy; + + device->floats_per_vertex = 0; + device->current_source = NULL; + device->current_mask = NULL; + device->current_clip = NULL; + + i915_device_reset (device); + + return _cairo_drm_device_init (&device->intel.base, + fd, dev_id, vendor_id, chip_id, + 16*1024); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i965-glyphs.c b/gfx/cairo/cairo/src/drm/cairo-drm-i965-glyphs.c new file mode 100644 index 000000000000..1ef0a6f59e5e --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i965-glyphs.c @@ -0,0 +1,504 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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 + */ + +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-drm-i965-private.h" +#include "cairo-error-private.h" +#include "cairo-rtree-private.h" + +typedef struct _i965_glyphs i965_glyphs_t; + +typedef float * +(*i965_get_rectangle_func_t) (i965_glyphs_t *glyphs); + +struct _i965_glyphs { + i965_get_rectangle_func_t get_rectangle; + i965_shader_t shader; + + struct i965_vbo head, *tail; + + unsigned int vbo_offset; + float *vbo_base; +}; + +static float * +i965_glyphs_emit_rectangle (i965_glyphs_t *glyphs) +{ + return i965_add_rectangle (glyphs->shader.device); +} + +static float * +i965_glyphs_accumulate_rectangle (i965_glyphs_t *glyphs) +{ + float *vertices; + uint32_t size; + + size = glyphs->shader.device->rectangle_size; + if (unlikely (glyphs->vbo_offset + size > I965_VERTEX_SIZE)) { + struct i965_vbo *vbo; + + vbo = _cairo_malloc (sizeof (struct i965_vbo)); + if (unlikely (vbo == NULL)) { + /* throw error! */ + } + + glyphs->tail->next = vbo; + glyphs->tail = vbo; + + vbo->next = NULL; + vbo->bo = intel_bo_create (&glyphs->shader.device->intel, + I965_VERTEX_SIZE, I965_VERTEX_SIZE, + FALSE, I915_TILING_NONE, 0); + vbo->count = 0; + + glyphs->vbo_offset = 0; + glyphs->vbo_base = intel_bo_map (&glyphs->shader.device->intel, vbo->bo); + } + + vertices = glyphs->vbo_base + glyphs->vbo_offset; + glyphs->vbo_offset += size; + glyphs->tail->count += 3; + + return vertices; +} + +static void +i965_add_glyph_rectangle (i965_glyphs_t *glyphs, + int x1, int y1, + int x2, int y2, + intel_glyph_t *glyph) +{ + float *v; + + /* Each vertex is: + * 2 vertex coordinates + * 1 glyph texture coordinate + */ + + v = glyphs->get_rectangle (glyphs); + + /* bottom right */ + *v++ = x2; *v++ = y2; + *v++ = glyph->texcoord[0]; + + /* bottom left */ + *v++ = x1; *v++ = y2; + *v++ = glyph->texcoord[1]; + + /* top left */ + *v++ = x1; *v++ = y1; + *v++ = glyph->texcoord[2]; +} + +static cairo_status_t +i965_surface_mask_internal (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *source, + i965_surface_t *mask, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + i965_device_t *device; + i965_shader_t shader; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + + i965_shader_init (&shader, dst, op); + + status = i965_shader_acquire_pattern (&shader, &shader.source, + source, &extents->bounded); + if (unlikely (status)) + return status; + + shader.mask.type.vertex = VS_NONE; + shader.mask.type.fragment = FS_SURFACE; + shader.mask.base.content = mask->intel.drm.base.content; + shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST); + shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE); + + cairo_matrix_init_translate (&shader.mask.base.matrix, + -extents->bounded.x, + -extents->bounded.y); + cairo_matrix_scale (&shader.mask.base.matrix, + 1. / mask->intel.drm.width, + 1. / mask->intel.drm.height); + + shader.mask.base.bo = to_intel_bo (mask->intel.drm.bo); + shader.mask.base.format = mask->intel.drm.format; + shader.mask.base.width = mask->intel.drm.width; + shader.mask.base.height = mask->intel.drm.height; + shader.mask.base.stride = mask->intel.drm.stride; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&shader, clip); + } + + status = cairo_device_acquire (dst->intel.drm.base.device); + if (unlikely (status)) + goto CLEANUP_SHADER; + + device = i965_device (dst); + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) + goto CLEANUP_DEVICE; + + if (clip_region != NULL) { + unsigned int n, num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rectangles; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + } else { + i965_shader_add_rectangle (&shader, + extents->bounded.x, + extents->bounded.y, + extents->bounded.width, + extents->bounded.height); + } + + if (! extents->is_bounded) + status = i965_fixup_unbounded (dst, extents, clip); + + CLEANUP_DEVICE: + cairo_device_release (&device->intel.base.base); + CLEANUP_SHADER: + i965_shader_fini (&shader); + return status; +} + +cairo_int_status_t +i965_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *g, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip, + int *num_remaining) +{ + i965_surface_t *surface = abstract_surface; + i965_surface_t *mask = NULL; + i965_device_t *device; + i965_glyphs_t glyphs; + cairo_composite_rectangles_t extents; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_bool_t overlap; + cairo_region_t *clip_region = NULL; + intel_bo_t *last_bo = NULL; + cairo_scaled_glyph_t *glyph_cache[64]; + cairo_status_t status; + int mask_x = 0, mask_y = 0; + int i = 0; + + *num_remaining = 0; + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + surface->intel.drm.width, + surface->intel.drm.height, + op, source, + scaled_font, + g, num_glyphs, + clip, + &overlap); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_rectangle (clip, &extents.mask)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) + return status; + + have_clip = TRUE; + } + + if (overlap || ! extents.is_bounded) { + cairo_format_t format; + + format = CAIRO_FORMAT_A8; + if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) + format = CAIRO_FORMAT_ARGB32; + + mask = (i965_surface_t *) + i965_surface_create_internal (&i965_device (surface)->intel.base, + format, + extents.bounded.width, + extents.bounded.height, + I965_TILING_DEFAULT, + TRUE); + if (unlikely (mask->intel.drm.base.status)) + return mask->intel.drm.base.status; + + status = _cairo_surface_paint (&mask->intel.drm.base, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (&mask->intel.drm.base); + return status; + } + + i965_shader_init (&glyphs.shader, mask, CAIRO_OPERATOR_ADD); + + status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source, + &_cairo_pattern_white.base, + &extents.bounded); + if (unlikely (status)) { + cairo_surface_destroy (&mask->intel.drm.base); + return status; + } + + mask_x = -extents.bounded.x; + mask_y = -extents.bounded.y; + } else { + i965_shader_init (&glyphs.shader, surface, op); + + status = i965_shader_acquire_pattern (&glyphs.shader, &glyphs.shader.source, + source, &extents.bounded); + if (unlikely (status)) + return status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&glyphs.shader, clip); + } + } + + glyphs.head.next = NULL; + glyphs.head.bo = NULL; + glyphs.head.count = 0; + glyphs.tail = &glyphs.head; + + device = i965_device (surface); + if (mask != NULL || clip_region == NULL) { + glyphs.get_rectangle = i965_glyphs_emit_rectangle; + } else { + glyphs.get_rectangle = i965_glyphs_accumulate_rectangle; + glyphs.head.bo = intel_bo_create (&device->intel, + I965_VERTEX_SIZE, I965_VERTEX_SIZE, + FALSE, I915_TILING_NONE, 0); + if (unlikely (glyphs.head.bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + glyphs.vbo_base = intel_bo_map (&device->intel, glyphs.head.bo); + } + glyphs.vbo_offset = 0; + + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto CLEANUP_GLYPHS; + + _cairo_scaled_font_freeze_cache (scaled_font); + //private = _cairo_scaled_font_get_device (scaled_font, device); + if (scaled_font->surface_private == NULL) { + scaled_font->surface_private = device; + scaled_font->surface_backend = surface->intel.drm.base.backend; + cairo_list_add (&scaled_font->link, &device->intel.fonts); + } + + memset (glyph_cache, 0, sizeof (glyph_cache)); + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + int x, y, x1, x2, y1, y2; + int cache_index = g[i].index % ARRAY_LENGTH (glyph_cache); + intel_glyph_t *glyph; + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != g[i].index) + { + status = _cairo_scaled_glyph_lookup (scaled_font, + g[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + goto FINISH; + + glyph_cache[cache_index] = scaled_glyph; + } + + if (unlikely (scaled_glyph->metrics.width == 0 || + scaled_glyph->metrics.height == 0)) + { + continue; + } + + /* XXX glyph images are snapped to pixel locations */ + x = _cairo_lround (g[i].x); + y = _cairo_lround (g[i].y); + + x1 = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + + if (x2 < extents.bounded.x || + y2 < extents.bounded.y || + x1 > extents.bounded.x + extents.bounded.width || + y1 > extents.bounded.y + extents.bounded.height) + { + continue; + } + + if (scaled_glyph->surface_private == NULL) { + status = intel_get_glyph (&device->intel, scaled_font, scaled_glyph); + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) { + status = CAIRO_STATUS_SUCCESS; + continue; + } + if (unlikely (status)) + goto FINISH; + } + glyph = intel_glyph_pin (scaled_glyph->surface_private); + + if (glyph->cache->buffer.bo != last_bo) { + intel_buffer_cache_t *cache = glyph->cache; + + glyphs.shader.mask.type.vertex = VS_GLYPHS; + glyphs.shader.mask.type.fragment = FS_GLYPHS; + glyphs.shader.mask.type.pattern = PATTERN_BASE; + + glyphs.shader.mask.base.bo = cache->buffer.bo; + glyphs.shader.mask.base.format = cache->buffer.format; + glyphs.shader.mask.base.width = cache->buffer.width; + glyphs.shader.mask.base.height = cache->buffer.height; + glyphs.shader.mask.base.stride = cache->buffer.stride; + glyphs.shader.mask.base.filter = i965_filter (CAIRO_FILTER_NEAREST); + glyphs.shader.mask.base.extend = i965_extend (CAIRO_EXTEND_NONE); + glyphs.shader.mask.base.content = CAIRO_CONTENT_ALPHA; /* XXX */ + + glyphs.shader.committed = FALSE; + status = i965_shader_commit (&glyphs.shader, device); + if (unlikely (status)) + goto FINISH; + + last_bo = cache->buffer.bo; + } + + x2 = x1 + glyph->width; + y2 = y1 + glyph->height; + + if (mask_x) + x1 += mask_x, x2 += mask_x; + if (mask_y) + y1 += mask_y, y2 += mask_y; + + i965_add_glyph_rectangle (&glyphs, x1, y1, x2, y2, glyph); + } + + if (mask != NULL && clip_region != NULL) + i965_clipped_vertices (device, &glyphs.head, clip_region); + + status = CAIRO_STATUS_SUCCESS; + FINISH: + _cairo_scaled_font_thaw_cache (scaled_font); + cairo_device_release (surface->intel.drm.base.device); + CLEANUP_GLYPHS: + i965_shader_fini (&glyphs.shader); + + if (glyphs.head.bo != NULL) { + struct i965_vbo *vbo, *next; + + intel_bo_destroy (&device->intel, glyphs.head.bo); + for (vbo = glyphs.head.next; vbo != NULL; vbo = next) { + next = vbo->next; + intel_bo_destroy (&device->intel, vbo->bo); + free (vbo); + } + } + + if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { + cairo_path_fixed_t path; + + _cairo_path_fixed_init (&path); + status = _cairo_scaled_font_glyph_path (scaled_font, + g + i, num_glyphs - i, + &path); + if (mask_x | mask_y) { + _cairo_path_fixed_translate (&path, + _cairo_fixed_from_int (mask_x), + _cairo_fixed_from_int (mask_y)); + } + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = surface->intel.drm.base.backend->fill (glyphs.shader.target, + glyphs.shader.op, + mask != NULL ? &_cairo_pattern_white.base : source, + &path, + CAIRO_FILL_RULE_WINDING, + 0, + scaled_font->options.antialias, + clip); + } + _cairo_path_fixed_fini (&path); + } + + if (mask != NULL) { + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = i965_surface_mask_internal (surface, op, source, mask, + clip, &extents); + } + cairo_surface_finish (&mask->intel.drm.base); + cairo_surface_destroy (&mask->intel.drm.base); + } + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i965-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-i965-private.h new file mode 100644 index 000000000000..79568a63d7fb --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i965-private.h @@ -0,0 +1,737 @@ +#ifndef CAIRO_DRM_I965_PRIVATE_H +#define CAIRO_DRM_I965_PRIVATE_H + +#include "cairo-drm-intel-private.h" + +#include "cairo-hash-private.h" +#include "cairo-freelist-private.h" + +#include "cairo-drm-intel-brw-defines.h" + +#include + +#define BRW_MI_GLOBAL_SNAPSHOT_RESET (1 << 3) + +/* + * New regs for broadwater -- we need to split this file up sensibly somehow. + */ +#define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \ + ((Pipeline) << 27) | \ + ((Opcode) << 24) | \ + ((Subopcode) << 16)) + +#define BRW_URB_FENCE BRW_3D(0, 0, 0) +#define BRW_CS_URB_STATE BRW_3D(0, 0, 1) +#define BRW_CONSTANT_BUFFER BRW_3D(0, 0, 2) +#define BRW_STATE_PREFETCH BRW_3D(0, 0, 3) + +#define BRW_STATE_BASE_ADDRESS BRW_3D(0, 1, 1) +#define BRW_STATE_SIP BRW_3D(0, 1, 2) +#define BRW_PIPELINE_SELECT BRW_3D(0, 1, 4) + +#define NEW_PIPELINE_SELECT BRW_3D(1, 1, 4) + +#define BRW_MEDIA_STATE_POINTERS BRW_3D(2, 0, 0) +#define BRW_MEDIA_OBJECT BRW_3D(2, 1, 0) + +#define BRW_3DSTATE_PIPELINED_POINTERS BRW_3D(3, 0, 0) +#define BRW_3DSTATE_BINDING_TABLE_POINTERS BRW_3D(3, 0, 1) +#define BRW_3DSTATE_VERTEX_BUFFERS BRW_3D(3, 0, 8) +#define BRW_3DSTATE_VERTEX_ELEMENTS BRW_3D(3, 0, 9) +#define BRW_3DSTATE_INDEX_BUFFER BRW_3D(3, 0, 0xa) +#define BRW_3DSTATE_VF_STATISTICS BRW_3D(3, 0, 0xb) + +#define BRW_3DSTATE_DRAWING_RECTANGLE BRW_3D(3, 1, 0) +#define BRW_3DSTATE_CONSTANT_COLOR BRW_3D(3, 1, 1) +#define BRW_3DSTATE_SAMPLER_PALETTE_LOAD BRW_3D(3, 1, 2) +#define BRW_3DSTATE_CHROMA_KEY BRW_3D(3, 1, 4) +#define BRW_3DSTATE_DEPTH_BUFFER BRW_3D(3, 1, 5) +#define BRW_3DSTATE_POLY_STIPPLE_OFFSET BRW_3D(3, 1, 6) +#define BRW_3DSTATE_POLY_STIPPLE_PATTERN BRW_3D(3, 1, 7) +#define BRW_3DSTATE_LINE_STIPPLE BRW_3D(3, 1, 8) +#define BRW_3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP BRW_3D(3, 1, 9) +/* These two are BLC and CTG only, not BW or CL */ +#define BRW_3DSTATE_AA_LINE_PARAMS BRW_3D(3, 1, 0xa) +#define BRW_3DSTATE_GS_SVB_INDEX BRW_3D(3, 1, 0xb) + +#define BRW_PIPE_CONTROL BRW_3D(3, 2, 0) + +#define BRW_3DPRIMITIVE BRW_3D(3, 3, 0) + +#define PIPELINE_SELECT_3D 0 +#define PIPELINE_SELECT_MEDIA 1 + +#define UF0_CS_REALLOC (1 << 13) +#define UF0_VFE_REALLOC (1 << 12) +#define UF0_SF_REALLOC (1 << 11) +#define UF0_CLIP_REALLOC (1 << 10) +#define UF0_GS_REALLOC (1 << 9) +#define UF0_VS_REALLOC (1 << 8) +#define UF1_CLIP_FENCE_SHIFT 20 +#define UF1_GS_FENCE_SHIFT 10 +#define UF1_VS_FENCE_SHIFT 0 +#define UF2_CS_FENCE_SHIFT 20 +#define UF2_VFE_FENCE_SHIFT 10 +#define UF2_SF_FENCE_SHIFT 0 + +/* for BRW_STATE_BASE_ADDRESS */ +#define BASE_ADDRESS_MODIFY (1 << 0) + +/* for BRW_3DSTATE_PIPELINED_POINTERS */ +#define BRW_GS_DISABLE 0 +#define BRW_GS_ENABLE 1 +#define BRW_CLIP_DISABLE 0 +#define BRW_CLIP_ENABLE 1 + +/* for BRW_PIPE_CONTROL */ +#define BRW_PIPE_CONTROL_NOWRITE (0 << 14) +#define BRW_PIPE_CONTROL_WRITE_QWORD (1 << 14) +#define BRW_PIPE_CONTROL_WRITE_DEPTH (2 << 14) +#define BRW_PIPE_CONTROL_WRITE_TIME (3 << 14) +#define BRW_PIPE_CONTROL_DEPTH_STALL (1 << 13) +#define BRW_PIPE_CONTROL_WC_FLUSH (1 << 12) +#define BRW_PIPE_CONTROL_IS_FLUSH (1 << 11) +#define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8) +#define BRW_PIPE_CONTROL_GLOBAL_GTT (1 << 2) +#define BRW_PIPE_CONTROL_LOCAL_PGTT (0 << 2) + +/* VERTEX_BUFFER_STATE Structure */ +#define VB0_BUFFER_INDEX_SHIFT 27 +#define VB0_VERTEXDATA (0 << 26) +#define VB0_INSTANCEDATA (1 << 26) +#define VB0_BUFFER_PITCH_SHIFT 0 + +/* VERTEX_ELEMENT_STATE Structure */ +#define VE0_VERTEX_BUFFER_INDEX_SHIFT 27 +#define VE0_VALID (1 << 26) +#define VE0_FORMAT_SHIFT 16 +#define VE0_OFFSET_SHIFT 0 +#define VE1_VFCOMPONENT_0_SHIFT 28 +#define VE1_VFCOMPONENT_1_SHIFT 24 +#define VE1_VFCOMPONENT_2_SHIFT 20 +#define VE1_VFCOMPONENT_3_SHIFT 16 +#define VE1_DESTINATION_ELEMENT_OFFSET_SHIFT 0 + +/* 3DPRIMITIVE bits */ +#define BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL (0 << 15) +#define BRW_3DPRIMITIVE_VERTEX_RANDOM (1 << 15) +/* Primitive types are in brw_defines.h */ +#define BRW_3DPRIMITIVE_TOPOLOGY_SHIFT 10 + +#define BRW_SVG_CTL 0x7400 + +#define BRW_SVG_CTL_GS_BA (0 << 8) +#define BRW_SVG_CTL_SS_BA (1 << 8) +#define BRW_SVG_CTL_IO_BA (2 << 8) +#define BRW_SVG_CTL_GS_AUB (3 << 8) +#define BRW_SVG_CTL_IO_AUB (4 << 8) +#define BRW_SVG_CTL_SIP (5 << 8) + +#define BRW_SVG_RDATA 0x7404 +#define BRW_SVG_WORK_CTL 0x7408 + +#define BRW_VF_CTL 0x7500 + +#define BRW_VF_CTL_SNAPSHOT_COMPLETE (1 << 31) +#define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_THREADID (0 << 8) +#define BRW_VF_CTL_SNAPSHOT_MUX_SELECT_VF_DEBUG (1 << 8) +#define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_SEQUENCE (0 << 4) +#define BRW_VF_CTL_SNAPSHOT_TYPE_VERTEX_INDEX (1 << 4) +#define BRW_VF_CTL_SKIP_INITIAL_PRIMITIVES (1 << 3) +#define BRW_VF_CTL_MAX_PRIMITIVES_LIMIT_ENABLE (1 << 2) +#define BRW_VF_CTL_VERTEX_RANGE_LIMIT_ENABLE (1 << 1) +#define BRW_VF_CTL_SNAPSHOT_ENABLE (1 << 0) + +#define BRW_VF_STRG_VAL 0x7504 +#define BRW_VF_STR_VL_OVR 0x7508 +#define BRW_VF_VC_OVR 0x750c +#define BRW_VF_STR_PSKIP 0x7510 +#define BRW_VF_MAX_PRIM 0x7514 +#define BRW_VF_RDATA 0x7518 + +#define BRW_VS_CTL 0x7600 +#define BRW_VS_CTL_SNAPSHOT_COMPLETE (1 << 31) +#define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_0 (0 << 8) +#define BRW_VS_CTL_SNAPSHOT_MUX_VERTEX_1 (1 << 8) +#define BRW_VS_CTL_SNAPSHOT_MUX_VALID_COUNT (2 << 8) +#define BRW_VS_CTL_SNAPSHOT_MUX_VS_KERNEL_POINTER (3 << 8) +#define BRW_VS_CTL_SNAPSHOT_ALL_THREADS (1 << 2) +#define BRW_VS_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1) +#define BRW_VS_CTL_SNAPSHOT_ENABLE (1 << 0) + +#define BRW_VS_STRG_VAL 0x7604 +#define BRW_VS_RDATA 0x7608 + +#define BRW_SF_CTL 0x7b00 +#define BRW_SF_CTL_SNAPSHOT_COMPLETE (1 << 31) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_FF_ID (0 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_0_REL_COUNT (1 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_FF_ID (2 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_1_REL_COUNT (3 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_FF_ID (4 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_2_REL_COUNT (5 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_VERTEX_COUNT (6 << 8) +#define BRW_SF_CTL_SNAPSHOT_MUX_SF_KERNEL_POINTER (7 << 8) +#define BRW_SF_CTL_MIN_MAX_PRIMITIVE_RANGE_ENABLE (1 << 4) +#define BRW_SF_CTL_DEBUG_CLIP_RECTANGLE_ENABLE (1 << 3) +#define BRW_SF_CTL_SNAPSHOT_ALL_THREADS (1 << 2) +#define BRW_SF_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1) +#define BRW_SF_CTL_SNAPSHOT_ENABLE (1 << 0) + +#define BRW_SF_STRG_VAL 0x7b04 +#define BRW_SF_RDATA 0x7b18 + +#define BRW_WIZ_CTL 0x7c00 +#define BRW_WIZ_CTL_SNAPSHOT_COMPLETE (1 << 31) +#define BRW_WIZ_CTL_SUBSPAN_INSTANCE_SHIFT 16 +#define BRW_WIZ_CTL_SNAPSHOT_MUX_WIZ_KERNEL_POINTER (0 << 8) +#define BRW_WIZ_CTL_SNAPSHOT_MUX_SUBSPAN_INSTANCE (1 << 8) +#define BRW_WIZ_CTL_SNAPSHOT_MUX_PRIMITIVE_SEQUENCE (2 << 8) +#define BRW_WIZ_CTL_SINGLE_SUBSPAN_DISPATCH (1 << 6) +#define BRW_WIZ_CTL_IGNORE_COLOR_SCOREBOARD_STALLS (1 << 5) +#define BRW_WIZ_CTL_ENABLE_SUBSPAN_INSTANCE_COMPARE (1 << 4) +#define BRW_WIZ_CTL_USE_UPSTREAM_SNAPSHOT_FLAG (1 << 3) +#define BRW_WIZ_CTL_SNAPSHOT_ALL_THREADS (1 << 2) +#define BRW_WIZ_CTL_THREAD_SNAPSHOT_ENABLE (1 << 1) +#define BRW_WIZ_CTL_SNAPSHOT_ENABLE (1 << 0) + +#define BRW_WIZ_STRG_VAL 0x7c04 +#define BRW_WIZ_RDATA 0x7c18 + +#define BRW_TS_CTL 0x7e00 +#define BRW_TS_CTL_SNAPSHOT_COMPLETE (1 << 31) +#define BRW_TS_CTL_SNAPSHOT_MESSAGE_ERROR (0 << 8) +#define BRW_TS_CTL_SNAPSHOT_INTERFACE_DESCRIPTOR (3 << 8) +#define BRW_TS_CTL_SNAPSHOT_ALL_CHILD_THREADS (1 << 2) +#define BRW_TS_CTL_SNAPSHOT_ALL_ROOT_THREADS (1 << 1) +#define BRW_TS_CTL_SNAPSHOT_ENABLE (1 << 0) + +#define BRW_TS_STRG_VAL 0x7e04 +#define BRW_TS_RDATA 0x7e08 + +#define BRW_TD_CTL 0x8000 +#define BRW_TD_CTL_MUX_SHIFT 8 +#define BRW_TD_CTL_EXTERNAL_HALT_R0_DEBUG_MATCH (1 << 7) +#define BRW_TD_CTL_FORCE_EXTERNAL_HALT (1 << 6) +#define BRW_TD_CTL_EXCEPTION_MASK_OVERRIDE (1 << 5) +#define BRW_TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE (1 << 4) +#define BRW_TD_CTL_BREAKPOINT_ENABLE (1 << 2) +#define BRW_TD_CTL2 0x8004 +#define BRW_TD_CTL2_ILLEGAL_OPCODE_EXCEPTION_OVERRIDE (1 << 28) +#define BRW_TD_CTL2_MASKSTACK_EXCEPTION_OVERRIDE (1 << 26) +#define BRW_TD_CTL2_SOFTWARE_EXCEPTION_OVERRIDE (1 << 25) +#define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_SHIFT 16 +#define BRW_TD_CTL2_ACTIVE_THREAD_LIMIT_ENABLE (1 << 8) +#define BRW_TD_CTL2_THREAD_SPAWNER_EXECUTION_MASK_ENABLE (1 << 7) +#define BRW_TD_CTL2_WIZ_EXECUTION_MASK_ENABLE (1 << 6) +#define BRW_TD_CTL2_SF_EXECUTION_MASK_ENABLE (1 << 5) +#define BRW_TD_CTL2_CLIPPER_EXECUTION_MASK_ENABLE (1 << 4) +#define BRW_TD_CTL2_GS_EXECUTION_MASK_ENABLE (1 << 3) +#define BRW_TD_CTL2_VS_EXECUTION_MASK_ENABLE (1 << 0) +#define BRW_TD_VF_VS_EMSK 0x8008 +#define BRW_TD_GS_EMSK 0x800c +#define BRW_TD_CLIP_EMSK 0x8010 +#define BRW_TD_SF_EMSK 0x8014 +#define BRW_TD_WIZ_EMSK 0x8018 +#define BRW_TD_0_6_EHTRG_VAL 0x801c +#define BRW_TD_0_7_EHTRG_VAL 0x8020 +#define BRW_TD_0_6_EHTRG_MSK 0x8024 +#define BRW_TD_0_7_EHTRG_MSK 0x8028 +#define BRW_TD_RDATA 0x802c +#define BRW_TD_TS_EMSK 0x8030 + +#define BRW_EU_CTL 0x8800 +#define BRW_EU_CTL_SELECT_SHIFT 16 +#define BRW_EU_CTL_DATA_MUX_SHIFT 8 +#define BRW_EU_ATT_0 0x8810 +#define BRW_EU_ATT_1 0x8814 +#define BRW_EU_ATT_DATA_0 0x8820 +#define BRW_EU_ATT_DATA_1 0x8824 +#define BRW_EU_ATT_CLR_0 0x8830 +#define BRW_EU_ATT_CLR_1 0x8834 +#define BRW_EU_RDATA 0x8840 + +typedef struct i965_device i965_device_t; +typedef struct i965_surface i965_surface_t; +typedef struct i965_shader i965_shader_t; +typedef struct i965_stream i965_stream_t; + +struct i965_sf_state { + cairo_hash_entry_t entry; + uint32_t offset; +}; + +cairo_private cairo_bool_t +i965_sf_state_equal (const void *, const void *); + +struct i965_cc_state { + cairo_hash_entry_t entry; + uint32_t offset; +}; + +cairo_private cairo_bool_t +i965_cc_state_equal (const void *, const void *); + +struct i965_wm_kernel { + cairo_hash_entry_t entry; + uint32_t offset; +}; + +struct i965_wm_state { + cairo_hash_entry_t entry; + uint32_t kernel; + uint32_t sampler; + uint32_t offset; +}; + +cairo_private cairo_bool_t +i965_wm_state_equal (const void *, const void *); + +struct i965_wm_binding { + cairo_hash_entry_t entry; + uint32_t table[4]; + int size; + uint32_t offset; +}; + +cairo_private cairo_bool_t +i965_wm_binding_equal (const void *, const void *); + +struct i965_sampler { + cairo_hash_entry_t entry; + uint32_t offset; +}; + +struct i965_vbo { + struct i965_vbo *next; + intel_bo_t *bo; + unsigned int count; +}; + +struct i965_surface { + intel_surface_t intel; + + uint32_t stream; + uint32_t offset; +}; + +struct i965_pending_relocation { + uint32_t offset; + uint32_t read_domains; + uint32_t write_domain; + uint32_t delta; +}; + +struct i965_stream { + uint32_t used; + uint32_t committed; + uint32_t size; + uint8_t *data; + uint32_t serial; + + int num_pending_relocations; + int max_pending_relocations; + struct i965_pending_relocation *pending_relocations; + + int num_relocations; + int max_relocations; + struct drm_i915_gem_relocation_entry *relocations; +}; + +#define I965_BATCH_SIZE (16 * 4096) +#define I965_GENERAL_SIZE (16 * 4096) +#define I965_SURFACE_SIZE (32 * 4096) +#define I965_VERTEX_SIZE (128 * 4096) + +#define I965_TILING_DEFAULT I915_TILING_Y + + +struct i965_device { + intel_device_t intel; + + cairo_bool_t is_g4x; + + i965_shader_t *shader; /* note: only valid during geometry emission */ + + /* track state changes */ + struct i965_sf_state sf_state; + struct i965_cc_state cc_state; + struct i965_wm_state wm_state; + struct i965_wm_binding wm_binding; + + i965_surface_t *target; + uint32_t target_offset; + + intel_bo_t *source; + uint32_t source_offset; + + intel_bo_t *mask; + uint32_t mask_offset; + + intel_bo_t *clip; + uint32_t clip_offset; + + uint32_t draw_rectangle; + + uint32_t vs_offset; + uint32_t border_color_offset; + cairo_hash_table_t *sf_states; + cairo_hash_table_t *cc_states; + cairo_hash_table_t *wm_kernels; + cairo_hash_table_t *wm_states; + cairo_hash_table_t *wm_bindings; + cairo_hash_table_t *samplers; + intel_bo_t *general_state; + + cairo_freelist_t sf_freelist; + cairo_freelist_t cc_freelist; + cairo_freelist_t wm_kernel_freelist; + cairo_freelist_t wm_state_freelist; + cairo_freelist_t wm_binding_freelist; + cairo_freelist_t sampler_freelist; + + uint32_t vertex_type; + uint32_t vertex_size; + uint32_t rectangle_size; + uint32_t last_vertex_size; + + float *constants; /* 4 x matrix + 2 x source */ + unsigned constants_size; + cairo_bool_t have_urb_fences; + + i965_stream_t batch; + uint8_t batch_base[I965_BATCH_SIZE]; + struct drm_i915_gem_relocation_entry batch_relocations[2048]; + + i965_stream_t surface; + uint8_t surface_base[I965_SURFACE_SIZE]; + struct i965_pending_relocation surface_pending_relocations[1]; + struct drm_i915_gem_relocation_entry surface_relocations[1024]; + + i965_stream_t general; + uint8_t general_base[I965_GENERAL_SIZE]; + struct i965_pending_relocation general_pending_relocations[1]; + + i965_stream_t vertex; + uint8_t vertex_base[I965_VERTEX_SIZE]; + struct i965_pending_relocation vertex_pending_relocations[512]; + + struct { + size_t gtt_size; + + intel_bo_t *bo[1024]; + int count; + + struct drm_i915_gem_exec_object2 exec[1024]; + } exec; + cairo_list_t flush; +}; + +typedef enum { + VS_NONE = 0, + VS_GLYPHS, + VS_SPANS, +} i965_vertex_shader_t; + +typedef enum { + FS_NONE = 0, + FS_CONSTANT, + FS_LINEAR, + FS_RADIAL, + FS_SURFACE, + FS_GLYPHS, + FS_SPANS, +} i965_fragment_shader_t; + +typedef enum { + PATTERN_BASE, + PATTERN_SOLID, + PATTERN_LINEAR, + PATTERN_RADIAL, + PATTERN_SURFACE, +} i965_shader_channel_t; +#define PATTERN_NONE (i965_shader_channel_t)-1 + +struct i965_shader { + i965_device_t *device; + i965_surface_t *target; + + cairo_operator_t op; + + cairo_bool_t committed; + cairo_bool_t need_combine; + + float constants[4*8 + 2*8]; /* 4 x matrix + 2 x source */ + unsigned constants_size; + + union i965_shader_channel { + struct { + i965_vertex_shader_t vertex; + i965_fragment_shader_t fragment; + i965_shader_channel_t pattern; + } type; + struct i965_shader_base { + i965_vertex_shader_t vertex; + i965_fragment_shader_t fragment; + i965_shader_channel_t pattern; + + uint32_t mode; + + float constants[8]; + unsigned constants_size; + + intel_bo_t *bo; + cairo_format_t format; + cairo_content_t content; + int width, height, stride; + int filter, extend; + cairo_matrix_t matrix; + cairo_bool_t has_component_alpha; + } base; + struct i965_shader_solid { + struct i965_shader_base base; + } solid; + struct i965_shader_linear { + struct i965_shader_base base; + } linear; + struct i965_shader_radial { + struct i965_shader_base base; + } radial; + struct i965_shader_surface { + struct i965_shader_base base; + cairo_surface_t *surface; + } surface; + } source, mask, clip, dst; + + jmp_buf unwind; +}; + +enum i965_shader_linear_mode { + /* XXX REFLECT */ + LINEAR_TEXTURE, + LINEAR_NONE, + LINEAR_REPEAT, + LINEAR_PAD, +}; + +enum i965_shader_radial_mode { + RADIAL_ONE, + RADIAL_TWO +}; + +typedef cairo_status_t +(*i965_spans_func_t) (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents); + +static inline i965_device_t * +i965_device (i965_surface_t *surface) +{ + return (i965_device_t *) surface->intel.drm.base.device; +} + +cairo_private void +i965_emit_relocation (i965_device_t *device, + i965_stream_t *stream, + intel_bo_t *target, + uint32_t target_offset, + uint32_t read_domains, + uint32_t write_domain, + uint32_t offset); + +static cairo_always_inline uint32_t +i965_stream_emit (i965_stream_t *stream, const void *data, size_t size) +{ + uint32_t offset; + + offset = stream->used; + assert (offset + size <= stream->size); + memcpy (stream->data + offset, data, size); + stream->used += size; + + return offset; +} + +static cairo_always_inline void +i965_stream_align (i965_stream_t *stream, uint32_t size) +{ + stream->used = (stream->used + size - 1) & -size; +} + +static cairo_always_inline void * +i965_stream_alloc (i965_stream_t *stream, uint32_t align, uint32_t size) +{ + void *ptr; + + if (align) + i965_stream_align (stream, align); + + assert (stream->used + size <= stream->size); + ptr = stream->data + stream->used; + stream->used += size; + + return ptr; +} + +static cairo_always_inline uint32_t +i965_stream_offsetof (i965_stream_t *stream, const void *ptr) +{ + return (char *) ptr - (char *) stream->data; +} + +cairo_private void +i965_stream_commit (i965_device_t *device, + i965_stream_t *stream); + +cairo_private void +i965_general_state_reset (i965_device_t *device); + +static inline void +i965_batch_emit_dword (i965_device_t *device, uint32_t dword) +{ + *(uint32_t *) (device->batch.data + device->batch.used) = dword; + device->batch.used += 4; +} + +#define OUT_BATCH(dword) i965_batch_emit_dword(device, dword) + +cairo_private void +i965_clipped_vertices (i965_device_t *device, + struct i965_vbo *vbo, + cairo_region_t *clip_region); + +cairo_private void +i965_flush_vertices (i965_device_t *device); + +cairo_private void +i965_finish_vertices (i965_device_t *device); + +static inline float * +i965_add_rectangle (i965_device_t *device) +{ + float *vertices; + uint32_t size; + + size = device->rectangle_size; + if (unlikely (device->vertex.used + size > device->vertex.size)) + i965_finish_vertices (device); + + vertices = (float *) (device->vertex.data + device->vertex.used); + device->vertex.used += size; + + return vertices; +} + +static inline void +i965_shader_add_rectangle (const i965_shader_t *shader, + int x, int y, + int w, int h) +{ + float *v; + + v= i965_add_rectangle (shader->device); + + /* bottom-right */ + *v++ = x + w; + *v++ = y + h; + + /* bottom-left */ + *v++ = x; + *v++ = y + h; + + /* top-left */ + *v++ = x; + *v++ = y; +} + +cairo_private cairo_surface_t * +i965_surface_create_internal (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height, + uint32_t tiling, + cairo_bool_t gpu_target); + +cairo_private cairo_status_t +i965_clip_and_composite_spans (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + i965_spans_func_t draw_func, + void *draw_closure, + const cairo_composite_rectangles_t*extents, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +i965_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, + cairo_clip_t *clip, + int *num_remaining); + +cairo_private void +i965_shader_init (i965_shader_t *shader, + i965_surface_t *dst, + cairo_operator_t op); + +cairo_private cairo_status_t +i965_shader_acquire_pattern (i965_shader_t *shader, + union i965_shader_channel *src, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents); + +cairo_private void +i965_shader_set_clip (i965_shader_t *shader, + cairo_clip_t *clip); + +cairo_private cairo_status_t +i965_shader_commit (i965_shader_t *shader, + i965_device_t *device); + +cairo_private void +i965_shader_fini (i965_shader_t *shader); + +cairo_private cairo_status_t +i965_device_flush (i965_device_t *device); + +cairo_private cairo_status_t +i965_fixup_unbounded (i965_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip); + +static inline int +i965_filter (cairo_filter_t filter) +{ + switch (filter) { + default: + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return BRW_MAPFILTER_NEAREST; + + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + return BRW_MAPFILTER_LINEAR; + } +} + +static inline int +i965_extend (cairo_extend_t extend) +{ + switch (extend) { + default: + case CAIRO_EXTEND_NONE: + return BRW_TEXCOORDMODE_CLAMP_BORDER; + case CAIRO_EXTEND_REPEAT: + return BRW_TEXCOORDMODE_WRAP; + case CAIRO_EXTEND_PAD: + return BRW_TEXCOORDMODE_CLAMP; + case CAIRO_EXTEND_REFLECT: + return BRW_TEXCOORDMODE_MIRROR; + } +} + +#endif /* CAIRO_DRM_I965_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i965-shader.c b/gfx/cairo/cairo/src/drm/cairo-drm-i965-shader.c new file mode 100644 index 000000000000..8fa3b4bd2ca9 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i965-shader.c @@ -0,0 +1,2825 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Kristian Høgsberg + * Copyright © 2009 Chris Wilson + * Copyright © 2009 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. + * + * Contributor(s): + * Chris Wilson + * Kristian Høgsberg + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-drm-i965-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-snapshot-private.h" + +#include "cairo-drm-intel-brw-eu.h" + +/* Theory of shaders: + * + * 3 types of rectangular inputs: + * (a) standard composite: x,y, use source, mask matrices to compute texcoords + * (b) spans: x,y, alpha, use source matrix + * (c) glyphs: x,y, s,t, use source matrix + * + * 5 types of pixel shaders: + * (a) Solid colour + * (b) Linear gradient (via 1D texture, with precomputed tex) + * (c) Radial gradient (per-pixel s computation, 1D texture) + * (d) Spans (mask only): apply opacity + * (e) Texture (includes glyphs). + * + * Clip masks are limited to 2D textures only. + */ + +/* XXX dual source blending for LERP + ComponentAlpha!!! */ + +#define BRW_GRF_BLOCKS(nreg) ((nreg + 15) / 16 - 1) + +#define SF_KERNEL_NUM_GRF 1 +#define SF_MAX_THREADS 24 + +#define PS_MAX_THREADS_CTG 50 +#define PS_MAX_THREADS_BRW 32 + +#define URB_CS_ENTRY_SIZE 3 /* We need 4 matrices + 2 sources */ +#define URB_CS_ENTRIES 4 /* 4x sets of CONSTANT_BUFFER */ + +#define URB_VS_ENTRY_SIZE 1 +#define URB_VS_ENTRIES 8 + +#define URB_GS_ENTRY_SIZE 0 +#define URB_GS_ENTRIES 0 + +#define URB_CLIP_ENTRY_SIZE 0 +#define URB_CLIP_ENTRIES 0 + +#define URB_SF_ENTRY_SIZE 1 +#define URB_SF_ENTRIES (SF_MAX_THREADS + 1) + +static void +i965_pipelined_flush (i965_device_t *device) +{ + intel_bo_t *bo, *next; + + if (device->batch.used == 0) + return; + + OUT_BATCH (BRW_PIPE_CONTROL | + BRW_PIPE_CONTROL_NOWRITE | + BRW_PIPE_CONTROL_WC_FLUSH | + 2); + OUT_BATCH(0); /* Destination address */ + OUT_BATCH(0); /* Immediate data low DW */ + OUT_BATCH(0); /* Immediate data high DW */ + + cairo_list_foreach_entry_safe (bo, next, intel_bo_t, &device->flush, link) { + bo->batch_write_domain = 0; + cairo_list_init (&bo->link); + } + cairo_list_init (&device->flush); +} + +static cairo_status_t +i965_shader_acquire_solid (i965_shader_t *shader, + union i965_shader_channel *src, + const cairo_solid_pattern_t *solid, + const cairo_rectangle_int_t *extents) +{ + src->type.fragment = FS_CONSTANT; + src->type.vertex = VS_NONE; + src->type.pattern = PATTERN_SOLID; + + src->base.content = _cairo_color_get_content (&solid->color); + src->base.constants[0] = solid->color.red * solid->color.alpha; + src->base.constants[1] = solid->color.green * solid->color.alpha; + src->base.constants[2] = solid->color.blue * solid->color.alpha; + src->base.constants[3] = solid->color.alpha; + src->base.constants_size = 4; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_shader_acquire_linear (i965_shader_t *shader, + union i965_shader_channel *src, + const cairo_linear_pattern_t *linear, + const cairo_rectangle_int_t *extents) +{ + intel_buffer_t buffer; + cairo_status_t status; + double x0, y0, sf; + double dx, dy, offset; + + status = intel_gradient_render (&i965_device (shader->target)->intel, + &linear->base, &buffer); + if (unlikely (status)) + return status; + + src->type.vertex = VS_NONE; + src->type.pattern = PATTERN_LINEAR; + src->type.fragment = FS_LINEAR; + src->base.bo = buffer.bo; + src->base.content = CAIRO_CONTENT_COLOR_ALPHA; + src->base.format = buffer.format; + src->base.width = buffer.width; + src->base.height = buffer.height; + src->base.stride = buffer.stride; + src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR); + src->base.extend = i965_extend (linear->base.base.extend); + + dx = linear->pd2.x - linear->pd1.x; + dy = linear->pd2.y - linear->pd1.y; + sf = 1. / (dx * dx + dy * dy); + dx *= sf; + dy *= sf; + + x0 = linear->pd1.x; + y0 = linear->pd1.y; + offset = dx*x0 + dy*y0; + + if (_cairo_matrix_is_identity (&linear->base.base.matrix)) { + src->base.matrix.xx = dx; + src->base.matrix.xy = dy; + src->base.matrix.x0 = -offset; + } else { + cairo_matrix_t m; + + cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0); + cairo_matrix_multiply (&src->base.matrix, &linear->base.base.matrix, &m); + } + src->base.matrix.yx = 0.; + src->base.matrix.yy = 1.; + src->base.matrix.y0 = 0.; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_shader_acquire_radial (i965_shader_t *shader, + union i965_shader_channel *src, + const cairo_radial_pattern_t *radial, + const cairo_rectangle_int_t *extents) +{ + intel_buffer_t buffer; + cairo_status_t status; + double dx, dy, dr, r1; + + status = intel_gradient_render (&i965_device (shader->target)->intel, + &radial->base, &buffer); + if (unlikely (status)) + return status; + + src->type.vertex = VS_NONE; + src->type.pattern = PATTERN_RADIAL; + src->type.fragment = FS_RADIAL; + src->base.bo = buffer.bo; + src->base.content = CAIRO_CONTENT_COLOR_ALPHA; + src->base.format = buffer.format; + src->base.width = buffer.width; + src->base.height = buffer.height; + src->base.stride = buffer.stride; + src->base.filter = i965_filter (CAIRO_FILTER_BILINEAR); + src->base.extend = i965_extend (radial->base.base.extend); + + dx = radial->cd2.center.x - radial->cd1.center.x; + dy = radial->cd2.center.y - radial->cd1.center.y; + dr = radial->cd2.radius - radial->cd1.radius; + + r1 = radial->cd1.radius; + + if (FALSE && (radial->cd2.center.x == radial->cd1.center.x && + radial->cd2.center.y == radial->cd1.center.y)) + { + /* XXX dr == 0, meaningless with anything other than PAD */ + src->base.constants[0] = radial->cd1.center.x / dr; + src->base.constants[1] = radial->cd1.center.y / dr; + src->base.constants[2] = 1. / dr; + src->base.constants[3] = -r1 / dr; + + src->base.constants_size = 4; + src->base.mode = RADIAL_ONE; + } else { + src->base.constants[0] = -radial->cd1.center.x; + src->base.constants[1] = -radial->cd1.center.y; + src->base.constants[2] = r1; + src->base.constants[3] = -4 * (dx*dx + dy*dy - dr*dr); + + src->base.constants[4] = -2 * dx; + src->base.constants[5] = -2 * dy; + src->base.constants[6] = -2 * r1 * dr; + src->base.constants[7] = 1 / (2 * (dx*dx + dy*dy - dr*dr)); + + src->base.constants_size = 8; + src->base.mode = RADIAL_TWO; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_surface_clone (i965_device_t *device, + cairo_image_surface_t *image, + i965_surface_t **clone_out) +{ + i965_surface_t *clone; + cairo_status_t status; + + clone = (i965_surface_t *) + i965_surface_create_internal (&device->intel.base, + image->base.content, + image->width, + image->height, + I965_TILING_DEFAULT, + FALSE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + status = intel_bo_put_image (&device->intel, + to_intel_bo (clone->intel.drm.bo), + image, + 0, 0, + image->width, image->height, + 0, 0); + + if (unlikely (status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return status; + } + + status = intel_snapshot_cache_insert (&device->intel, &clone->intel); + if (unlikely (status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return status; + } + + _cairo_surface_attach_snapshot (&image->base, + &clone->intel.drm.base, + intel_surface_detach_snapshot); + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_surface_clone_subimage (i965_device_t *device, + cairo_image_surface_t *image, + const cairo_rectangle_int_t *extents, + i965_surface_t **clone_out) +{ + i965_surface_t *clone; + cairo_status_t status; + + clone = (i965_surface_t *) + i965_surface_create_internal (&device->intel.base, + image->base.content, + extents->width, + extents->height, + I965_TILING_DEFAULT, + FALSE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + status = intel_bo_put_image (to_intel_device (clone->intel.drm.base.device), + to_intel_bo (clone->intel.drm.bo), + image, + extents->x, extents->y, + extents->width, extents->height, + 0, 0); + if (unlikely (status)) + return status; + + *clone_out = clone; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_shader_acquire_solid_surface (i965_shader_t *shader, + union i965_shader_channel *src, + cairo_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + uint32_t argb; + + status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->format != CAIRO_FORMAT_ARGB32) { + cairo_surface_t *pixel; + cairo_surface_pattern_t pattern; + + /* extract the pixel as argb32 */ + pixel = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); + _cairo_pattern_init_for_surface (&pattern, &image->base); + cairo_matrix_init_translate (&pattern.base.matrix, extents->x, extents->y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (pixel, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + _cairo_surface_release_source_image (surface, image, image_extra); + cairo_surface_destroy (pixel); + return status; + } + + argb = *(uint32_t *) ((cairo_image_surface_t *) pixel)->data; + cairo_surface_destroy (pixel); + } else { + argb = ((uint32_t *) (image->data + extents->y * image->stride))[extents->x]; + } + + _cairo_surface_release_source_image (surface, image, image_extra); + + if (argb >> 24 == 0) + argb = 0; + + src->base.constants[0] = ((argb >> 16) & 0xff) / 255.; + src->base.constants[1] = ((argb >> 8) & 0xff) / 255.; + src->base.constants[2] = ((argb >> 0) & 0xff) / 255.; + src->base.constants[3] = ((argb >> 24) & 0xff) / 255.; + src->base.constants_size = 4; + + src->base.content = CAIRO_CONTENT_COLOR_ALPHA; + if (CAIRO_ALPHA_IS_OPAQUE(src->base.constants[3])) + src->base.content &= ~CAIRO_CONTENT_ALPHA; + src->type.fragment = FS_CONSTANT; + src->type.vertex = VS_NONE; + src->type.pattern = PATTERN_SOLID; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_shader_acquire_surface (i965_shader_t *shader, + union i965_shader_channel *src, + const cairo_surface_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *surface, *drm; + cairo_matrix_t m; + cairo_status_t status; + int src_x = 0, src_y = 0; + + assert (src->type.fragment == FS_NONE); + drm = surface = pattern->surface; + + if (surface->type == CAIRO_SURFACE_TYPE_DRM) { + if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + drm = ((cairo_surface_subsurface_t *) surface)->target; + } else if (surface->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { + drm = ((cairo_surface_snapshot_t *) surface)->target; + } + } + + src->type.pattern = PATTERN_SURFACE; + src->surface.surface = NULL; + if (drm->type == CAIRO_SURFACE_TYPE_DRM) { + i965_surface_t *s = (i965_surface_t *) drm; + + if (surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + if (s->intel.drm.base.device == shader->target->intel.drm.base.device) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surface; + if (s != shader->target) { + int x; + + if (s->intel.drm.fallback != NULL) { + status = intel_surface_flush (s, 0); + if (unlikely (status)) + return status; + } + + if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) + i965_pipelined_flush (i965_device (s)); + + src->type.fragment = FS_SURFACE; + + src->base.bo = to_intel_bo (s->intel.drm.bo); + src->base.format = s->intel.drm.format; + src->base.content = s->intel.drm.base.content; + src->base.width = sub->extents.width; + src->base.height = sub->extents.height; + src->base.stride = s->intel.drm.stride; + + x = sub->extents.x; + if (s->intel.drm.format != CAIRO_FORMAT_A8) + x *= 4; + + /* XXX tiling restrictions upon offset? */ + //src->base.offset[0] = s->offset + sub->extents.y * s->intel.drm.stride + x; + } else { + i965_surface_t *clone; + cairo_surface_pattern_t pattern; + + clone = (i965_surface_t *) + i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device, + s->intel.drm.base.content, + sub->extents.width, + sub->extents.height, + I965_TILING_DEFAULT, + TRUE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + cairo_matrix_init_translate (&pattern.base.matrix, + sub->extents.x, sub->extents.y); + + status = _cairo_surface_paint (&clone->intel.drm.base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return status; + } + + i965_pipelined_flush (i965_device (s)); + src->type.fragment = FS_SURFACE; + + src->base.bo = to_intel_bo (clone->intel.drm.bo); + src->base.format = clone->intel.drm.format; + src->base.content = clone->intel.drm.base.content; + src->base.width = clone->intel.drm.width; + src->base.height = clone->intel.drm.height; + src->base.stride = clone->intel.drm.stride; + + src->surface.surface = &clone->intel.drm.base; + } + + src_x = sub->extents.x; + src_y = sub->extents.y; + } + } else { + if (s->intel.drm.base.device == shader->target->intel.drm.base.device) { + if (s != shader->target) { + if (s->intel.drm.fallback != NULL) { + status = intel_surface_flush (s, 0); + if (unlikely (status)) + return status; + } + + if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) + i965_pipelined_flush (i965_device (s)); + + src->type.fragment = FS_SURFACE; + + src->base.bo = to_intel_bo (s->intel.drm.bo); + src->base.format = s->intel.drm.format; + src->base.content = s->intel.drm.base.content; + src->base.width = s->intel.drm.width; + src->base.height = s->intel.drm.height; + src->base.stride = s->intel.drm.stride; + } else { + i965_surface_t *clone; + cairo_surface_pattern_t pattern; + + clone = (i965_surface_t *) + i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device, + s->intel.drm.base.content, + s->intel.drm.width, + s->intel.drm.height, + I965_TILING_DEFAULT, + TRUE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + _cairo_pattern_init_for_surface (&pattern, &s->intel.drm.base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (&clone->intel.drm.base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (&clone->intel.drm.base); + return status; + } + + i965_pipelined_flush (i965_device (s)); + src->type.fragment = FS_SURFACE; + + src->base.bo = to_intel_bo (clone->intel.drm.bo); + src->base.format = clone->intel.drm.format; + src->base.content = clone->intel.drm.base.content; + src->base.width = clone->intel.drm.width; + src->base.height = clone->intel.drm.height; + src->base.stride = clone->intel.drm.stride; + + src->surface.surface = &clone->intel.drm.base; + } + } + } + } + + if (src->type.fragment == FS_NONE) { + i965_surface_t *s; + + if (extents->width == 1 && extents->height == 1) { + return i965_shader_acquire_solid_surface (shader, src, + surface, extents); + } + + s = (i965_surface_t *) + _cairo_surface_has_snapshot (surface, + shader->target->intel.drm.base.backend); + if (s != NULL) { + i965_device_t *device = i965_device (shader->target); + intel_bo_t *bo = to_intel_bo (s->intel.drm.bo); + + if (bo->purgeable && + ! intel_bo_madvise (&device->intel, bo, I915_MADV_WILLNEED)) + { + _cairo_surface_detach_snapshot (&s->intel.drm.base); + s = NULL; + } + + if (s != NULL) + cairo_surface_reference (&s->intel.drm.base); + } + + if (s == NULL) { + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (surface, &image, &image_extra); + if (unlikely (status)) + return status; + + if (image->width < 8192 && image->height < 8192) { + status = i965_surface_clone (i965_device (shader->target), image, &s); + } else { + status = i965_surface_clone_subimage (i965_device (shader->target), + image, extents, &s); + src_x = -extents->x; + src_y = -extents->y; + } + + _cairo_surface_release_source_image (surface, image, image_extra); + + if (unlikely (status)) + return status; + + /* XXX? */ + //intel_bo_mark_purgeable (to_intel_bo (s->intel.drm.bo), TRUE); + } + + src->type.fragment = FS_SURFACE; + + src->base.bo = to_intel_bo (s->intel.drm.bo); + src->base.content = s->intel.drm.base.content; + src->base.format = s->intel.drm.format; + src->base.width = s->intel.drm.width; + src->base.height = s->intel.drm.height; + src->base.stride = s->intel.drm.stride; + + src->surface.surface = &s->intel.drm.base; + + drm = &s->intel.drm.base; + } + + /* XXX transform nx1 or 1xn surfaces to 1D? */ + + src->type.vertex = VS_NONE; + + src->base.extend = i965_extend (pattern->base.extend); + if (pattern->base.extend == CAIRO_EXTEND_NONE && + extents->x >= 0 && extents->y >= 0 && + extents->x + extents->width <= src->base.width && + extents->y + extents->height <= src->base.height) + { + /* Convert a wholly contained NONE to a REFLECT as the contiguous sampler + * cannot not handle CLAMP_BORDER textures. + */ + src->base.extend = i965_extend (CAIRO_EXTEND_REFLECT); + /* XXX also need to check |u,v| < 3 */ + } + + src->base.filter = i965_filter (pattern->base.filter); + if (_cairo_matrix_is_pixel_exact (&pattern->base.matrix)) + src->base.filter = i965_filter (CAIRO_FILTER_NEAREST); + + /* tweak the src matrix to map from dst to texture coordinates */ + src->base.matrix = pattern->base.matrix; + if (src_x | src_y) + cairo_matrix_translate (&src->base.matrix, src_x, src_x); + cairo_matrix_init_scale (&m, 1. / src->base.width, 1. / src->base.height); + cairo_matrix_multiply (&src->base.matrix, &src->base.matrix, &m); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +i965_shader_acquire_pattern (i965_shader_t *shader, + union i965_shader_channel *src, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return i965_shader_acquire_solid (shader, src, + (cairo_solid_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_LINEAR: + return i965_shader_acquire_linear (shader, src, + (cairo_linear_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_RADIAL: + return i965_shader_acquire_radial (shader, src, + (cairo_radial_pattern_t *) pattern, + extents); + + case CAIRO_PATTERN_TYPE_SURFACE: + return i965_shader_acquire_surface (shader, src, + (cairo_surface_pattern_t *) pattern, + extents); + + default: + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; + } +} + +static void +i965_shader_channel_init (union i965_shader_channel *channel) +{ + channel->type.vertex = VS_NONE; + channel->type.fragment = FS_NONE; + channel->type.pattern = PATTERN_NONE; + + channel->base.mode = 0; + channel->base.bo = NULL; + channel->base.filter = i965_extend (CAIRO_FILTER_NEAREST); + channel->base.extend = i965_extend (CAIRO_EXTEND_NONE); + channel->base.has_component_alpha = 0; + channel->base.constants_size = 0; +} + +void +i965_shader_init (i965_shader_t *shader, + i965_surface_t *dst, + cairo_operator_t op) +{ + shader->committed = FALSE; + shader->device = i965_device (dst); + shader->target = dst; + shader->op = op; + shader->constants_size = 0; + + shader->need_combine = FALSE; + + i965_shader_channel_init (&shader->source); + i965_shader_channel_init (&shader->mask); + i965_shader_channel_init (&shader->clip); + i965_shader_channel_init (&shader->dst); +} + +void +i965_shader_fini (i965_shader_t *shader) +{ + if (shader->source.type.pattern == PATTERN_SURFACE) + cairo_surface_destroy (shader->source.surface.surface); + if (shader->mask.type.pattern == PATTERN_SURFACE) + cairo_surface_destroy (shader->mask.surface.surface); + if (shader->clip.type.pattern == PATTERN_SURFACE) + cairo_surface_destroy (shader->clip.surface.surface); + if (shader->dst.type.pattern == PATTERN_SURFACE) + cairo_surface_destroy (shader->dst.surface.surface); +} + +void +i965_shader_set_clip (i965_shader_t *shader, + cairo_clip_t *clip) +{ + cairo_surface_t *clip_surface; + int clip_x, clip_y; + union i965_shader_channel *channel; + i965_surface_t *s; + + clip_surface = _cairo_clip_get_surface (clip, &shader->target->intel.drm.base, &clip_x, &clip_y); + assert (clip_surface->status == CAIRO_STATUS_SUCCESS); + assert (clip_surface->type == CAIRO_SURFACE_TYPE_DRM); + s = (i965_surface_t *) clip_surface; + + if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) + i965_pipelined_flush (i965_device (s)); + + channel = &shader->clip; + channel->type.pattern = PATTERN_BASE; + channel->type.vertex = VS_NONE; + channel->type.fragment = FS_SURFACE; + + channel->base.bo = to_intel_bo (s->intel.drm.bo); + channel->base.content = CAIRO_CONTENT_ALPHA; + channel->base.format = CAIRO_FORMAT_A8; + channel->base.width = s->intel.drm.width; + channel->base.height = s->intel.drm.height; + channel->base.stride = s->intel.drm.stride; + + channel->base.extend = i965_extend (CAIRO_EXTEND_NONE); + channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST); + + cairo_matrix_init_scale (&shader->clip.base.matrix, + 1. / s->intel.drm.width, + 1. / s->intel.drm.height); + + cairo_matrix_translate (&shader->clip.base.matrix, + -clip_x, -clip_y); +} + +static cairo_bool_t +i965_shader_check_aperture (i965_shader_t *shader, + i965_device_t *device) +{ + uint32_t size = device->exec.gtt_size; + + if (shader->target != device->target) { + const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); + if (bo->exec == NULL) + size += bo->base.size; + } + + if (shader->source.base.bo != NULL && shader->source.base.bo != device->source) { + const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); + if (bo->exec == NULL) + size += bo->base.size; + } + + if (shader->mask.base.bo != NULL && shader->mask.base.bo != device->mask) { + const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); + if (bo->exec == NULL) + size += bo->base.size; + } + + if (shader->clip.base.bo != NULL && shader->clip.base.bo != device->clip) { + const intel_bo_t *bo = to_intel_bo (shader->target->intel.drm.bo); + if (bo->exec == NULL) + size += bo->base.size; + } + + return size <= device->intel.gtt_avail_size; +} + +static cairo_status_t +i965_shader_setup_dst (i965_shader_t *shader) +{ + union i965_shader_channel *channel; + i965_surface_t *s, *clone; + + /* We need to manual blending if we have a clip surface and an unbounded op, + * or an extended blend mode. + */ + if (shader->need_combine || + (shader->op < CAIRO_OPERATOR_SATURATE && + (shader->clip.type.fragment == FS_NONE || + _cairo_operator_bounded_by_mask (shader->op)))) + { + return CAIRO_STATUS_SUCCESS; + } + + shader->need_combine = TRUE; + + s = shader->target; + + /* we need to allocate a new render target and use the original as a source */ + clone = (i965_surface_t *) + i965_surface_create_internal ((cairo_drm_device_t *) s->intel.drm.base.device, + s->intel.drm.base.content, + s->intel.drm.width, + s->intel.drm.height, + I965_TILING_DEFAULT, + TRUE); + if (unlikely (clone->intel.drm.base.status)) + return clone->intel.drm.base.status; + + if (to_intel_bo (s->intel.drm.bo)->batch_write_domain) + i965_pipelined_flush (i965_device (s)); + + channel = &shader->dst; + + channel->type.vertex = VS_NONE; + channel->type.fragment = FS_SURFACE; + channel->type.pattern = PATTERN_SURFACE; + + /* swap buffer objects */ + channel->base.bo = to_intel_bo (s->intel.drm.bo); + s->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo; + ((cairo_drm_surface_t *) clone)->bo = &channel->base.bo->base; + + channel->base.content = s->intel.drm.base.content; + channel->base.format = s->intel.drm.format; + channel->base.width = s->intel.drm.width; + channel->base.height = s->intel.drm.height; + channel->base.stride = s->intel.drm.stride; + + channel->base.filter = i965_filter (CAIRO_FILTER_NEAREST); + channel->base.extend = i965_extend (CAIRO_EXTEND_NONE); + + cairo_matrix_init_scale (&channel->base.matrix, + 1. / s->intel.drm.width, + 1. / s->intel.drm.height); + + channel->surface.surface = &clone->intel.drm.base; + + s->intel.drm.base.content = clone->intel.drm.base.content; + s->intel.drm.format = clone->intel.drm.format; + assert (s->intel.drm.width == clone->intel.drm.width); + assert (s->intel.drm.height == clone->intel.drm.height); + s->intel.drm.stride = clone->intel.drm.stride; + + return CAIRO_STATUS_SUCCESS; +} + +static inline void +constant_add_float (i965_shader_t *shader, float v) +{ + shader->constants[shader->constants_size++] = v; +} + +static inline void +i965_shader_copy_channel_constants (i965_shader_t *shader, + const union i965_shader_channel *channel) +{ + if (channel->base.constants_size) { + assert (shader->constants_size + channel->base.constants_size < ARRAY_LENGTH (shader->constants)); + + memcpy (shader->constants + shader->constants_size, + channel->base.constants, + sizeof (float) * channel->base.constants_size); + shader->constants_size += channel->base.constants_size; + } +} + +static void +i965_shader_setup_channel_constants (i965_shader_t *shader, + const union i965_shader_channel *channel) +{ + switch (channel->type.fragment) { + case FS_NONE: + case FS_CONSTANT: + /* no plane equations */ + break; + + case FS_LINEAR: + constant_add_float (shader, channel->base.matrix.xx); + constant_add_float (shader, channel->base.matrix.xy); + constant_add_float (shader, 0); + constant_add_float (shader, channel->base.matrix.x0); + break; + + case FS_RADIAL: + case FS_SURFACE: + constant_add_float (shader, channel->base.matrix.xx); + constant_add_float (shader, channel->base.matrix.xy); + constant_add_float (shader, 0); + constant_add_float (shader, channel->base.matrix.x0); + + constant_add_float (shader, channel->base.matrix.yx); + constant_add_float (shader, channel->base.matrix.yy); + constant_add_float (shader, 0); + constant_add_float (shader, channel->base.matrix.y0); + break; + + case FS_SPANS: + case FS_GLYPHS: + /* use pue from SF */ + break; + } + + i965_shader_copy_channel_constants (shader, channel); +} + +static void +i965_shader_setup_constants (i965_shader_t *shader) +{ + i965_shader_setup_channel_constants (shader, &shader->source); + i965_shader_setup_channel_constants (shader, &shader->mask); + i965_shader_setup_channel_constants (shader, &shader->clip); + i965_shader_setup_channel_constants (shader, &shader->dst); + assert (shader->constants_size < ARRAY_LENGTH (shader->constants)); +} + +/* + * Highest-valued BLENDFACTOR used in i965_blend_op. + * + * This leaves out BRW_BLENDFACTOR_INV_DST_COLOR, + * BRW_BLENDFACTOR_INV_CONST_{COLOR,ALPHA}, + * BRW_BLENDFACTOR_INV_SRC1_{COLOR,ALPHA} + */ +#define BRW_BLENDFACTOR_COUNT (BRW_BLENDFACTOR_INV_DST_ALPHA + 1) + +static void +i965_shader_get_blend_cntl (const i965_shader_t *shader, + uint32_t *sblend, uint32_t *dblend) +{ + static const struct blendinfo { + cairo_bool_t dst_alpha; + cairo_bool_t src_alpha; + uint32_t src_blend; + uint32_t dst_blend; + } i965_blend_op[] = { + /* CAIRO_OPERATOR_CLEAR treat as SOURCE with transparent */ + {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ZERO}, + /* CAIRO_OPERATOR_SOURCE */ + {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ZERO}, + /* CAIRO_OPERATOR_OVER */ + {0, 1, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_INV_SRC_ALPHA}, + /* CAIRO_OPERATOR_IN */ + {1, 0, BRW_BLENDFACTOR_DST_ALPHA, BRW_BLENDFACTOR_ZERO}, + /* CAIRO_OPERATOR_OUT */ + {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ZERO}, + /* CAIRO_OPERATOR_ATOP */ + {1, 1, BRW_BLENDFACTOR_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA}, + + /* CAIRO_OPERATOR_DEST */ + {0, 0, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_ONE}, + /* CAIRO_OPERATOR_DEST_OVER */ + {1, 0, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_ONE}, + /* CAIRO_OPERATOR_DEST_IN */ + {0, 1, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_SRC_ALPHA}, + /* CAIRO_OPERATOR_DEST_OUT */ + {0, 1, BRW_BLENDFACTOR_ZERO, BRW_BLENDFACTOR_INV_SRC_ALPHA}, + /* CAIRO_OPERATOR_DEST_ATOP */ + {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_SRC_ALPHA}, + /* CAIRO_OPERATOR_XOR */ + {1, 1, BRW_BLENDFACTOR_INV_DST_ALPHA, BRW_BLENDFACTOR_INV_SRC_ALPHA}, + /* CAIRO_OPERATOR_ADD */ + {0, 0, BRW_BLENDFACTOR_ONE, BRW_BLENDFACTOR_ONE}, + }; + const struct blendinfo *op = &i965_blend_op[shader->op]; + + *sblend = op->src_blend; + *dblend = op->dst_blend; + + /* If there's no dst alpha channel, adjust the blend op so that we'll treat + * it as always 1. + */ + if (shader->target->intel.drm.base.content == CAIRO_CONTENT_COLOR && + op->dst_alpha) + { + if (*sblend == BRW_BLENDFACTOR_DST_ALPHA) + *sblend = BRW_BLENDFACTOR_ONE; + else if (*sblend == BRW_BLENDFACTOR_INV_DST_ALPHA) + *sblend = BRW_BLENDFACTOR_ZERO; + } +} + +static void +emit_wm_subpans_to_pixels (struct brw_compile *compile, + int tmp) +{ + /* Inputs: + * R1.5 x/y of upper-left pixel of subspan 3 + * R1.4 x/y of upper-left pixel of subspan 2 + * R1.3 x/y of upper-left pixel of subspan 1 + * R1.2 x/y of upper-left pixel of subspan 0 + * + * Outputs: + * M1,2: u + * M3,4: v + * + * upper left, upper right, lower left, lower right. + */ + + /* compute pixel locations for each subspan */ + brw_set_compression_control (compile, BRW_COMPRESSION_NONE); + brw_ADD (compile, + brw_vec8_grf (tmp), + brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 4, + BRW_REGISTER_TYPE_UW, + BRW_VERTICAL_STRIDE_2, + BRW_WIDTH_4, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_NOOP, + WRITEMASK_XYZW), + brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE)); + brw_ADD (compile, + brw_vec8_grf (tmp+1), + brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 8, + BRW_REGISTER_TYPE_UW, + BRW_VERTICAL_STRIDE_2, + BRW_WIDTH_4, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_NOOP, + WRITEMASK_XYZW), + brw_imm_vf4 (VF_ZERO, VF_ONE, VF_ZERO, VF_ONE)); + brw_ADD (compile, + brw_vec8_grf (tmp+2), + brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 5, + BRW_REGISTER_TYPE_UW, + BRW_VERTICAL_STRIDE_2, + BRW_WIDTH_4, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_NOOP, + WRITEMASK_XYZW), + brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE)); + brw_ADD (compile, + brw_vec8_grf (tmp+3), + brw_reg (BRW_GENERAL_REGISTER_FILE, 1, 9, + BRW_REGISTER_TYPE_UW, + BRW_VERTICAL_STRIDE_2, + BRW_WIDTH_4, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_NOOP, + WRITEMASK_XYZW), + brw_imm_vf4 (VF_ZERO, VF_ZERO, VF_ONE, VF_ONE)); + brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED); +} + +static void +emit_wm_affine (struct brw_compile *compile, + int tmp, int reg, int msg) +{ + emit_wm_subpans_to_pixels (compile, tmp); + + brw_LINE (compile, + brw_null_reg (), + brw_vec1_grf (reg, 0), + brw_vec8_grf (tmp)); + brw_MAC (compile, + brw_message_reg (msg + 1), + brw_vec1_grf (reg, 1), + brw_vec8_grf (tmp+2)); + + brw_LINE (compile, + brw_null_reg (), + brw_vec1_grf (reg, 4), + brw_vec8_grf (tmp)); + brw_MAC (compile, + brw_message_reg (msg + 3), + brw_vec1_grf (reg, 5), + brw_vec8_grf (tmp+2)); +} + +static void +emit_wm_glyph (struct brw_compile *compile, + int tmp, int vue, int msg) +{ + emit_wm_subpans_to_pixels (compile, tmp); + + brw_MUL (compile, + brw_null_reg (), + brw_vec8_grf (tmp), + brw_imm_f (1./1024)); + brw_ADD (compile, + brw_message_reg (msg + 1), + brw_acc_reg (), + brw_vec1_grf (vue, 0)); + + brw_MUL (compile, + brw_null_reg (), + brw_vec8_grf (tmp + 2), + brw_imm_f (1./1024)); + brw_ADD (compile, + brw_message_reg (msg + 3), + brw_acc_reg (), + brw_vec1_grf (vue, 1)); +} + +static void +emit_wm_load_constant (struct brw_compile *compile, + int reg, + struct brw_reg *result) +{ + int n; + + for (n = 0; n < 4; n++) { + result[n] = result[n+4] = brw_reg (BRW_GENERAL_REGISTER_FILE, reg, n, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_0, + BRW_WIDTH_1, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_XXXX, + WRITEMASK_XYZW); + } +} + +static void +emit_wm_load_opacity (struct brw_compile *compile, + int reg, + struct brw_reg *result) +{ + result[0] = result[1] = result[2] = result[3] = + result[4] = result[5] = result[6] = result[7] = + brw_reg (BRW_GENERAL_REGISTER_FILE, reg, 0, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_0, + BRW_WIDTH_1, + BRW_HORIZONTAL_STRIDE_1, + BRW_SWIZZLE_XXXX, + WRITEMASK_XYZW); +} + +static void +emit_wm_load_linear (struct brw_compile *compile, + int tmp, int reg, int msg) +{ + emit_wm_subpans_to_pixels (compile, tmp); + + brw_LINE (compile, + brw_null_reg(), + brw_vec1_grf (reg, 0), + brw_vec8_grf (tmp)); + brw_MAC (compile, + brw_message_reg(msg + 1), + brw_vec1_grf (reg, 1), + brw_vec8_grf (tmp + 2)); +} + +static void +emit_wm_load_radial (struct brw_compile *compile, + int reg, int msg) + +{ + struct brw_reg c1x = brw_vec1_grf (reg, 0); + struct brw_reg c1y = brw_vec1_grf (reg, 1); + struct brw_reg minus_r_sq = brw_vec1_grf (reg, 3); + struct brw_reg cdx = brw_vec1_grf (reg, 4); + struct brw_reg cdy = brw_vec1_grf (reg, 5); + struct brw_reg neg_4a = brw_vec1_grf (reg + 1, 0); + struct brw_reg inv_2a = brw_vec1_grf (reg + 1, 1); + + struct brw_reg tmp_x = brw_uw16_grf (30, 0); + struct brw_reg tmp_y = brw_uw16_grf (28, 0); + struct brw_reg det = brw_vec8_grf (22); + struct brw_reg b = brw_vec8_grf (20); + struct brw_reg c = brw_vec8_grf (18); + struct brw_reg pdx = brw_vec8_grf (16); + struct brw_reg pdy = brw_vec8_grf (14); + struct brw_reg t = brw_message_reg (msg + 1); + + /* cdx = (c₂x - c₁x) + * cdy = (c₂y - c₁y) + * dr = r₂-r₁ + * pdx = px - c₁x + * pdy = py - c₁y + * + * A = cdx² + cdy² - dr² + * B = -2·(pdx·cdx + pdy·cdy + r₁·dr) + * C = pdx² + pdy² - r₁² + * + * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A + */ + + brw_ADD (compile, pdx, vec8 (tmp_x), negate (c1x)); + brw_ADD (compile, pdy, vec8 (tmp_y), negate (c1y)); + + brw_LINE (compile, brw_null_reg (), cdx, pdx); + brw_MAC (compile, b, cdy, pdy); + + brw_MUL (compile, brw_null_reg (), pdx, pdx); + brw_MAC (compile, c, pdy, pdy); + brw_ADD (compile, c, c, minus_r_sq); + + brw_MUL (compile, brw_null_reg (), b, b); + brw_MAC (compile, det, neg_4a, c); + + /* XXX use rsqrt like i915?, it's faster and we need to mac anyway */ + brw_math (compile, + det, + BRW_MATH_FUNCTION_SQRT, + BRW_MATH_SATURATE_NONE, + 2, + det, + BRW_MATH_DATA_VECTOR, + BRW_MATH_PRECISION_FULL); + + /* XXX cmp, +- */ + + brw_ADD (compile, det, negate (det), negate (b)); + brw_ADD (compile, det, det, negate (b)); + brw_MUL (compile, t, det, inv_2a); +} + +static int +emit_wm_sample (struct brw_compile *compile, + union i965_shader_channel *channel, + int sampler, + int msg_base, int msg_len, + int dst, + struct brw_reg *result) +{ + int response_len, mask; + + if (channel->base.content == CAIRO_CONTENT_ALPHA) { + mask = 0x7000; + response_len = 2; + result[0] = result[1] = result[2] = result[3] = brw_vec8_grf (dst); + result[4] = result[5] = result[6] = result[7] = brw_vec8_grf (dst + 1); + } else { + mask = 0; + response_len = 8; + result[0] = brw_vec8_grf (dst + 0); + result[1] = brw_vec8_grf (dst + 2); + result[2] = brw_vec8_grf (dst + 4); + result[3] = brw_vec8_grf (dst + 6); + result[4] = brw_vec8_grf (dst + 1); + result[5] = brw_vec8_grf (dst + 3); + result[6] = brw_vec8_grf (dst + 5); + result[7] = brw_vec8_grf (dst + 7); + } + + brw_set_compression_control (compile, BRW_COMPRESSION_NONE); + + brw_set_mask_control (compile, BRW_MASK_DISABLE); + brw_MOV (compile, + get_element_ud (brw_vec8_grf (0), 2), + brw_imm_ud (mask)); + brw_set_mask_control (compile, BRW_MASK_ENABLE); + + brw_SAMPLE (compile, + brw_uw16_grf (dst, 0), + msg_base, + brw_uw8_grf (0, 0), + sampler + 1, /* binding table */ + sampler, + WRITEMASK_XYZW, + BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE, + response_len, + msg_len, + 0 /* eot */); + + brw_set_compression_control (compile, BRW_COMPRESSION_COMPRESSED); + + return response_len; +} + +#define MAX_MSG_REGISTER 16 + +static void +emit_wm_load_channel (struct brw_compile *compile, + union i965_shader_channel *channel, + int *vue, + int *cue, + int *msg, + int *sampler, + int *grf, + struct brw_reg *result) +{ + switch (channel->type.fragment) { + case FS_NONE: + break; + + case FS_CONSTANT: + emit_wm_load_constant (compile, *cue, result); + *cue += 1; + break; + + case FS_RADIAL: + emit_wm_load_radial (compile, *cue, *msg); + *cue += 2; + + if (*msg + 3 > MAX_MSG_REGISTER) + *msg = 1; + + *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result); + *sampler += 1; + *msg += 3; + break; + + case FS_LINEAR: + emit_wm_load_linear (compile, *grf, *cue, *msg); + *cue += 1; + + if (*msg + 3 > MAX_MSG_REGISTER) + *msg = 1; + + *grf += emit_wm_sample (compile, channel, *sampler, *msg, 3, *grf, result); + *sampler += 1; + *msg += 3; + break; + + case FS_SURFACE: + emit_wm_affine (compile, *grf, *cue, *msg); + *cue += 2; + + if (*msg + 5 > MAX_MSG_REGISTER) + *msg = 1; + + *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result); + *sampler += 1; + *msg += 5; + break; + + case FS_SPANS: + emit_wm_load_opacity (compile, *vue, result); + *vue += 1; + break; + + case FS_GLYPHS: + emit_wm_glyph (compile, *grf, *vue, *msg); + *vue += 1; + + if (*msg + 5 > MAX_MSG_REGISTER) + *msg = 1; + + *grf += emit_wm_sample (compile, channel, *sampler, *msg, 5, *grf, result); + *sampler += 1; + *msg += 5; + break; + } +} + +static unsigned long +i965_wm_kernel_hash (const i965_shader_t *shader) +{ + unsigned long hash; + + hash = + (shader->source.type.fragment & 0xff) | + (shader->mask.type.fragment & 0xff) << 8 | + (shader->clip.type.fragment & 0xff) << 16; + if (shader->need_combine) + hash |= (1u + shader->op) << 24; + + return hash; +} + +static void +i965_wm_kernel_init (struct i965_wm_kernel *key, + const i965_shader_t *shader) +{ + key->entry.hash = i965_wm_kernel_hash (shader); +} + +static uint32_t +i965_shader_const_urb_length (i965_shader_t *shader) +{ + const int lengths[] = { 0, 1, 1, 4, 2, 0, 0 }; + int count = 0; /* 128-bit/16-byte increments */ + + count += lengths[shader->source.type.fragment]; + count += lengths[shader->mask.type.fragment]; + count += lengths[shader->clip.type.fragment]; + count += lengths[shader->dst.type.fragment]; + + return (count + 1) / 2; /* 256-bit/32-byte increments */ +} + +static uint32_t +i965_shader_pue_length (i965_shader_t *shader) +{ + return 1 + (shader->mask.type.vertex != VS_NONE); +} + +static uint32_t +create_wm_kernel (i965_device_t *device, + i965_shader_t *shader, + int *num_reg) +{ + struct brw_compile compile; + struct brw_reg source[8], mask[8], clip[8], dst[8]; + const uint32_t *program; + uint32_t size; + int msg, cue, vue, grf, sampler; + int i; + + struct i965_wm_kernel key, *cache; + cairo_status_t status; + uint32_t offset; + + i965_wm_kernel_init (&key, shader); + cache = _cairo_hash_table_lookup (device->wm_kernels, &key.entry); + if (cache != NULL) + return cache->offset; + + brw_compile_init (&compile, device->is_g4x); + + if (key.entry.hash == FS_CONSTANT && + to_intel_bo (shader->target->intel.drm.bo)->tiling) + { + struct brw_instruction *insn; + + assert (i965_shader_const_urb_length (shader) == 1); + brw_MOV (&compile, brw_message4_reg (2), brw_vec4_grf (2, 0)); + grf = 3; + + brw_push_insn_state (&compile); + brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */ + brw_MOV (&compile, + retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD), + retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD)); + brw_pop_insn_state (&compile); + + insn = brw_next_instruction (&compile, BRW_OPCODE_SEND); + insn->header.predicate_control = 0; + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.destreg__conditonalmod = 0; + + brw_instruction_set_destination (insn, + retype (vec16 (brw_acc_reg ()), + BRW_REGISTER_TYPE_UW)); + + brw_instruction_set_source0 (insn, + retype (brw_vec8_grf (0), + BRW_REGISTER_TYPE_UW)); + + brw_instruction_set_dp_write_message (insn, + 0, + BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED, /* msg_control */ + BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */ + 3, + 1, /* pixel scoreboard */ + 0, + TRUE); + } + else + { + msg = 1; + cue = 2; + vue = cue + i965_shader_const_urb_length (shader); + grf = vue + i965_shader_pue_length (shader); + sampler = 0; + + brw_set_compression_control (&compile, BRW_COMPRESSION_COMPRESSED); + emit_wm_load_channel (&compile, &shader->source, + &vue, &cue, &msg, &sampler, &grf, + source); + emit_wm_load_channel (&compile, &shader->mask, + &vue, &cue, &msg, &sampler, &grf, + mask); + emit_wm_load_channel (&compile, &shader->clip, + &vue, &cue, &msg, &sampler, &grf, + clip); + emit_wm_load_channel (&compile, &shader->dst, + &vue, &cue, &msg, &sampler, &grf, + dst); + brw_set_compression_control (&compile, BRW_COMPRESSION_NONE); + + if (shader->need_combine) { + if (shader->mask.type.fragment != FS_NONE && + shader->clip.type.fragment != FS_NONE) + { + for (i = 0; i < 8; i++) + brw_MUL (&compile, mask[i], mask[i], clip[i]); + } + + /* XXX LERP ! */ + for (i = 0; i < 8; i++) + brw_MOV (&compile, brw_message_reg (2 + i), source[i]); + } else { + if (shader->mask.type.fragment != FS_NONE) { + if (shader->clip.type.fragment != FS_NONE) { + for (i = 0; i < 8; i++) + brw_MUL (&compile, mask[i], mask[i], clip[i]); + } + + for (i = 0; i < 8; i++) + brw_MUL (&compile, brw_message_reg (2 + i), source[i], mask[i]); + } else { + if (shader->clip.type.fragment != FS_NONE) { + for (i = 0; i < 8; i++) + brw_MUL (&compile, brw_message_reg (2 + i), source[i], clip[i]); + } else { + for (i = 0; i < 8; i++) + brw_MOV (&compile, brw_message_reg (2 + i), source[i]); + } + } + } + + brw_push_insn_state (&compile); + brw_set_mask_control (&compile, BRW_MASK_DISABLE); /* ? */ + brw_MOV (&compile, + retype (brw_message_reg (1), BRW_REGISTER_TYPE_UD), + retype (brw_vec8_grf (1), BRW_REGISTER_TYPE_UD)); + brw_pop_insn_state (&compile); + + brw_fb_WRITE (&compile, + retype (vec16 (brw_acc_reg ()), BRW_REGISTER_TYPE_UW), + 0, /* base reg */ + retype (brw_vec8_grf (0), BRW_REGISTER_TYPE_UW), + 0, /* binding table index */ + 2 + 8, /* msg length */ + 0, /* response length */ + TRUE); /* EOT */ + } + + program = brw_get_program (&compile, &size); + *num_reg = grf; + + i965_stream_align (&device->general, 64); + offset = i965_stream_emit (&device->general, program, size); + + cache = _cairo_freelist_alloc (&device->wm_kernel_freelist); + if (likely (cache != NULL)) { + i965_wm_kernel_init (cache, shader); + cache->offset = offset; + status = _cairo_hash_table_insert (device->wm_kernels, &cache->entry); + if (unlikely (status)) + _cairo_freelist_free (&device->wm_kernel_freelist, cache); + } + + return offset; +} + +static uint32_t +create_sf_kernel (i965_device_t *device, + i965_shader_t *shader) +{ + struct brw_compile compile; + const uint32_t *program; + uint32_t size; + int msg_len; + + brw_compile_init (&compile, device->is_g4x); + + switch (shader->mask.type.vertex) { + default: + case VS_NONE: + /* use curb plane eq in WM */ + msg_len = 1; + break; + + case VS_SPANS: + /* just a constant opacity */ + brw_MOV (&compile, + brw_message4_reg (1), + brw_vec4_grf (3, 0)); + msg_len = 2; + break; + + case VS_GLYPHS: + /* an offset+sf into the glyph cache */ + brw_MOV (&compile, + brw_acc_reg (), + brw_vec2_grf (3, 0)); + brw_MAC (&compile, + brw_message4_reg (1), + negate (brw_vec2_grf (1, 4)), + brw_imm_f (1./1024)); + msg_len = 2; + break; + } + + brw_urb_WRITE (&compile, + brw_null_reg (), + 0, + brw_vec8_grf (0), /* r0, will be copied to m0 */ + 0, /* allocate */ + 1, /* used */ + msg_len, + 0, /* response len */ + 1, /* eot */ + 1, /* writes complete */ + 0, /* offset */ + BRW_URB_SWIZZLE_NONE); + + program = brw_get_program (&compile, &size); + + i965_stream_align (&device->general, 64); + return i965_stream_emit (&device->general, program, size); +} + +static uint32_t +i965_sf_kernel (const i965_shader_t *shader) +{ + return shader->mask.type.vertex; +} + +static void +i965_sf_state_init (struct i965_sf_state *key, + const i965_shader_t *shader) +{ + key->entry.hash = i965_sf_kernel (shader); +} + +cairo_bool_t +i965_sf_state_equal (const void *A, const void *B) +{ + const cairo_hash_entry_t *a = A, *b = B; + return a->hash == b->hash; +} + +/* + * Sets up the SF state pointing at an SF kernel. + * + * The SF kernel does coord interp: for each attribute, + * calculate dA/dx and dA/dy. Hand these interpolation coefficients + * back to SF which then hands pixels off to WM. + */ +static uint32_t +gen4_create_sf_state (i965_device_t *device, + i965_shader_t *shader) +{ + struct brw_sf_unit_state *state; + struct i965_sf_state key, *cache; + cairo_status_t status; + uint32_t offset; + + i965_sf_state_init (&key, shader); + if (i965_sf_state_equal (&key, &device->sf_state)) + return device->sf_state.offset; + + cache = _cairo_hash_table_lookup (device->sf_states, &key.entry); + if (cache != NULL) { + offset = cache->offset; + goto DONE; + } + + offset = create_sf_kernel (device, shader); + + state = i965_stream_alloc (&device->general, 32, sizeof (*state)); + memset (state, 0, sizeof (*state)); + + state->thread0.grf_reg_count = BRW_GRF_BLOCKS (3); + assert ((offset & 63) == 0); + state->thread0.kernel_start_pointer = offset >> 6; + state->sf1.single_program_flow = 1; + state->thread3.urb_entry_read_length = 1; /* 1 URB per vertex */ + state->thread3.urb_entry_read_offset = 1; + state->thread3.dispatch_grf_start_reg = 3; + state->thread4.max_threads = SF_MAX_THREADS - 1; + state->thread4.urb_entry_allocation_size = URB_SF_ENTRY_SIZE - 1; + state->thread4.nr_urb_entries = URB_SF_ENTRIES; + state->sf6.dest_org_vbias = 0x8; + state->sf6.dest_org_hbias = 0x8; + + offset = i965_stream_offsetof (&device->general, state); + + cache = _cairo_freelist_alloc (&device->sf_freelist); + if (likely (cache != NULL)) { + i965_sf_state_init (cache, shader); + cache->offset = offset; + status = _cairo_hash_table_insert (device->sf_states, &cache->entry); + if (unlikely (status)) + _cairo_freelist_free (&device->sf_freelist, cache); + } + + DONE: + i965_sf_state_init (&device->sf_state, shader); + device->sf_state.offset = offset; + + return offset; +} + +static unsigned long +i965_shader_sampler_hash (const i965_shader_t *shader) +{ + unsigned long hash = 0; + unsigned int offset = 0; + + if (shader->source.base.bo != NULL) { + hash |= (shader->source.base.filter << offset) | + (shader->source.base.extend << (offset + 4)); + offset += 8; + } + + if (shader->mask.base.bo != NULL) { + hash |= (shader->mask.base.filter << offset) | + (shader->mask.base.extend << (offset + 4)); + offset += 8; + } + + if (shader->clip.base.bo != NULL) { + hash |= (shader->clip.base.filter << offset) | + (shader->clip.base.extend << (offset + 4)); + offset += 8; + } + + if (shader->dst.base.bo != NULL) { + hash |= (shader->dst.base.filter << offset) | + (shader->dst.base.extend << (offset + 4)); + offset += 8; + } + + return hash; +} + +static void +i965_sampler_init (struct i965_sampler *key, + const i965_shader_t *shader) +{ + key->entry.hash = i965_shader_sampler_hash (shader); +} + +static void +emit_sampler_channel (i965_device_t *device, + const union i965_shader_channel *channel, + uint32_t border_color) +{ + struct brw_sampler_state *state; + + state = i965_stream_alloc (&device->general, 0, sizeof (*state)); + memset (state, 0, sizeof (*state)); + + state->ss0.lod_preclamp = 1; /* GL mode */ + + state->ss0.border_color_mode = BRW_BORDER_COLOR_MODE_LEGACY; + + state->ss0.min_filter = channel->base.filter; + state->ss0.mag_filter = channel->base.filter; + + state->ss1.r_wrap_mode = channel->base.extend; + state->ss1.s_wrap_mode = channel->base.extend; + state->ss1.t_wrap_mode = channel->base.extend; + + assert ((border_color & 31) == 0); + state->ss2.border_color_pointer = border_color >> 5; +} + +static uint32_t +emit_sampler_state_table (i965_device_t *device, + i965_shader_t *shader) +{ + struct i965_sampler key, *cache; + cairo_status_t status; + uint32_t offset; + + if (device->border_color_offset == (uint32_t) -1) { + struct brw_sampler_legacy_border_color *border_color; + + border_color = i965_stream_alloc (&device->general, 32, + sizeof (*border_color)); + border_color->color[0] = 0; /* R */ + border_color->color[1] = 0; /* G */ + border_color->color[2] = 0; /* B */ + border_color->color[3] = 0; /* A */ + + device->border_color_offset = i965_stream_offsetof (&device->general, + border_color); + } else { + i965_sampler_init (&key, shader); + cache = _cairo_hash_table_lookup (device->samplers, &key.entry); + if (cache != NULL) + return cache->offset; + } + + i965_stream_align (&device->general, 32); + offset = device->general.used; + if (shader->source.base.bo != NULL) { + emit_sampler_channel (device, + &shader->source, + device->border_color_offset); + } + if (shader->mask.base.bo != NULL) { + emit_sampler_channel (device, + &shader->mask, + device->border_color_offset); + } + if (shader->clip.base.bo != NULL) { + emit_sampler_channel (device, + &shader->clip, + device->border_color_offset); + } + if (shader->dst.base.bo != NULL) { + emit_sampler_channel (device, + &shader->dst, + device->border_color_offset); + } + + cache = _cairo_freelist_alloc (&device->sampler_freelist); + if (likely (cache != NULL)) { + i965_sampler_init (cache, shader); + cache->offset = offset; + status = _cairo_hash_table_insert (device->samplers, &cache->entry); + if (unlikely (status)) + _cairo_freelist_free (&device->sampler_freelist, cache); + } + + return offset; +} + +static void +i965_cc_state_init (struct i965_cc_state *key, + const i965_shader_t *shader) +{ + uint32_t src_blend, dst_blend; + + if (shader->need_combine) + src_blend = dst_blend = 0; + else + i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend); + + key->entry.hash = src_blend | ((dst_blend & 0xffff) << 16); +} + +cairo_bool_t +i965_cc_state_equal (const void *A, const void *B) +{ + const cairo_hash_entry_t *a = A, *b = B; + return a->hash == b->hash; +} + +static uint32_t +cc_state_emit (i965_device_t *device, i965_shader_t *shader) +{ + struct brw_cc_unit_state *state; + struct i965_cc_state key, *cache; + cairo_status_t status; + uint32_t src_blend, dst_blend; + uint32_t offset; + + i965_cc_state_init (&key, shader); + if (i965_cc_state_equal (&key, &device->cc_state)) + return device->cc_state.offset; + + cache = _cairo_hash_table_lookup (device->cc_states, &key.entry); + if (cache != NULL) { + offset = cache->offset; + goto DONE; + } + + if (shader->need_combine) + src_blend = dst_blend = 0; + else + i965_shader_get_blend_cntl (shader, &src_blend, &dst_blend); + + state = i965_stream_alloc (&device->general, 64, sizeof (*state)); + memset (state, 0, sizeof (*state)); + + /* XXX Note errata, need to flush render cache when blend_enable 0 -> 1 */ + /* XXX 2 source blend */ + state->cc3.blend_enable = ! shader->need_combine; + state->cc5.ia_blend_function = BRW_BLENDFUNCTION_ADD; + state->cc5.ia_src_blend_factor = src_blend; + state->cc5.ia_dest_blend_factor = dst_blend; + state->cc6.blend_function = BRW_BLENDFUNCTION_ADD; + state->cc6.clamp_post_alpha_blend = 1; + state->cc6.clamp_pre_alpha_blend = 1; + state->cc6.src_blend_factor = src_blend; + state->cc6.dest_blend_factor = dst_blend; + + offset = i965_stream_offsetof (&device->general, state); + + cache = _cairo_freelist_alloc (&device->cc_freelist); + if (likely (cache != NULL)) { + i965_cc_state_init (cache, shader); + cache->offset = offset; + status = _cairo_hash_table_insert (device->cc_states, &cache->entry); + if (unlikely (status)) + _cairo_freelist_free (&device->cc_freelist, cache); + } + + DONE: + i965_cc_state_init (&device->cc_state, shader); + device->cc_state.offset = offset; + + return offset; +} + +static void +i965_wm_state_init (struct i965_wm_state *key, + const i965_shader_t *shader) +{ + key->kernel = i965_wm_kernel_hash (shader); + key->sampler = i965_shader_sampler_hash (shader); + + key->entry.hash = key->kernel ^ ((key->sampler) << 16 | (key->sampler >> 16)); +} + +cairo_bool_t +i965_wm_state_equal (const void *A, const void *B) +{ + const struct i965_wm_state *a = A, *b = B; + + if (a->entry.hash != b->entry.hash) + return FALSE; + + return a->kernel == b->kernel && a->sampler == b->sampler; +} + +static int +i965_shader_binding_table_count (i965_shader_t *shader) +{ + int count; + + count = 1; + if (shader->source.type.fragment != FS_CONSTANT) + count++; + switch (shader->mask.type.fragment) { + case FS_NONE: + case FS_CONSTANT: + case FS_SPANS: + break; + case FS_LINEAR: + case FS_RADIAL: + case FS_SURFACE: + case FS_GLYPHS: + count++; + } + if (shader->clip.type.fragment == FS_SURFACE) + count++; + if (shader->dst.type.fragment == FS_SURFACE) + count++; + + return count; +} + +static uint32_t +gen4_create_wm_state (i965_device_t *device, + i965_shader_t *shader) +{ + struct brw_wm_unit_state *state; + uint32_t sampler; + uint32_t kernel; + + struct i965_wm_state key, *cache; + cairo_status_t status; + int num_reg; + + i965_wm_state_init (&key, shader); + if (i965_wm_state_equal (&key, &device->wm_state)) + return device->wm_state.offset; + + cache = _cairo_hash_table_lookup (device->wm_states, &key.entry); + if (cache != NULL) { + device->wm_state = *cache; + return cache->offset; + } + + kernel = create_wm_kernel (device, shader, &num_reg); + sampler = emit_sampler_state_table (device, shader); + + state = i965_stream_alloc (&device->general, 32, sizeof (*state)); + memset (state, 0, sizeof (*state)); + state->thread0.grf_reg_count = BRW_GRF_BLOCKS (num_reg); + assert ((kernel & 63) == 0); + state->thread0.kernel_start_pointer = kernel >> 6; + + state->thread3.dispatch_grf_start_reg = 2; + + state->wm4.sampler_count = 1; /* 1-4 samplers used */ + assert ((sampler & 31) == 0); + state->wm4.sampler_state_pointer = sampler >> 5; + if (device->is_g4x) + state->wm5.max_threads = PS_MAX_THREADS_CTG - 1; + else + state->wm5.max_threads = PS_MAX_THREADS_BRW - 1; + state->wm5.thread_dispatch_enable = 1; + + if (device->is_g4x) { + /* XXX contiguous 32 pixel dispatch */ + } + state->wm5.enable_16_pix = 1; + /* 8 pixel dispatch and friends */ + //state->wm5.early_depth_test = 1; + + state->thread1.binding_table_entry_count = i965_shader_binding_table_count(shader); + state->thread3.urb_entry_read_length = i965_shader_pue_length (shader); + state->thread3.const_urb_entry_read_length = i965_shader_const_urb_length (shader); + + key.offset = i965_stream_offsetof (&device->general, state); + + cache = _cairo_freelist_alloc (&device->wm_state_freelist); + if (likely (cache != NULL)) { + *cache = key; + status = _cairo_hash_table_insert (device->wm_states, &cache->entry); + if (unlikely (status)) + _cairo_freelist_free (&device->wm_state_freelist, cache); + } + + device->wm_state = key; + return key.offset; +} + +static uint32_t +vs_unit_state_emit (i965_device_t *device) +{ + if (device->vs_offset == (uint32_t) -1) { + struct brw_vs_unit_state *state; + + /* Set up the vertex shader to be disabled (passthrough) */ + state = i965_stream_alloc (&device->general, 32, sizeof (*state)); + memset (state, 0, sizeof (*state)); + + state->thread4.nr_urb_entries = URB_VS_ENTRIES; + state->thread4.urb_entry_allocation_size = URB_VS_ENTRY_SIZE - 1; + state->vs6.vert_cache_disable = 1; + + device->vs_offset = i965_stream_offsetof (&device->general, state); + } + + return device->vs_offset; +} + +static uint32_t +i965_get_card_format (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + return BRW_SURFACEFORMAT_B8G8R8A8_UNORM; + case CAIRO_FORMAT_RGB24: + return BRW_SURFACEFORMAT_B8G8R8X8_UNORM; + case CAIRO_FORMAT_RGB16_565: + return BRW_SURFACEFORMAT_B5G6R5_UNORM; + case CAIRO_FORMAT_A8: + return BRW_SURFACEFORMAT_A8_UNORM; + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static uint32_t +i965_get_dest_format (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + return BRW_SURFACEFORMAT_B8G8R8A8_UNORM; + case CAIRO_FORMAT_RGB16_565: + return BRW_SURFACEFORMAT_B5G6R5_UNORM; + case CAIRO_FORMAT_A8: + return BRW_SURFACEFORMAT_A8_UNORM; + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +/* XXX silly inline due to compiler bug... */ +static inline void +i965_stream_add_pending_relocation (i965_stream_t *stream, + uint32_t target_offset, + uint32_t read_domains, + uint32_t write_domain, + uint32_t delta) +{ + int n; + + n = stream->num_pending_relocations++; + assert (n < stream->max_pending_relocations); + + stream->pending_relocations[n].offset = target_offset; + stream->pending_relocations[n].read_domains = read_domains; + stream->pending_relocations[n].write_domain = write_domain; + stream->pending_relocations[n].delta = delta; +} + +static uint32_t +emit_surface_state (i965_device_t *device, + cairo_bool_t is_target, + intel_bo_t *bo, + cairo_format_t format, + int width, int height, int stride, + int type) +{ + struct brw_surface_state *state; + uint32_t write_domain, read_domains; + uint32_t offset; + + state = i965_stream_alloc (&device->surface, 32, sizeof (*state)); + memset (state, 0, sizeof (*state)); + + state->ss0.surface_type = type; + if (is_target) + state->ss0.surface_format = i965_get_dest_format (format); + else + state->ss0.surface_format = i965_get_card_format (format); + + state->ss0.data_return_format = BRW_SURFACERETURNFORMAT_FLOAT32; + state->ss0.color_blend = 1; + if (is_target && device->is_g4x) + state->ss0.render_cache_read_mode = 1; + + state->ss1.base_addr = bo->offset; + + state->ss2.height = height - 1; + state->ss2.width = width - 1; + state->ss3.pitch = stride - 1; + state->ss3.tile_walk = bo->tiling == I915_TILING_Y; + state->ss3.tiled_surface = bo->tiling != I915_TILING_NONE; + + if (is_target) { + read_domains = I915_GEM_DOMAIN_RENDER; + write_domain = I915_GEM_DOMAIN_RENDER; + } else { + read_domains = I915_GEM_DOMAIN_SAMPLER; + write_domain = 0; + } + + offset = i965_stream_offsetof (&device->surface, state); + i965_emit_relocation (device, &device->surface, + bo, 0, + read_domains, write_domain, + offset + offsetof (struct brw_surface_state, ss1.base_addr)); + return offset; +} + +static uint32_t +emit_surface_state_for_shader (i965_device_t *device, + const union i965_shader_channel *channel) +{ + int type = BRW_SURFACE_2D; + + assert (channel->type.fragment != FS_NONE); + assert (channel->type.fragment != FS_CONSTANT); + + if (channel->type.fragment != FS_SURFACE) + type = BRW_SURFACE_1D; + + return emit_surface_state (device, FALSE, + channel->base.bo, + channel->base.format, + channel->base.width, + channel->base.height, + channel->base.stride, + type); +} + +cairo_bool_t +i965_wm_binding_equal (const void *A, + const void *B) +{ + const struct i965_wm_binding *a = A, *b = B; + + if (a->entry.hash != b->entry.hash) + return FALSE; + + if (a->size != b->size) + return FALSE; + + return memcmp (a->table, b->table, sizeof (uint32_t) * a->size) == 0; +} + +static void +i965_wm_binding_init (struct i965_wm_binding *state, + const uint32_t *table, + int size) +{ + int n; + + state->entry.hash = size; + state->size = size; + + for (n = 0; n < size; n++) { + state->table[n] = table[n]; + state->entry.hash ^= (table[n] << (8 * n)) | + (table[n] >> (32 - (8*n))); + } +} + +static uint32_t +emit_binding_table (i965_device_t *device, + i965_shader_t *shader) +{ + intel_bo_t *bo; + struct i965_wm_binding key, *cache; + uint32_t *table; + int n = 0; + + table = i965_stream_alloc (&device->surface, 32, 5 * sizeof (uint32_t)); + if (shader->target->stream != device->surface.serial) { + shader->target->stream = device->surface.serial; + shader->target->offset = emit_surface_state (device, + TRUE, + to_intel_bo (shader->target->intel.drm.bo), + shader->target->intel.drm.format, + shader->target->intel.drm.width, + shader->target->intel.drm.height, + shader->target->intel.drm.stride, + BRW_SURFACE_2D); + } + table[n++] = shader->target->offset; + + bo = shader->source.base.bo; + if (bo != NULL) { + if (bo->opaque0 != device->surface.serial) { + bo->opaque0 = device->surface.serial; + bo->opaque1 = emit_surface_state_for_shader (device, &shader->source); + } + table[n++] = bo->opaque1; + } + + bo = shader->mask.base.bo; + if (bo != NULL) { + if (bo->opaque0 != device->surface.serial) { + bo->opaque0 = device->surface.serial; + bo->opaque1 = emit_surface_state_for_shader (device, &shader->mask); + } + table[n++] = bo->opaque1; + } + + bo = shader->clip.base.bo; + if (bo != NULL) { + if (bo->opaque0 != device->surface.serial) { + bo->opaque0 = device->surface.serial; + bo->opaque1 = emit_surface_state_for_shader (device, &shader->clip); + } + table[n++] = bo->opaque1; + } + + bo = shader->dst.base.bo; + if (bo != NULL) { + if (bo->opaque0 != device->surface.serial) { + bo->opaque0 = device->surface.serial; + bo->opaque1 = emit_surface_state_for_shader (device, &shader->dst); + } + table[n++] = bo->opaque1; + } + + i965_wm_binding_init (&key, table, n); + key.offset = i965_stream_offsetof (&device->surface, table); + + if (i965_wm_binding_equal (&key, &device->wm_binding)) { + device->surface.used = key.offset; + return device->wm_binding.offset; + } + + cache = _cairo_hash_table_lookup (device->wm_bindings, &key.entry); + if (cache != NULL) { + device->surface.used = key.offset; + key.offset = cache->offset; + } + + device->wm_binding = key; + return key.offset; +} + +static void +i965_emit_invariants (i965_device_t *device) +{ + OUT_BATCH (BRW_CS_URB_STATE | 0); + OUT_BATCH (((URB_CS_ENTRY_SIZE-1) << 4) | (URB_CS_ENTRIES << 0)); +} + +static void +i965_emit_urb_fences (i965_device_t *device) +{ + int urb_vs_start, urb_vs_size; + int urb_gs_start, urb_gs_size; + int urb_clip_start, urb_clip_size; + int urb_sf_start, urb_sf_size; + int urb_cs_start, urb_cs_size; + + if (device->have_urb_fences) + return; + + /* URB fence */ + urb_vs_start = 0; + urb_vs_size = URB_VS_ENTRIES * URB_VS_ENTRY_SIZE; + urb_gs_start = urb_vs_start + urb_vs_size; + urb_gs_size = URB_GS_ENTRIES * URB_GS_ENTRY_SIZE; + urb_clip_start = urb_gs_start + urb_gs_size; + urb_clip_size = URB_CLIP_ENTRIES * URB_CLIP_ENTRY_SIZE; + urb_sf_start = urb_clip_start + urb_clip_size; + urb_sf_size = URB_SF_ENTRIES * URB_SF_ENTRY_SIZE; + urb_cs_start = urb_sf_start + urb_sf_size; + urb_cs_size = URB_CS_ENTRIES * URB_CS_ENTRY_SIZE; + + /* erratum: URB_FENCE must not cross a 64-byte cache-line */ + while ((device->batch.used & 63) > 64-12) + OUT_BATCH (MI_NOOP); + OUT_BATCH (BRW_URB_FENCE | + UF0_CS_REALLOC | + UF0_SF_REALLOC | + UF0_CLIP_REALLOC | + UF0_GS_REALLOC | + UF0_VS_REALLOC | + 1); + OUT_BATCH (((urb_clip_start + urb_clip_size) << UF1_CLIP_FENCE_SHIFT) | + ((urb_gs_start + urb_gs_size) << UF1_GS_FENCE_SHIFT) | + ((urb_vs_start + urb_vs_size) << UF1_VS_FENCE_SHIFT)); + OUT_BATCH (((urb_cs_start + urb_cs_size) << UF2_CS_FENCE_SHIFT) | + ((urb_sf_start + urb_sf_size) << UF2_SF_FENCE_SHIFT)); + + device->have_urb_fences = TRUE; + device->constants_size = 0; +} + +static void +i965_emit_base (i965_device_t *device) +{ + OUT_BATCH (BRW_STATE_BASE_ADDRESS | 4); + if (likely (device->general.num_pending_relocations == 0)) { + i965_stream_add_pending_relocation (&device->general, + device->batch.used, + I915_GEM_DOMAIN_INSTRUCTION, 0, + BASE_ADDRESS_MODIFY); + } + OUT_BATCH (0); /* pending relocation */ + + if (likely (device->surface.num_pending_relocations == 0)) { + i965_stream_add_pending_relocation (&device->surface, + device->batch.used, + I915_GEM_DOMAIN_INSTRUCTION, 0, + BASE_ADDRESS_MODIFY); + } + OUT_BATCH (0); /* pending relocation */ + + OUT_BATCH (0 | BASE_ADDRESS_MODIFY); + /* general state max addr, disabled */ + OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY); + /* media object state max addr, disabled */ + OUT_BATCH (0x10000000 | BASE_ADDRESS_MODIFY); +} + +static void +i965_emit_vertex_element (i965_device_t *device, + i965_shader_t *shader) +{ + uint32_t offset; + uint32_t type; + int nelem; + + type = 0; + nelem = 1; + if (shader->mask.type.vertex == VS_SPANS || + shader->mask.type.vertex == VS_GLYPHS) + { + type = shader->mask.type.vertex; + nelem++; + } + + if (type == device->vertex_type) + return; + device->vertex_type = type; + + offset = 0; + + OUT_BATCH (BRW_3DSTATE_VERTEX_ELEMENTS | ((2 * nelem) - 1)); + OUT_BATCH ((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) | + VE0_VALID | + (BRW_SURFACEFORMAT_R32G32_FLOAT << VE0_FORMAT_SHIFT) | + (offset << VE0_OFFSET_SHIFT)); + OUT_BATCH ((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) | + (BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) | + (BRW_VFCOMPONENT_STORE_0 << VE1_VFCOMPONENT_2_SHIFT) | + (BRW_VFCOMPONENT_STORE_1_FLT << VE1_VFCOMPONENT_3_SHIFT) | + (4 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT)); + offset += 8; + + assert (shader->source.type.vertex == VS_NONE); + switch (shader->mask.type.vertex) { + default: + case VS_NONE: + break; + + case VS_SPANS: + OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) | + VE0_VALID | + (BRW_SURFACEFORMAT_R32_FLOAT << VE0_FORMAT_SHIFT) | + (offset << VE0_OFFSET_SHIFT)); + OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) | + (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_1_SHIFT) | + (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_2_SHIFT) | + (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_3_SHIFT) | + (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT)); + + offset += 4; + break; + + case VS_GLYPHS: + OUT_BATCH((0 << VE0_VERTEX_BUFFER_INDEX_SHIFT) | + VE0_VALID | + (BRW_SURFACEFORMAT_R16G16_FLOAT << VE0_FORMAT_SHIFT) | + (offset << VE0_OFFSET_SHIFT)); + OUT_BATCH((BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_0_SHIFT) | + (BRW_VFCOMPONENT_STORE_SRC << VE1_VFCOMPONENT_1_SHIFT) | + (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_2_SHIFT) | + (BRW_VFCOMPONENT_NOSTORE << VE1_VFCOMPONENT_3_SHIFT) | + (8 << VE1_DESTINATION_ELEMENT_OFFSET_SHIFT)); + + offset += 4; + break; + } + assert (shader->clip.type.vertex == VS_NONE); + assert (shader->dst.type.vertex == VS_NONE); + + device->vertex_size = offset; + i965_stream_align (&device->vertex, device->vertex_size); + device->vertex.committed = device->vertex.used; + + device->rectangle_size = 3 * offset; +} + +static cairo_bool_t +i965_shader_needs_surface_update (const i965_shader_t *shader, + const i965_device_t *device) +{ + return device->target != shader->target || shader->target->stream == 0 || + (shader->source.base.bo != NULL && device->source != shader->source.base.bo) || + (shader->mask.base.bo != NULL && device->mask != shader->mask.base.bo) || + (shader->clip.base.bo != NULL && device->clip != shader->clip.base.bo); +} + +static cairo_bool_t +i965_shader_needs_constants_update (const i965_shader_t *shader, + const i965_device_t *device) +{ + if (shader->constants_size == 0) + return FALSE; + + if (device->constants_size != shader->constants_size) + return TRUE; + + return memcmp (device->constants, + shader->constants, + sizeof (float) * shader->constants_size); +} + +static cairo_bool_t +i965_shader_needs_state_update (const i965_shader_t *shader, + const i965_device_t *device) +{ + union { + struct i965_sf_state sf; + struct i965_wm_state wm; + struct i965_cc_state cc; + } state; + + i965_sf_state_init (&state.sf, shader); + if (! i965_sf_state_equal (&state.sf, &device->sf_state)) + return TRUE; + + i965_wm_state_init (&state.wm, shader); + if (! i965_wm_state_equal (&state.wm, &device->wm_state)) + return TRUE; + + i965_cc_state_init (&state.cc, shader); + if (! i965_cc_state_equal (&state.cc, &device->cc_state)) + return TRUE; + + return FALSE; +} + +static void +i965_emit_composite (i965_device_t *device, + i965_shader_t *shader) +{ + uint32_t draw_rectangle; + + if (i965_shader_needs_surface_update (shader, device)) { + uint32_t offset; + + offset = emit_binding_table (device, shader); + + /* Only the PS uses the binding table */ + OUT_BATCH (BRW_3DSTATE_BINDING_TABLE_POINTERS | 4); + OUT_BATCH (0); /* vs */ + OUT_BATCH (0); /* gs */ + OUT_BATCH (0); /* clip */ + OUT_BATCH (0); /* sf */ + OUT_BATCH (offset); + + device->target = shader->target; + device->source = shader->source.base.bo; + device->mask = shader->mask.base.bo; + device->clip = shader->clip.base.bo; + } + + /* The drawing rectangle clipping is always on. Set it to values that + * shouldn't do any clipping. + */ + draw_rectangle = DRAW_YMAX (shader->target->intel.drm.height) | + DRAW_XMAX (shader->target->intel.drm.width); + if (draw_rectangle != device->draw_rectangle) { + OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2); + OUT_BATCH (0x00000000); /* ymin, xmin */ + OUT_BATCH (draw_rectangle); + OUT_BATCH (0x00000000); /* yorigin, xorigin */ + device->draw_rectangle = draw_rectangle; + } + + /* skip the depth buffer */ + /* skip the polygon stipple */ + /* skip the polygon stipple offset */ + /* skip the line stipple */ + + /* Set the pointers to the 3d pipeline state */ + if (i965_shader_needs_state_update (shader, device)) { + OUT_BATCH (BRW_3DSTATE_PIPELINED_POINTERS | 5); + OUT_BATCH (vs_unit_state_emit (device)); + OUT_BATCH (BRW_GS_DISABLE); + OUT_BATCH (BRW_CLIP_DISABLE); + OUT_BATCH (gen4_create_sf_state (device, shader)); + OUT_BATCH (gen4_create_wm_state (device, shader)); + OUT_BATCH (cc_state_emit (device, shader)); + + /* Once the units are initialized, we need to setup the fences */ + i965_emit_urb_fences (device); + } + + if (i965_shader_needs_constants_update (shader, device)) { + uint32_t size = (sizeof (float) * shader->constants_size + 63) & -64; + + /* XXX reuse clear/black/white + * ht! + */ + + /* XXX CONSTANT_BUFFER Address Offset Disable? INSTPM? */ + + assert (size <= 64 * URB_CS_ENTRY_SIZE); + assert (((sizeof (float) * shader->constants_size + 31) & -32) == 32 * i965_shader_const_urb_length (shader)); + + device->constants = i965_stream_alloc (&device->surface, 64, size); + memcpy (device->constants, shader->constants, size); + device->constants_size = shader->constants_size; + + OUT_BATCH (BRW_CONSTANT_BUFFER | (1 << 8)); + OUT_BATCH (i965_stream_offsetof (&device->surface, device->constants) + size / 64 - 1); + } + + i965_emit_vertex_element (device, shader); +} + +void +i965_flush_vertices (i965_device_t *device) +{ + int vertex_count, vertex_start; + + if (device->vertex.used == device->vertex.committed) + return; + + assert (device->vertex.used > device->vertex.committed); + + vertex_start = device->vertex.committed / device->vertex_size; + vertex_count = + (device->vertex.used - device->vertex.committed) / device->vertex_size; + + assert (vertex_count); + + if (device->vertex_size != device->last_vertex_size) { + i965_stream_add_pending_relocation (&device->vertex, + device->batch.used + 8, + I915_GEM_DOMAIN_VERTEX, 0, + 0); + + OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3); + OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) | + VB0_VERTEXDATA | + (device->vertex_size << VB0_BUFFER_PITCH_SHIFT)); + OUT_BATCH (0); /* pending relocation */ + OUT_BATCH (0); + OUT_BATCH (0); + device->last_vertex_size = device->vertex_size; + } + + OUT_BATCH (BRW_3DPRIMITIVE | + BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL | + (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) | + (0 << 9) | + 4); + OUT_BATCH (vertex_count); /* vertex count per instance */ + OUT_BATCH (vertex_start); /* start vertex offset */ + OUT_BATCH (1); /* single instance */ + OUT_BATCH (0); + OUT_BATCH (0); + + device->vertex.committed = device->vertex.used; +} + +void +i965_finish_vertices (i965_device_t *device) +{ + cairo_status_t status; + + i965_flush_vertices (device); + + i965_stream_commit (device, &device->vertex); + + if (! i965_shader_check_aperture (device->shader, device)) { + status = i965_device_flush (device); + if (unlikely (status)) + longjmp (device->shader->unwind, status); + + status = i965_shader_commit (device->shader, device); + assert (status == CAIRO_STATUS_SUCCESS); + } + + device->last_vertex_size = 0; +} + +static cairo_bool_t +i965_shader_needs_update (const i965_shader_t *shader, + const i965_device_t *device) +{ + if (i965_shader_needs_surface_update (shader, device)) + return TRUE; + + if (i965_shader_needs_constants_update (shader, device)) + return TRUE; + + return i965_shader_needs_state_update (shader, device); +} + +static void +i965_shader_reduce (i965_shader_t *shader, + const i965_device_t *device) +{ + if (shader->op == CAIRO_OPERATOR_OVER && + (i965_wm_kernel_hash (shader) & ~0xff) == 0 && + (shader->source.base.content & CAIRO_CONTENT_ALPHA) == 0) + { + shader->op = CAIRO_OPERATOR_SOURCE; + } +} + +cairo_status_t +i965_shader_commit (i965_shader_t *shader, + i965_device_t *device) +{ + cairo_status_t status; + + if (! shader->committed) { + device->shader = shader; + + status = i965_shader_setup_dst (shader); + if (unlikely (status)) + return status; + + i965_shader_setup_constants (shader); + i965_shader_reduce (shader, device); + + if ((status = setjmp (shader->unwind))) + return status; + + shader->committed = TRUE; + } + + if (! i965_shader_needs_update (shader, device)) + return CAIRO_STATUS_SUCCESS; + + /* XXX too many guestimates about likely maximum sizes */ +recheck: + if (device->batch.used + 128 > device->batch.size || + ! i965_shader_check_aperture (shader, device)) + { + status = i965_device_flush (device); + if (unlikely (status)) + longjmp (shader->unwind, status); + } + + i965_flush_vertices (device); + + if (unlikely (device->surface.used + 128 > device->surface.size || + device->surface.num_relocations + 4 > device->surface.max_relocations)) + { + i965_stream_commit (device, &device->surface); + goto recheck; + } + + if (unlikely (device->general.used + 512 > device->general.size)) { + i965_stream_commit (device, &device->general); + i965_general_state_reset (device); + goto recheck; + } + + if (unlikely (device->batch.used == 0)) + i965_emit_invariants (device); + + if (unlikely (device->surface.num_pending_relocations == 0 || + device->general.num_pending_relocations == 0)) + { + i965_emit_base (device); + } + + i965_emit_composite (device, shader); + + return CAIRO_STATUS_SUCCESS; +} + +void +i965_clipped_vertices (i965_device_t *device, + struct i965_vbo *vbo, + cairo_region_t *clip_region) +{ + int i, num_rectangles, size; + cairo_status_t status; + + if (vbo->count == 0) + return; + + num_rectangles = cairo_region_num_rectangles (clip_region); + assert (num_rectangles); + + if (vbo->next || + vbo->count * device->vertex_size + device->vertex.used > device->vertex.size) + { + i965_finish_vertices (device); + + size = device->rectangle_size; + do { + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, i, &rect); + + if (unlikely (device->vertex.used + size > device->vertex.size || + device->batch.used + 64 > device->batch.size || + ! i965_shader_check_aperture (device->shader, device))) + { + status = i965_device_flush (device); + if (unlikely (status)) + longjmp (device->shader->unwind, status); + + status = i965_shader_commit (device->shader, device); + assert (status == CAIRO_STATUS_SUCCESS); + } + + i965_emit_relocation (device, &device->batch, + vbo->bo, 0, + I915_GEM_DOMAIN_VERTEX, 0, + device->batch.used + 8); + + OUT_BATCH (BRW_3DSTATE_VERTEX_BUFFERS | 3); + OUT_BATCH ((0 << VB0_BUFFER_INDEX_SHIFT) | + VB0_VERTEXDATA | + (device->vertex_size << VB0_BUFFER_PITCH_SHIFT)); + OUT_BATCH (vbo->bo->offset); + OUT_BATCH (0); + OUT_BATCH (0); + + /* XXX scissor? */ + OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2); + OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x)); + OUT_BATCH (DRAW_YMAX (rect.y + rect.height) | + DRAW_XMAX (rect.x + rect.width)); + OUT_BATCH (0x00000000); /* yorigin, xorigin */ + + OUT_BATCH (BRW_3DPRIMITIVE | + BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL | + (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) | + (0 << 9) | + 4); + OUT_BATCH (vbo->count); /* vertex count per instance */ + OUT_BATCH (0); /* start vertex offset */ + OUT_BATCH (1); /* single instance */ + OUT_BATCH (0); + OUT_BATCH (0); + } + } while ((vbo = vbo->next) != NULL); + assert (device->last_vertex_size == 0); + } else { + int vertex_start, vertex_count; + void *ptr; + + vertex_start = device->vertex.committed / device->vertex_size; + vertex_count = vbo->count; + + size = vertex_count * device->vertex_size; + ptr = intel_bo_map (&device->intel, vbo->bo); + memcpy (device->vertex.data + device->vertex.used, ptr, size); + device->vertex.committed = device->vertex.used += size; + + for (i = 0; i < num_rectangles; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, i, &rect); + + /* XXX scissor? */ + OUT_BATCH (BRW_3DSTATE_DRAWING_RECTANGLE | 2); + OUT_BATCH (DRAW_YMIN (rect.y) | DRAW_XMIN (rect.x)); + OUT_BATCH (DRAW_YMAX (rect.y + rect.height) | + DRAW_XMAX (rect.x + rect.width)); + OUT_BATCH (0x00000000); /* yorigin, xorigin */ + + OUT_BATCH (BRW_3DPRIMITIVE | + BRW_3DPRIMITIVE_VERTEX_SEQUENTIAL | + (_3DPRIM_RECTLIST << BRW_3DPRIMITIVE_TOPOLOGY_SHIFT) | + (0 << 9) | + 4); + OUT_BATCH (vertex_count); /* vertex count per instance */ + OUT_BATCH (vertex_start); /* start vertex offset */ + OUT_BATCH (1); /* single instance */ + OUT_BATCH (0); + OUT_BATCH (0); + } + } + + device->draw_rectangle = 0; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i965-spans.c b/gfx/cairo/cairo/src/drm/cairo-drm-i965-spans.c new file mode 100644 index 000000000000..4d1f491fa22b --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i965-spans.c @@ -0,0 +1,407 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 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 + */ + +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" +#include "cairo-drm-i965-private.h" + +/* Operates in either immediate or retained mode. + * When given a clip region we record the sequence of vbo and then + * replay them for each clip rectangle, otherwise we simply emit + * the vbo straight into the command stream. + */ + +typedef struct _i965_spans i965_spans_t; + +typedef float * +(*i965_get_rectangle_func_t) (i965_spans_t *spans); + +struct _i965_spans { + cairo_span_renderer_t renderer; + + i965_device_t *device; + + int xmin, xmax; + cairo_bool_t is_bounded; + const cairo_rectangle_int_t *extents; + + i965_get_rectangle_func_t get_rectangle; + i965_shader_t shader; + + cairo_region_t *clip_region; + + struct i965_vbo head, *tail; + + unsigned int vbo_offset; + float *vbo_base; +}; + +static float * +i965_spans_emit_rectangle (i965_spans_t *spans) +{ + return i965_add_rectangle (spans->device); +} + +static float * +i965_spans_accumulate_rectangle (i965_spans_t *spans) +{ + float *vertices; + uint32_t size; + + size = spans->device->rectangle_size; + if (unlikely (spans->vbo_offset + size > I965_VERTEX_SIZE)) { + struct i965_vbo *vbo; + + vbo = _cairo_malloc (sizeof (struct i965_vbo)); + if (unlikely (vbo == NULL)) { + /* throw error! */ + } + + spans->tail->next = vbo; + spans->tail = vbo; + + vbo->next = NULL; + vbo->bo = intel_bo_create (&spans->device->intel, + I965_VERTEX_SIZE, I965_VERTEX_SIZE, + FALSE, I915_TILING_NONE, 0); + vbo->count = 0; + + spans->vbo_offset = 0; + spans->vbo_base = intel_bo_map (&spans->device->intel, vbo->bo); + } + + vertices = spans->vbo_base + spans->vbo_offset; + spans->vbo_offset += size; + spans->tail->count += 3; + + return vertices; +} + +static void +i965_span_rectangle (i965_spans_t *spans, + int x0, int x1, int y0, int y1, + int alpha) +{ + float *vertices; + float a = alpha / 255.; + + vertices = spans->get_rectangle (spans); + + *vertices++ = x1; + *vertices++ = y1; + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y1; + *vertices++ = a; + + *vertices++ = x0; + *vertices++ = y0; + *vertices++ = a; +} + +static cairo_status_t +i965_bounded_spans_mono (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i965_spans_t *spans = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (half[0].coverage >= 128) { + i965_span_rectangle (spans, + half[0].x, half[1].x, + y, y + height, + 255); + } + half++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_bounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i965_spans_t *spans = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (half[0].coverage) { + i965_span_rectangle (spans, + half[0].x, half[1].x, + y, y + height, + half[0].coverage); + } + half++; + } while (--num_spans > 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_unbounded_spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i965_spans_t *spans = abstract_renderer; + + if (num_spans == 0) { + i965_span_rectangle (spans, + spans->xmin, spans->xmax, + y, y + height, + 0); + return CAIRO_STATUS_SUCCESS; + } + + if (half[0].x != spans->xmin) { + i965_span_rectangle (spans, + spans->xmin, half[0].x, + y, y + height, + 0); + } + + do { + i965_span_rectangle (spans, + half[0].x, half[1].x, + y, y + height, + half[0].coverage); + half++; + } while (--num_spans > 1); + + if (half[0].x != spans->xmax) { + i965_span_rectangle (spans, + half[0].x, spans->xmax, + y, y + height, + 0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_unbounded_spans_mono (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *half, + unsigned num_spans) +{ + i965_spans_t *spans = abstract_renderer; + + if (num_spans == 0) { + i965_span_rectangle (spans, + spans->xmin, spans->xmax, + y, y + height, + 0); + return CAIRO_STATUS_SUCCESS; + } + + if (half[0].x != spans->xmin) { + i965_span_rectangle (spans, + spans->xmin, half[0].x, + y, y + height, + 0); + } + + do { + int alpha = 0; + if (half[0].coverage >= 128) + alpha = 255; + i965_span_rectangle (spans, + half[0].x, half[1].x, + y, y + height, + alpha); + half++; + } while (--num_spans > 1); + + if (half[0].x != spans->xmax) { + i965_span_rectangle (spans, + half[0].x, spans->xmax, + y, y + height, + 0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +i965_spans_init (i965_spans_t *spans, + i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_status_t status; + + spans->device = i965_device (dst); + i965_shader_init (&spans->shader, dst, op); + + spans->is_bounded = extents->is_bounded; + if (extents->is_bounded) { + if (antialias == CAIRO_ANTIALIAS_NONE) + spans->renderer.render_rows = i965_bounded_spans_mono; + else + spans->renderer.render_rows = i965_bounded_spans; + + spans->extents = &extents->bounded; + } else { + if (antialias == CAIRO_ANTIALIAS_NONE) + spans->renderer.render_rows = i965_unbounded_spans_mono; + else + spans->renderer.render_rows = i965_unbounded_spans; + + spans->extents = &extents->unbounded; + } + spans->xmin = spans->extents->x; + spans->xmax = spans->extents->x + spans->extents->width; + + spans->clip_region = NULL; + if (clip != NULL) { + cairo_region_t *clip_region = NULL; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED); + + if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) + clip_region = NULL; + + spans->clip_region = clip_region; + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&spans->shader, clip); + } + + spans->head.next = NULL; + spans->head.bo = NULL; + spans->head.count = 0; + spans->tail = &spans->head; + + if (spans->clip_region == NULL) { + spans->get_rectangle = i965_spans_emit_rectangle; + } else { + spans->get_rectangle = i965_spans_accumulate_rectangle; + spans->head.bo = intel_bo_create (&spans->device->intel, + I965_VERTEX_SIZE, I965_VERTEX_SIZE, + FALSE, I915_TILING_NONE, 0); + if (unlikely (spans->head.bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + spans->vbo_base = intel_bo_map (&spans->device->intel, spans->head.bo); + } + spans->vbo_offset = 0; + + return i965_shader_acquire_pattern (&spans->shader, + &spans->shader.source, + pattern, &extents->bounded); +} + +static void +i965_spans_fini (i965_spans_t *spans) +{ + i965_shader_fini (&spans->shader); + + if (spans->head.bo != NULL) { + struct i965_vbo *vbo, *next; + + intel_bo_destroy (&spans->device->intel, spans->head.bo); + for (vbo = spans->head.next; vbo != NULL; vbo = next) { + next = vbo->next; + intel_bo_destroy (&spans->device->intel, vbo->bo); + free (vbo); + } + } +} + +cairo_status_t +i965_clip_and_composite_spans (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_antialias_t antialias, + i965_spans_func_t draw_func, + void *draw_closure, + const cairo_composite_rectangles_t*extents, + cairo_clip_t *clip) +{ + i965_spans_t spans; + i965_device_t *device; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + status = i965_spans_init (&spans, dst, op, pattern, antialias, clip, extents); + if (unlikely (status)) + return status; + + spans.shader.mask.base.content = CAIRO_CONTENT_ALPHA; + spans.shader.mask.type.fragment = FS_SPANS; + spans.shader.mask.type.vertex = VS_SPANS; + spans.shader.mask.type.pattern = PATTERN_BASE; + + status = cairo_device_acquire (dst->intel.drm.base.device); + if (unlikely (status)) + goto CLEANUP_SPANS; + + device = i965_device (dst); + status = i965_shader_commit (&spans.shader, device); + if (unlikely (status)) + goto CLEANUP_DEVICE; + + status = draw_func (draw_closure, &spans.renderer, spans.extents); + if (spans.clip_region != NULL && status == CAIRO_STATUS_SUCCESS) + i965_clipped_vertices (device, &spans.head, spans.clip_region); + + CLEANUP_DEVICE: + cairo_device_release (dst->intel.drm.base.device); + CLEANUP_SPANS: + i965_spans_fini (&spans); + + return status; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-i965-surface.c b/gfx/cairo/cairo/src/drm/cairo-drm-i965-surface.c new file mode 100644 index 000000000000..9d29e4753b31 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-i965-surface.c @@ -0,0 +1,1926 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Kristian Høgsberg + * Copyright © 2009 Chris Wilson + * Copyright © 2009 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 Kristian Høgsberg. + * + * Based on the xf86-intel-driver i965 render acceleration code, + * authored by: + * Wang Zhenyu + * Eric Anholt + * Carl Worth + * Keith Packard + */ + +/* XXX + * + * FIXME: Use brw_PLN for [DevCTG-B+] + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-intel-private.h" +#include "cairo-drm-intel-command-private.h" +#include "cairo-drm-intel-ioctl-private.h" +#include "cairo-drm-i965-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-region-private.h" +#include "cairo-surface-offset-private.h" + +#include +#include + +#define I965_MAX_SIZE 8192 + +static const cairo_surface_backend_t i965_surface_backend; + +static void +i965_stream_init (i965_stream_t *stream, + uint8_t *data, uint32_t size, + struct i965_pending_relocation *pending, int max_pending, + struct drm_i915_gem_relocation_entry *relocations, int max_relocations) + +{ + stream->used = stream->committed = 0; + stream->data = data; + stream->size = size; + stream->serial = 1; + + stream->num_pending_relocations = 0; + stream->max_pending_relocations = max_pending; + stream->pending_relocations = pending; + + stream->num_relocations = 0; + stream->max_relocations = max_relocations; + stream->relocations = relocations; +} + +static void +i965_add_relocation (i965_device_t *device, + intel_bo_t *bo, + uint32_t read_domains, + uint32_t write_domain) +{ + if (bo->exec == NULL) { + int i; + + device->exec.gtt_size += bo->base.size; + + i = device->exec.count++; + assert (i < ARRAY_LENGTH (device->exec.exec)); + + device->exec.exec[i].handle = bo->base.handle; + device->exec.exec[i].relocation_count = 0; + device->exec.exec[i].relocs_ptr = 0; + device->exec.exec[i].alignment = 0; + device->exec.exec[i].offset = 0; + device->exec.exec[i].flags = 0; + device->exec.exec[i].rsvd1 = 0; + device->exec.exec[i].rsvd2 = 0; + + device->exec.bo[i] = intel_bo_reference (bo); + bo->exec = &device->exec.exec[i]; + } + + if (cairo_list_is_empty (&bo->link)) + cairo_list_add_tail (&device->flush, &bo->link); + + assert (write_domain == 0 || bo->batch_write_domain == 0 || bo->batch_write_domain == write_domain); + bo->batch_read_domains |= read_domains; + bo->batch_write_domain |= write_domain; +} + +void +i965_emit_relocation (i965_device_t *device, + i965_stream_t *stream, + intel_bo_t *target, + uint32_t target_offset, + uint32_t read_domains, + uint32_t write_domain, + uint32_t offset) +{ + int n; + + assert (target_offset < target->base.size); + + i965_add_relocation (device, target, read_domains, write_domain); + + n = stream->num_relocations++; + assert (n < stream->max_relocations); + + stream->relocations[n].offset = offset; + stream->relocations[n].delta = target_offset; + stream->relocations[n].target_handle = target->base.handle; + stream->relocations[n].read_domains = read_domains; + stream->relocations[n].write_domain = write_domain; + stream->relocations[n].presumed_offset = target->offset; +} + +static void +i965_stream_reset (i965_stream_t *stream) +{ + stream->used = stream->committed = 0; + stream->num_relocations = 0; + stream->num_pending_relocations = 0; + if (++stream->serial == 0) + stream->serial = 1; +} + +void +i965_stream_commit (i965_device_t *device, + i965_stream_t *stream) +{ + intel_bo_t *bo; + int n; + + assert (stream->used); + + bo = intel_bo_create (&device->intel, + stream->used, stream->used, + FALSE, I915_TILING_NONE, 0); + + /* apply pending relocations */ + for (n = 0; n < stream->num_pending_relocations; n++) { + struct i965_pending_relocation *p = &stream->pending_relocations[n]; + + i965_emit_relocation (device, &device->batch, bo, + p->delta, + p->read_domains, + p->write_domain, + p->offset); + if (bo->offset) + *(uint32_t *) (device->batch.data + p->offset) = bo->offset + p->delta; + } + + intel_bo_write (&device->intel, bo, 0, stream->used, stream->data); + + if (stream->num_relocations) { + assert (bo->exec != NULL); + bo->exec->relocs_ptr = (uintptr_t) stream->relocations; + bo->exec->relocation_count = stream->num_relocations; + } + + intel_bo_destroy (&device->intel, bo); + + i965_stream_reset (stream); +} + +static void +sf_states_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->sf_states, entry); + _cairo_freelist_free (&device->sf_freelist, entry); +} + +static void +cc_offsets_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->cc_states, entry); + _cairo_freelist_free (&device->cc_freelist, entry); +} + +static void +wm_kernels_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->wm_kernels, entry); + _cairo_freelist_free (&device->wm_kernel_freelist, entry); +} + +static void +wm_states_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->wm_states, entry); + _cairo_freelist_free (&device->wm_state_freelist, entry); +} + +static void +wm_bindings_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->wm_bindings, entry); + _cairo_freelist_free (&device->wm_binding_freelist, entry); +} + +static void +samplers_pluck (void *entry, void *closure) +{ + i965_device_t *device = closure; + + _cairo_hash_table_remove (device->samplers, entry); + _cairo_freelist_free (&device->sampler_freelist, entry); +} + +void +i965_general_state_reset (i965_device_t *device) +{ + _cairo_hash_table_foreach (device->sf_states, + sf_states_pluck, + device); + + _cairo_hash_table_foreach (device->cc_states, + cc_offsets_pluck, + device); + + _cairo_hash_table_foreach (device->wm_kernels, + wm_kernels_pluck, + device); + + _cairo_hash_table_foreach (device->wm_states, + wm_states_pluck, + device); + + _cairo_hash_table_foreach (device->wm_bindings, + wm_bindings_pluck, + device); + + _cairo_hash_table_foreach (device->samplers, + samplers_pluck, + device); + + device->vs_offset = (uint32_t) -1; + device->border_color_offset = (uint32_t) -1; + + if (device->general_state != NULL) { + intel_bo_destroy (&device->intel, device->general_state); + device->general_state = NULL; + } +} + +static void +i965_device_reset (i965_device_t *device) +{ + device->exec.count = 0; + device->exec.gtt_size = I965_VERTEX_SIZE + + I965_SURFACE_SIZE + + I965_GENERAL_SIZE + + I965_BATCH_SIZE; + + device->sf_state.entry.hash = (uint32_t) -1; + device->wm_state.entry.hash = (uint32_t) -1; + device->wm_binding.entry.hash = (uint32_t) -1; + device->cc_state.entry.hash = (uint32_t) -1; + + device->target = NULL; + device->source = NULL; + device->mask = NULL; + device->clip = NULL; + + device->draw_rectangle = (uint32_t) -1; + + device->vertex_type = (uint32_t) -1; + device->vertex_size = 0; + device->rectangle_size = 0; + device->last_vertex_size = 0; + + device->constants = NULL; + device->constants_size = 0; + + device->have_urb_fences = FALSE; +} + +static cairo_status_t +i965_exec (i965_device_t *device, uint32_t offset) +{ + struct drm_i915_gem_execbuffer2 execbuf; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int ret, i; + + execbuf.buffers_ptr = (uintptr_t) device->exec.exec; + execbuf.buffer_count = device->exec.count; + execbuf.batch_start_offset = offset; + execbuf.batch_len = device->batch.used; + execbuf.DR1 = 0; + execbuf.DR4 = 0; + execbuf.num_cliprects = 0; + execbuf.cliprects_ptr = 0; + execbuf.flags = I915_GEM_3D_PIPELINE; + execbuf.rsvd1 = 0; + execbuf.rsvd2 = 0; + +#if 0 + printf ("exec: offset=%d, length=%d, buffers=%d\n", + offset, device->batch.used, device->exec.count); + intel_dump_batchbuffer ((uint32_t *) device->batch.data, + device->batch.used, + device->intel.base.chip_id); +#endif + + ret = 0; + do { + ret = ioctl (device->intel.base.fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + } while (ret != 0 && errno == EINTR); + if (unlikely (ret)) { + if (errno == ENOMEM) + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + else + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + + fprintf (stderr, "Batch submission failed: %d\n", errno); + fprintf (stderr, " gtt size: %zd/%zd\n", + device->exec.gtt_size, device->intel.gtt_avail_size); + + fprintf (stderr, " %d buffers:\n", + device->exec.count); + for (i = 0; i < device->exec.count; i++) { + fprintf (stderr, " exec[%d] = %d\n", + i, device->exec.bo[i]->base.size); + } + + intel_dump_batchbuffer ((uint32_t *) device->batch.data, + device->batch.used, + device->intel.base.chip_id); + } + + /* XXX any write target within the batch should now be in error */ + for (i = 0; i < device->exec.count; i++) { + intel_bo_t *bo = device->exec.bo[i]; + cairo_bool_t ret; + + bo->offset = device->exec.exec[i].offset; + bo->exec = NULL; + bo->batch_read_domains = 0; + bo->batch_write_domain = 0; + + if (bo->virtual) + intel_bo_unmap (bo); + bo->cpu = FALSE; + + if (bo->purgeable) + ret = intel_bo_madvise (&device->intel, bo, I915_MADV_DONTNEED); + /* ignore immediate notification of purging */ + + cairo_list_del (&bo->cache_list); + cairo_list_init (&bo->link); + intel_bo_destroy (&device->intel, bo); + } + cairo_list_init (&device->flush); + + device->exec.count = 0; + + return status; +} + +static inline uint32_t +next_bo_size (uint32_t v) +{ + v = (v + 8191) / 8192; + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + return v * 8192; +} + +static void +_copy_to_bo_and_apply_relocations (i965_device_t *device, + intel_bo_t *bo, + i965_stream_t *stream, + uint32_t offset) +{ + int n; + + intel_bo_write (&device->intel, bo, + offset, stream->used, + stream->data); + + for (n = 0; n < stream->num_pending_relocations; n++) { + struct i965_pending_relocation *p = &stream->pending_relocations[n]; + + i965_emit_relocation (device, &device->batch, bo, + p->delta + offset, + p->read_domains, + p->write_domain, + p->offset); + + if (bo->offset) { + *(uint32_t *) (device->batch.data + p->offset) = + bo->offset + p->delta + offset; + } + } +} + +cairo_status_t +i965_device_flush (i965_device_t *device) +{ + cairo_status_t status; + uint32_t aligned, max; + intel_bo_t *bo; + int n; + + if (device->batch.used == 0) + return CAIRO_STATUS_SUCCESS; + + i965_flush_vertices (device); + + OUT_BATCH (MI_BATCH_BUFFER_END); + /* Emit a padding dword if we aren't going to be quad-word aligned. */ + if (device->batch.used & 4) + OUT_BATCH (MI_NOOP); + +#if 0 + printf ("device flush: vertex=%d, constant=%d, surface=%d, general=%d, batch=%d\n", + device->vertex.used, + device->constant.used, + device->surface.used, + device->general.used, + device->batch.used); +#endif + + /* can we pack the surface state into the tail of the general state? */ + if (device->general.used == device->general.committed) { + if (device->general.used) { + assert (device->general.num_pending_relocations == 1); + assert (device->general_state != NULL); + i965_emit_relocation (device, &device->batch, + device->general_state, + device->general.pending_relocations[0].delta, + device->general.pending_relocations[0].read_domains, + device->general.pending_relocations[0].write_domain, + device->general.pending_relocations[0].offset); + + if (device->general_state->offset) { + *(uint32_t *) (device->batch.data + + device->general.pending_relocations[0].offset) = + device->general_state->offset + + device->general.pending_relocations[0].delta; + } + } + } else { + assert (device->general.num_pending_relocations == 1); + if (device->general_state != NULL) { + intel_bo_destroy (&device->intel, device->general_state); + device->general_state = NULL; + } + + bo = intel_bo_create (&device->intel, + device->general.used, + device->general.used, + FALSE, I915_TILING_NONE, 0); + if (unlikely (bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + aligned = (device->general.used + 31) & -32; + if (device->surface.used && + aligned + device->surface.used <= bo->base.size) + { + _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0); + _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned); + + if (device->surface.num_relocations) { + for (n = 0; n < device->surface.num_relocations; n++) + device->surface.relocations[n].offset += aligned; + + assert (bo->exec != NULL); + bo->exec->relocs_ptr = (uintptr_t) device->surface.relocations; + bo->exec->relocation_count = device->surface.num_relocations; + } + + i965_stream_reset (&device->surface); + } + else + { + _copy_to_bo_and_apply_relocations (device, bo, &device->general, 0); + } + + /* Note we don't reset the general state, just mark what data we've committed. */ + device->general.committed = device->general.used; + device->general_state = bo; + } + device->general.num_pending_relocations = 0; + + /* Combine vertex+constant+surface+batch streams? */ + max = aligned = device->vertex.used; + if (device->surface.used) { + aligned = (aligned + 63) & -64; + aligned += device->surface.used; + if (device->surface.used > max) + max = device->surface.used; + } + aligned = (aligned + 63) & -64; + aligned += device->batch.used; + if (device->batch.used > max) + max = device->batch.used; + if (aligned <= next_bo_size (max)) { + int batch_num_relocations; + + if (aligned <= 8192) + max = aligned; + + bo = intel_bo_create (&device->intel, + max, max, + FALSE, I915_TILING_NONE, 0); + if (unlikely (bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + assert (aligned <= bo->base.size); + + if (device->vertex.used) + _copy_to_bo_and_apply_relocations (device, bo, &device->vertex, 0); + + aligned = device->vertex.used; + + batch_num_relocations = device->batch.num_relocations; + if (device->surface.used) { + aligned = (aligned + 63) & -64; + _copy_to_bo_and_apply_relocations (device, bo, &device->surface, aligned); + + batch_num_relocations = device->batch.num_relocations; + if (device->surface.num_relocations) { + assert (device->batch.num_relocations + device->surface.num_relocations < device->batch.max_relocations); + + memcpy (device->batch.relocations + device->batch.num_relocations, + device->surface.relocations, + sizeof (device->surface.relocations[0]) * device->surface.num_relocations); + + for (n = 0; n < device->surface.num_relocations; n++) + device->batch.relocations[device->batch.num_relocations + n].offset += aligned; + + device->batch.num_relocations += device->surface.num_relocations; + } + + aligned += device->surface.used; + } + + aligned = (aligned + 63) & -64; + intel_bo_write (&device->intel, bo, + aligned, device->batch.used, + device->batch.data); + + for (n = 0; n < batch_num_relocations; n++) + device->batch.relocations[n].offset += aligned; + + if (device->exec.bo[device->exec.count-1] == bo) { + assert (bo->exec == &device->exec.exec[device->exec.count-1]); + + bo->exec->relocation_count = device->batch.num_relocations; + bo->exec->relocs_ptr = (uintptr_t) device->batch.relocations; + intel_bo_destroy (&device->intel, bo); + } else { + assert (bo->exec == NULL); + + n = device->exec.count++; + device->exec.exec[n].handle = bo->base.handle; + device->exec.exec[n].relocation_count = device->batch.num_relocations; + device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations; + device->exec.exec[n].alignment = 0; + device->exec.exec[n].offset = 0; + device->exec.exec[n].flags = 0; + device->exec.exec[n].rsvd1 = 0; + device->exec.exec[n].rsvd2 = 0; + + /* transfer ownership to the exec */ + device->exec.bo[n] = bo; + } + } else { + i965_stream_commit (device, &device->vertex); + if (device->surface.used) + i965_stream_commit (device, &device->surface); + + bo = intel_bo_create (&device->intel, + device->batch.used, device->batch.used, + FALSE, I915_TILING_NONE, 0); + if (unlikely (bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + intel_bo_write (&device->intel, bo, + 0, device->batch.used, + device->batch.data); + + n = device->exec.count++; + device->exec.exec[n].handle = bo->base.handle; + device->exec.exec[n].relocation_count = device->batch.num_relocations; + device->exec.exec[n].relocs_ptr = (uintptr_t) device->batch.relocations; + device->exec.exec[n].alignment = 0; + device->exec.exec[n].offset = 0; + device->exec.exec[n].flags = 0; + device->exec.exec[n].rsvd1 = 0; + device->exec.exec[n].rsvd2 = 0; + + /* transfer ownership to the exec */ + device->exec.bo[n] = bo; + aligned = 0; + } + + status = i965_exec (device, aligned); + + i965_stream_reset (&device->vertex); + i965_stream_reset (&device->surface); + i965_stream_reset (&device->batch); + + intel_glyph_cache_unpin (&device->intel); + intel_snapshot_cache_thaw (&device->intel); + + i965_device_reset (device); + + return status; +} + +static cairo_surface_t * +i965_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, int height) +{ + i965_surface_t *other; + cairo_format_t format; + + if (width > 8192 || height > 8192) + return NULL; + + other = abstract_other; + if (content == other->intel.drm.base.content) + format = other->intel.drm.format; + else + format = _cairo_format_from_content (content); + + return i965_surface_create_internal ((cairo_drm_device_t *) other->intel.drm.base.device, + format, + width, height, + I965_TILING_DEFAULT, TRUE); +} + +static cairo_status_t +i965_surface_finish (void *abstract_surface) +{ + i965_surface_t *surface = abstract_surface; + + return intel_surface_finish (&surface->intel); +} + +static cairo_status_t +i965_surface_flush (void *abstract_surface, unsigned flags) +{ + i965_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->intel.drm.fallback != NULL) + return intel_surface_flush (abstract_surface); + + /* Forgo flushing on finish as the user cannot access the surface directly. */ + if (! surface->intel.drm.base.finished && + to_intel_bo (surface->intel.drm.bo)->exec != NULL) + { + status = cairo_device_acquire (surface->intel.drm.base.device); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + i965_device_t *device; + + device = i965_device (surface); + status = i965_device_flush (device); + cairo_device_release (&device->intel.base.base); + } + } + + return status; +} + +/* rasterisation */ + +static cairo_status_t +_composite_boxes_spans (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents) +{ + cairo_boxes_t *boxes = closure; + cairo_rectangular_scan_converter_t converter; + struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + + _cairo_rectangular_scan_converter_init (&converter, extents); + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + int i; + + for (i = 0; i < chunk->count; i++) { + status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); + if (unlikely (status)) + goto CLEANUP; + } + } + + status = converter.base.generate (&converter.base, renderer); + + CLEANUP: + converter.base.destroy (&converter.base); + return status; +} + +cairo_status_t +i965_fixup_unbounded (i965_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + i965_shader_t shader; + i965_device_t *device; + cairo_status_t status; + + i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR); + + if (clip != NULL) { + cairo_region_t *clip_region = NULL; + + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + assert (clip_region == NULL); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&shader, clip); + } else { + if (extents->bounded.width == extents->unbounded.width && + extents->bounded.height == extents->unbounded.height) + { + return CAIRO_STATUS_SUCCESS; + } + } + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_clear.base, + &extents->unbounded); + if (unlikely (status)) { + i965_shader_fini (&shader); + return status; + } + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + return status; + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) { + goto BAIL; + } + + if (extents->bounded.width == 0 || extents->bounded.height == 0) { + i965_shader_add_rectangle (&shader, + extents->unbounded.x, + extents->unbounded.y, + extents->unbounded.width, + extents->unbounded.height); + } else { /* top */ + if (extents->bounded.y != extents->unbounded.y) { + cairo_rectangle_int_t rect; + + rect.x = extents->unbounded.x; + rect.y = extents->unbounded.y; + rect.width = extents->unbounded.width; + rect.height = extents->bounded.y - rect.y; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + cairo_rectangle_int_t rect; + + rect.x = extents->unbounded.x; + rect.y = extents->bounded.y; + rect.width = extents->bounded.x - extents->unbounded.x; + rect.height = extents->bounded.height; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + cairo_rectangle_int_t rect; + + rect.x = extents->bounded.x + extents->bounded.width; + rect.y = extents->bounded.y; + rect.width = extents->unbounded.x + extents->unbounded.width - rect.x; + rect.height = extents->bounded.height; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + cairo_rectangle_int_t rect; + + rect.x = extents->unbounded.x; + rect.y = extents->bounded.y + extents->bounded.height; + rect.width = extents->unbounded.width; + rect.height = extents->unbounded.y + extents->unbounded.height - rect.y; + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + } + + i965_shader_fini (&shader); + BAIL: + cairo_device_release (&device->intel.base.base); + return status; +} + +static cairo_status_t +i965_fixup_unbounded_boxes (i965_surface_t *dst, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip, + cairo_boxes_t *boxes) +{ + cairo_boxes_t clear; + cairo_box_t box; + cairo_region_t *clip_region = NULL; + cairo_status_t status; + struct _cairo_boxes_chunk *chunk; + i965_shader_t shader; + int i; + + if (boxes->num_boxes <= 1) + return i965_fixup_unbounded (dst, extents, clip); + + i965_shader_init (&shader, dst, CAIRO_OPERATOR_CLEAR); + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + i965_shader_set_clip (&shader, clip); + } + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + &_cairo_pattern_clear.base, + &extents->unbounded); + if (unlikely (status)) { + i965_shader_fini (&shader); + return status; + } + + _cairo_boxes_init (&clear); + + box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); + box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); + box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); + box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); + + if (clip_region == NULL) { + cairo_boxes_t tmp; + + _cairo_boxes_init (&tmp); + + status = _cairo_boxes_add (&tmp, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + tmp.chunks.next = &boxes->chunks; + tmp.num_boxes += boxes->num_boxes; + + status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, + CAIRO_FILL_RULE_WINDING, + &clear); + + tmp.chunks.next = NULL; + } else { + pixman_box32_t *pbox; + + pbox = pixman_region32_rectangles (&clip_region->rgn, &i); + _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i); + + status = _cairo_boxes_add (&clear, &box); + assert (status == CAIRO_STATUS_SUCCESS); + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) { + status = _cairo_boxes_add (&clear, &chunk->base[i]); + if (unlikely (status)) { + _cairo_boxes_fini (&clear); + return status; + } + } + } + + status = _cairo_bentley_ottmann_tessellate_boxes (&clear, + CAIRO_FILL_RULE_WINDING, + &clear); + } + + if (likely (status == CAIRO_STATUS_SUCCESS && clear.num_boxes)) { + i965_device_t *device; + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + for (chunk = &clear.chunks; chunk != NULL; 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); + + i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); + } + } + +err_device: + cairo_device_release (&device->intel.base.base); +err_shader: + i965_shader_fini (&shader); + } + + _cairo_boxes_fini (&clear); + + return status; +} + +static cairo_status_t +_composite_boxes (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + cairo_bool_t need_clip_surface = FALSE; + cairo_region_t *clip_region = NULL; + const struct _cairo_boxes_chunk *chunk; + cairo_status_t status; + i965_shader_t shader; + i965_device_t *device; + int i; + + /* If the boxes are not pixel-aligned, we will need to compute a real mask */ + if (antialias != CAIRO_ANTIALIAS_NONE) { + if (! boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + i965_shader_init (&shader, dst, op); + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + pattern, + &extents->bounded); + if (unlikely (status)) + return status; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_surface) + i965_shader_set_clip (&shader, clip); + } + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_commit (&shader, i965_device (dst)); + if (unlikely (status)) + goto err_device; + + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + cairo_box_t *box = chunk->base; + for (i = 0; i < chunk->count; i++) { + int x1 = _cairo_fixed_integer_round (box[i].p1.x); + int y1 = _cairo_fixed_integer_round (box[i].p1.y); + int x2 = _cairo_fixed_integer_round (box[i].p2.x); + int y2 = _cairo_fixed_integer_round (box[i].p2.y); + + if (x2 > x1 && y2 > y1) + i965_shader_add_rectangle (&shader, x1, y1, x2 - x1, y2 - y1); + } + } + + if (! extents->is_bounded) + status = i965_fixup_unbounded_boxes (dst, extents, clip, boxes); + + err_device: + cairo_device_release (&device->intel.base.base); + err_shader: + i965_shader_fini (&shader); + + return status; +} + +static cairo_status_t +_clip_and_composite_boxes (i965_surface_t *dst, + cairo_operator_t op, + const cairo_pattern_t *src, + cairo_boxes_t *boxes, + cairo_antialias_t antialias, + const cairo_composite_rectangles_t *extents, + cairo_clip_t *clip) +{ + cairo_status_t status; + + if (boxes->num_boxes == 0) { + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + return i965_fixup_unbounded (dst, extents, clip); + } + + /* Use a fast path if the boxes are pixel aligned */ + status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* Otherwise render the boxes via an implicit mask and composite in the usual + * fashion. + */ + return i965_clip_and_composite_spans (dst, op, src, antialias, + _composite_boxes_spans, boxes, + extents, clip); +} + +static cairo_int_status_t +i965_surface_paint (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + cairo_boxes_t boxes; + cairo_box_t *clip_boxes = boxes.boxes_embedded; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded); + cairo_status_t status; + + /* XXX unsupported operators? use pixel shader blending, eventually */ + + status = _cairo_composite_rectangles_init_for_paint (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, + clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); + status = _clip_and_composite_boxes (dst, op, source, + &boxes, CAIRO_ANTIALIAS_DEFAULT, + &extents, clip); + if (clip_boxes != boxes.boxes_embedded) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i965_surface_mask (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + i965_shader_t shader; + i965_device_t *device; + cairo_clip_t local_clip; + cairo_region_t *clip_region = NULL; + cairo_bool_t need_clip_surface = FALSE; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, mask, clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL && extents.is_bounded) { + clip = _cairo_clip_init_copy (&local_clip, clip); + status = _cairo_clip_rectangle (clip, &extents.bounded); + if (unlikely (status)) { + _cairo_clip_fini (&local_clip); + return status; + } + + have_clip = TRUE; + } + + i965_shader_init (&shader, dst, op); + + status = i965_shader_acquire_pattern (&shader, + &shader.source, + source, + &extents.bounded); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_acquire_pattern (&shader, + &shader.mask, + mask, + &extents.bounded); + if (unlikely (status)) + goto err_shader; + + if (clip != NULL) { + status = _cairo_clip_get_region (clip, &clip_region); + assert (status == CAIRO_STATUS_SUCCESS || CAIRO_INT_STATUS_UNSUPPORTED); + need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; + if (need_clip_surface) + i965_shader_set_clip (&shader, clip); + } + + device = i965_device (dst); + status = cairo_device_acquire (&device->intel.base.base); + if (unlikely (status)) + goto err_shader; + + status = i965_shader_commit (&shader, device); + if (unlikely (status)) + goto err_device; + + if (clip_region != NULL) { + unsigned int n, num_rectangles; + + num_rectangles = cairo_region_num_rectangles (clip_region); + for (n = 0; n < num_rectangles; n++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, n, &rect); + + i965_shader_add_rectangle (&shader, + rect.x, rect.y, + rect.width, rect.height); + } + } else { + i965_shader_add_rectangle (&shader, + extents.bounded.x, + extents.bounded.y, + extents.bounded.width, + extents.bounded.height); + } + + if (! extents.is_bounded) + status = i965_fixup_unbounded (dst, &extents, clip); + + err_device: + cairo_device_release (&device->intel.base.base); + err_shader: + i965_shader_fini (&shader); + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +typedef struct { + cairo_polygon_t polygon; + cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; +} composite_polygon_info_t; + +static cairo_status_t +_composite_polygon_spans (void *closure, + cairo_span_renderer_t *renderer, + const cairo_rectangle_int_t *extents) +{ + composite_polygon_info_t *info = closure; + cairo_botor_scan_converter_t converter; + cairo_status_t status; + cairo_box_t box; + + box.p1.x = _cairo_fixed_from_int (extents->x); + box.p1.y = _cairo_fixed_from_int (extents->y); + box.p2.x = _cairo_fixed_from_int (extents->x + extents->width); + box.p2.y = _cairo_fixed_from_int (extents->y + extents->height); + + _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule); + + status = converter.base.add_polygon (&converter.base, &info->polygon); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = converter.base.generate (&converter.base, renderer); + + converter.base.destroy (&converter.base); + + return status; +} + +static cairo_int_status_t +i965_surface_stroke (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + 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, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + composite_polygon_info_t info; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, + path, stroke_style, ctm, + clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + if (_cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + stroke_style, + ctm, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto CLEANUP_BOXES; + } + + _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &info.polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (extents.is_bounded) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); + if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) + goto CLEANUP_POLYGON; + } + + if (info.polygon.num_edges == 0) { + if (! extents.is_bounded) + status = i965_fixup_unbounded (dst, &extents, clip); + } else { + info.fill_rule = CAIRO_FILL_RULE_WINDING; + info.antialias = antialias; + status = i965_clip_and_composite_spans (dst, op, source, antialias, + _composite_polygon_spans, &info, + &extents, clip); + } + +CLEANUP_POLYGON: + _cairo_polygon_fini (&info.polygon); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static cairo_int_status_t +i965_surface_fill (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t*source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + i965_surface_t *dst = abstract_dst; + cairo_composite_rectangles_t extents; + composite_polygon_info_t info; + cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; + cairo_clip_t local_clip; + cairo_bool_t have_clip = FALSE; + int num_boxes = ARRAY_LENGTH (boxes_stack); + cairo_status_t status; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + dst->intel.drm.width, + dst->intel.drm.height, + op, source, path, + clip); + if (unlikely (status)) + return status; + + if (clip != NULL && _cairo_clip_contains_extents (clip, &extents)) + clip = NULL; + + if (clip != NULL) { + clip = _cairo_clip_init_copy (&local_clip, clip); + have_clip = TRUE; + } + + status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); + if (unlikely (status)) { + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; + } + + assert (! _cairo_path_fixed_fill_is_empty (path)); + + if (_cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + _cairo_boxes_init (&boxes); + _cairo_boxes_limit (&boxes, clip_boxes, num_boxes); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + &boxes); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = _clip_and_composite_boxes (dst, op, source, + &boxes, antialias, + &extents, clip); + } + + _cairo_boxes_fini (&boxes); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto CLEANUP_BOXES; + } + + _cairo_polygon_init (&info.polygon, clip_boxes, num_boxes); + + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &info.polygon); + if (unlikely (status)) + goto CLEANUP_POLYGON; + + if (extents.is_bounded) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&info.polygon.extents, &rect); + if (! _cairo_rectangle_intersect (&extents.bounded, &rect)) + goto CLEANUP_POLYGON; + } + + if (info.polygon.num_edges == 0) { + if (! extents.is_bounded) + status = i965_fixup_unbounded (dst, &extents, clip); + } else { + info.fill_rule = fill_rule; + info.antialias = antialias; + status = i965_clip_and_composite_spans (dst, op, source, antialias, + _composite_polygon_spans, &info, + &extents, clip); + } + +CLEANUP_POLYGON: + _cairo_polygon_fini (&info.polygon); + +CLEANUP_BOXES: + if (clip_boxes != boxes_stack) + free (clip_boxes); + + if (have_clip) + _cairo_clip_fini (&local_clip); + + return status; +} + +static const cairo_surface_backend_t i965_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + i965_surface_create_similar, + i965_surface_finish, + + NULL, + intel_surface_acquire_source_image, + intel_surface_release_source_image, + + NULL, NULL, NULL, + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_drm_surface_get_font_options, + + i965_surface_flush, + NULL, /* mark_dirty */ + intel_scaled_font_fini, + intel_scaled_glyph_fini, + + i965_surface_paint, + i965_surface_mask, + i965_surface_stroke, + i965_surface_fill, + i965_surface_glyphs, +}; + +static void +i965_surface_init (i965_surface_t *surface, + cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + intel_surface_init (&surface->intel, &i965_surface_backend, device, + format, width, height); + surface->stream = 0; +} + +static inline int cairo_const +i965_tiling_stride (uint32_t tiling, int stride) +{ + if (tiling == I915_TILING_NONE) + return stride; + + return (stride + 127) & -128; +} + +static inline int cairo_const +i965_tiling_height (uint32_t tiling, int height) +{ + switch (tiling) { + default: + case I915_TILING_NONE: return (height + 1) & -2; + case I915_TILING_X: return (height + 7) & -8; + case I915_TILING_Y: return (height + 31) & -32; + } +} + +cairo_surface_t * +i965_surface_create_internal (cairo_drm_device_t *base_dev, + cairo_format_t format, + int width, int height, + uint32_t tiling, + cairo_bool_t gpu_target) +{ + i965_surface_t *surface; + cairo_status_t status_ignored; + + surface = _cairo_malloc (sizeof (i965_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + i965_surface_init (surface, base_dev, format, width, height); + + if (width && height) { + uint32_t size, stride; + intel_bo_t *bo; + + width = (width + 3) & -4; + stride = cairo_format_stride_for_width (surface->intel.drm.format, width); + stride = (stride + 63) & ~63; + stride = i965_tiling_stride (tiling, stride); + surface->intel.drm.stride = stride; + + height = i965_tiling_height (tiling, height); + assert (height <= I965_MAX_SIZE); + + size = stride * height; + bo = intel_bo_create (to_intel_device (&base_dev->base), + size, size, + gpu_target, tiling, stride); + if (bo == NULL) { + status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + bo->tiling = tiling; + bo->stride = stride; + surface->intel.drm.bo = &bo->base; + + assert (bo->base.size >= (size_t) stride*height); + } + + return &surface->intel.drm.base; +} + +static cairo_surface_t * +i965_surface_create (cairo_drm_device_t *device, + cairo_format_t format, int width, int height) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_INVALID: + default: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + return i965_surface_create_internal (device, format, width, height, + I965_TILING_DEFAULT, TRUE); +} + +static cairo_surface_t * +i965_surface_create_for_name (cairo_drm_device_t *base_dev, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + i965_device_t *device; + i965_surface_t *surface; + cairo_status_t status_ignored; + int min_stride; + + min_stride = cairo_format_stride_for_width (format, (width + 3) & -4); + if (stride < min_stride || stride & 63) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + + if (format == CAIRO_FORMAT_A1) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + case CAIRO_FORMAT_INVALID: + default: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + surface = _cairo_malloc (sizeof (i965_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + i965_surface_init (surface, base_dev, format, width, height); + + device = (i965_device_t *) base_dev; + surface->intel.drm.bo = &intel_bo_create_for_name (&device->intel, name)->base; + if (unlikely (surface->intel.drm.bo == NULL)) { + status_ignored = _cairo_drm_surface_finish (&surface->intel.drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + surface->intel.drm.stride = stride; + + return &surface->intel.drm.base; +} + +static cairo_status_t +i965_surface_enable_scan_out (void *abstract_surface) +{ + i965_surface_t *surface = abstract_surface; + intel_bo_t *bo; + + if (unlikely (surface->intel.drm.bo == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + + bo = to_intel_bo (surface->intel.drm.bo); + if (bo->tiling != I915_TILING_X) { + i965_device_t *device = i965_device (surface); + cairo_surface_pattern_t pattern; + cairo_surface_t *clone; + cairo_status_t status; + + clone = i965_surface_create_internal (&device->intel.base, + surface->intel.drm.base.content, + surface->intel.drm.width, + surface->intel.drm.height, + I915_TILING_X, + TRUE); + if (unlikely (clone->status)) + return clone->status; + + /* 2D blit? */ + _cairo_pattern_init_for_surface (&pattern, &surface->intel.drm.base); + pattern.base.filter = CAIRO_FILTER_NEAREST; + + status = _cairo_surface_paint (clone, + CAIRO_OPERATOR_SOURCE, + &pattern.base, + NULL); + + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (clone); + return status; + } + + /* swap buffer objects */ + surface->intel.drm.bo = ((cairo_drm_surface_t *) clone)->bo; + ((cairo_drm_surface_t *) clone)->bo = &bo->base; + bo = to_intel_bo (surface->intel.drm.bo); + + cairo_surface_destroy (clone); + } + + if (unlikely (bo->tiling == I915_TILING_Y)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */ + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_i965_device_flush (cairo_drm_device_t *device) +{ + cairo_status_t status; + + if (unlikely (device->base.finished)) + return CAIRO_STATUS_SUCCESS; + + status = cairo_device_acquire (&device->base); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = i965_device_flush ((i965_device_t *) device); + + cairo_device_release (&device->base); + + return status; +} + +static cairo_int_status_t +_i965_device_throttle (cairo_drm_device_t *device) +{ + cairo_status_t status; + + status = cairo_device_acquire (&device->base); + if (unlikely (status)) + return status; + + status = i965_device_flush ((i965_device_t *) device); + intel_throttle ((intel_device_t *) device); + + cairo_device_release (&device->base); + + return status; +} + +static void +_i965_device_destroy (void *base) +{ + i965_device_t *device = base; + + i965_device_reset (device); + i965_general_state_reset (device); + + _cairo_hash_table_destroy (device->sf_states); + _cairo_hash_table_destroy (device->samplers); + _cairo_hash_table_destroy (device->cc_states); + _cairo_hash_table_destroy (device->wm_kernels); + _cairo_hash_table_destroy (device->wm_states); + _cairo_hash_table_destroy (device->wm_bindings); + + _cairo_freelist_fini (&device->sf_freelist); + _cairo_freelist_fini (&device->cc_freelist); + _cairo_freelist_fini (&device->wm_kernel_freelist); + _cairo_freelist_fini (&device->wm_state_freelist); + _cairo_freelist_fini (&device->wm_binding_freelist); + _cairo_freelist_fini (&device->sampler_freelist); + + intel_device_fini (&device->intel); + free (device); +} + +static cairo_bool_t +hash_equal (const void *A, const void *B) +{ + const cairo_hash_entry_t *a = A, *b = B; + return a->hash == b->hash; +} + +cairo_drm_device_t * +_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id) +{ + i965_device_t *device; + uint64_t gtt_size; + cairo_status_t status; + + if (! intel_info (fd, >t_size)) + return NULL; + + device = _cairo_malloc (sizeof (i965_device_t)); + if (unlikely (device == NULL)) + return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + status = intel_device_init (&device->intel, fd); + if (unlikely (status)) + goto CLEANUP; + + device->is_g4x = IS_G4X (chip_id); + //device->is_g5x = IS_G5X (chip_id); + + device->intel.base.surface.create = i965_surface_create; + device->intel.base.surface.create_for_name = i965_surface_create_for_name; + device->intel.base.surface.create_from_cacheable_image = NULL; + device->intel.base.surface.enable_scan_out = i965_surface_enable_scan_out; + + device->intel.base.device.flush = _i965_device_flush; + device->intel.base.device.throttle = _i965_device_throttle; + device->intel.base.device.destroy = _i965_device_destroy; + + device->sf_states = _cairo_hash_table_create (i965_sf_state_equal); + if (unlikely (device->sf_states == NULL)) + goto CLEANUP_INTEL; + + _cairo_freelist_init (&device->sf_freelist, + sizeof (struct i965_sf_state)); + + + device->cc_states = _cairo_hash_table_create (i965_cc_state_equal); + if (unlikely (device->cc_states == NULL)) + goto CLEANUP_SF; + + _cairo_freelist_init (&device->cc_freelist, + sizeof (struct i965_cc_state)); + + + device->wm_kernels = _cairo_hash_table_create (hash_equal); + if (unlikely (device->wm_kernels == NULL)) + goto CLEANUP_CC; + + _cairo_freelist_init (&device->wm_kernel_freelist, + sizeof (struct i965_wm_kernel)); + + device->wm_states = _cairo_hash_table_create (i965_wm_state_equal); + if (unlikely (device->wm_states == NULL)) + goto CLEANUP_WM_KERNEL; + + _cairo_freelist_init (&device->wm_state_freelist, + sizeof (struct i965_wm_state)); + + + device->wm_bindings = _cairo_hash_table_create (i965_wm_binding_equal); + if (unlikely (device->wm_bindings == NULL)) + goto CLEANUP_WM_STATE; + + _cairo_freelist_init (&device->wm_binding_freelist, + sizeof (struct i965_wm_binding)); + + device->samplers = _cairo_hash_table_create (hash_equal); + if (unlikely (device->samplers == NULL)) + goto CLEANUP_WM_BINDING; + + _cairo_freelist_init (&device->sampler_freelist, + sizeof (struct i965_sampler)); + + i965_stream_init (&device->batch, + device->batch_base, sizeof (device->batch_base), + NULL, 0, + device->batch_relocations, + ARRAY_LENGTH (device->batch_relocations)); + + i965_stream_init (&device->surface, + device->surface_base, sizeof (device->surface_base), + device->surface_pending_relocations, + ARRAY_LENGTH (device->surface_pending_relocations), + device->surface_relocations, + ARRAY_LENGTH (device->surface_relocations)); + + i965_stream_init (&device->general, + device->general_base, sizeof (device->general_base), + device->general_pending_relocations, + ARRAY_LENGTH (device->general_pending_relocations), + NULL, 0); + + i965_stream_init (&device->vertex, + device->vertex_base, sizeof (device->vertex_base), + device->vertex_pending_relocations, + ARRAY_LENGTH (device->vertex_pending_relocations), + NULL, 0); + + cairo_list_init (&device->flush); + i965_device_reset (device); + device->vs_offset = (uint32_t) -1; + device->border_color_offset = (uint32_t) -1; + device->general_state = NULL; + + return _cairo_drm_device_init (&device->intel.base, + fd, dev, vendor_id, chip_id, + I965_MAX_SIZE); + + CLEANUP_WM_BINDING: + _cairo_hash_table_destroy (device->wm_bindings); + CLEANUP_WM_STATE: + _cairo_hash_table_destroy (device->wm_states); + CLEANUP_WM_KERNEL: + _cairo_hash_table_destroy (device->wm_kernels); + CLEANUP_CC: + _cairo_hash_table_destroy (device->cc_states); + CLEANUP_SF: + _cairo_hash_table_destroy (device->sf_states); + CLEANUP_INTEL: + intel_device_fini (&device->intel); + CLEANUP: + free (device); + return (cairo_drm_device_t *) _cairo_device_create_in_error (status); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-defines.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-defines.h new file mode 100644 index 000000000000..b2be36f18396 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-defines.h @@ -0,0 +1,824 @@ +/************************************************************************** + * + * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef CAIRO_DRM_INTEL_BRW_DEFINES_H +#define CAIRO_DRM_INTEL_BRW_DEFINES_H + +/* 3D state: */ +#define _3DOP_3DSTATE_PIPELINED 0x0 +#define _3DOP_3DSTATE_NONPIPELINED 0x1 +#define _3DOP_3DCONTROL 0x2 +#define _3DOP_3DPRIMITIVE 0x3 + +#define _3DSTATE_PIPELINED_POINTERS 0x00 +#define _3DSTATE_BINDING_TABLE_POINTERS 0x01 +#define _3DSTATE_VERTEX_BUFFERS 0x08 +#define _3DSTATE_VERTEX_ELEMENTS 0x09 +#define _3DSTATE_INDEX_BUFFER 0x0A +#define _3DSTATE_VF_STATISTICS 0x0B +#define _3DSTATE_DRAWING_RECTANGLE 0x00 +#define _3DSTATE_CONSTANT_COLOR 0x01 +#define _3DSTATE_SAMPLER_PALETTE_LOAD 0x02 +#define _3DSTATE_CHROMA_KEY 0x04 +#define _3DSTATE_DEPTH_BUFFER 0x05 +#define _3DSTATE_POLY_STIPPLE_OFFSET 0x06 +#define _3DSTATE_POLY_STIPPLE_PATTERN 0x07 +#define _3DSTATE_LINE_STIPPLE 0x08 +#define _3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP 0x09 +#define _3DCONTROL 0x00 +#define _3DPRIMITIVE 0x00 + +#define PIPE_CONTROL_NOWRITE 0x00 +#define PIPE_CONTROL_WRITEIMMEDIATE 0x01 +#define PIPE_CONTROL_WRITEDEPTH 0x02 +#define PIPE_CONTROL_WRITETIMESTAMP 0x03 + +#define PIPE_CONTROL_GTTWRITE_PROCESS_LOCAL 0x00 +#define PIPE_CONTROL_GTTWRITE_GLOBAL 0x01 + +#define BRW_3D(Pipeline,Opcode,Subopcode) ((3 << 29) | \ + ((Pipeline) << 27) | \ + ((Opcode) << 24) | \ + ((Subopcode) << 16)) + +#define BRW_PIPE_CONTROL BRW_3D(3, 2, 0) +#define BRW_PIPE_CONTROL_NOWRITE (0 << 14) +#define BRW_PIPE_CONTROL_WRITE_QWORD (1 << 14) +#define BRW_PIPE_CONTROL_WRITE_DEPTH (2 << 14) +#define BRW_PIPE_CONTROL_WRITE_TIME (3 << 14) +#define BRW_PIPE_CONTROL_DEPTH_STALL (1 << 13) +#define BRW_PIPE_CONTROL_WC_FLUSH (1 << 12) +#define BRW_PIPE_CONTROL_IS_FLUSH (1 << 11) +#define BRW_PIPE_CONTROL_NOTIFY_ENABLE (1 << 8) +#define BRW_PIPE_CONTROL_GLOBAL_GTT (1 << 2) +#define BRW_PIPE_CONTROL_LOCAL_PGTT (0 << 2) + +#define _3DPRIM_POINTLIST 0x01 +#define _3DPRIM_LINELIST 0x02 +#define _3DPRIM_LINESTRIP 0x03 +#define _3DPRIM_TRILIST 0x04 +#define _3DPRIM_TRISTRIP 0x05 +#define _3DPRIM_TRIFAN 0x06 +#define _3DPRIM_QUADLIST 0x07 +#define _3DPRIM_QUADSTRIP 0x08 +#define _3DPRIM_LINELIST_ADJ 0x09 +#define _3DPRIM_LINESTRIP_ADJ 0x0A +#define _3DPRIM_TRILIST_ADJ 0x0B +#define _3DPRIM_TRISTRIP_ADJ 0x0C +#define _3DPRIM_TRISTRIP_REVERSE 0x0D +#define _3DPRIM_POLYGON 0x0E +#define _3DPRIM_RECTLIST 0x0F +#define _3DPRIM_LINELOOP 0x10 +#define _3DPRIM_POINTLIST_BF 0x11 +#define _3DPRIM_LINESTRIP_CONT 0x12 +#define _3DPRIM_LINESTRIP_BF 0x13 +#define _3DPRIM_LINESTRIP_CONT_BF 0x14 +#define _3DPRIM_TRIFAN_NOSTIPPLE 0x15 + +#define _3DPRIM_VERTEXBUFFER_ACCESS_SEQUENTIAL 0 +#define _3DPRIM_VERTEXBUFFER_ACCESS_RANDOM 1 + +#define BRW_ANISORATIO_2 0 +#define BRW_ANISORATIO_4 1 +#define BRW_ANISORATIO_6 2 +#define BRW_ANISORATIO_8 3 +#define BRW_ANISORATIO_10 4 +#define BRW_ANISORATIO_12 5 +#define BRW_ANISORATIO_14 6 +#define BRW_ANISORATIO_16 7 + +#define BRW_BLENDFACTOR_ONE 0x1 +#define BRW_BLENDFACTOR_SRC_COLOR 0x2 +#define BRW_BLENDFACTOR_SRC_ALPHA 0x3 +#define BRW_BLENDFACTOR_DST_ALPHA 0x4 +#define BRW_BLENDFACTOR_DST_COLOR 0x5 +#define BRW_BLENDFACTOR_SRC_ALPHA_SATURATE 0x6 +#define BRW_BLENDFACTOR_CONST_COLOR 0x7 +#define BRW_BLENDFACTOR_CONST_ALPHA 0x8 +#define BRW_BLENDFACTOR_SRC1_COLOR 0x9 +#define BRW_BLENDFACTOR_SRC1_ALPHA 0x0A +#define BRW_BLENDFACTOR_ZERO 0x11 +#define BRW_BLENDFACTOR_INV_SRC_COLOR 0x12 +#define BRW_BLENDFACTOR_INV_SRC_ALPHA 0x13 +#define BRW_BLENDFACTOR_INV_DST_ALPHA 0x14 +#define BRW_BLENDFACTOR_INV_DST_COLOR 0x15 +#define BRW_BLENDFACTOR_INV_CONST_COLOR 0x17 +#define BRW_BLENDFACTOR_INV_CONST_ALPHA 0x18 +#define BRW_BLENDFACTOR_INV_SRC1_COLOR 0x19 +#define BRW_BLENDFACTOR_INV_SRC1_ALPHA 0x1A + +#define BRW_BLENDFUNCTION_ADD 0 +#define BRW_BLENDFUNCTION_SUBTRACT 1 +#define BRW_BLENDFUNCTION_REVERSE_SUBTRACT 2 +#define BRW_BLENDFUNCTION_MIN 3 +#define BRW_BLENDFUNCTION_MAX 4 + +#define BRW_ALPHATEST_FORMAT_UNORM8 0 +#define BRW_ALPHATEST_FORMAT_FLOAT32 1 + +#define BRW_CHROMAKEY_KILL_ON_ANY_MATCH 0 +#define BRW_CHROMAKEY_REPLACE_BLACK 1 + +#define BRW_CLIP_API_OGL 0 +#define BRW_CLIP_API_DX 1 + +#define BRW_CLIPMODE_NORMAL 0 +#define BRW_CLIPMODE_CLIP_ALL 1 +#define BRW_CLIPMODE_CLIP_NON_REJECTED 2 +#define BRW_CLIPMODE_REJECT_ALL 3 +#define BRW_CLIPMODE_ACCEPT_ALL 4 + +#define BRW_CLIP_NDCSPACE 0 +#define BRW_CLIP_SCREENSPACE 1 + +#define BRW_COMPAREFUNCTION_ALWAYS 0 +#define BRW_COMPAREFUNCTION_NEVER 1 +#define BRW_COMPAREFUNCTION_LESS 2 +#define BRW_COMPAREFUNCTION_EQUAL 3 +#define BRW_COMPAREFUNCTION_LEQUAL 4 +#define BRW_COMPAREFUNCTION_GREATER 5 +#define BRW_COMPAREFUNCTION_NOTEQUAL 6 +#define BRW_COMPAREFUNCTION_GEQUAL 7 + +#define BRW_COVERAGE_PIXELS_HALF 0 +#define BRW_COVERAGE_PIXELS_1 1 +#define BRW_COVERAGE_PIXELS_2 2 +#define BRW_COVERAGE_PIXELS_4 3 + +#define BRW_CULLMODE_BOTH 0 +#define BRW_CULLMODE_NONE 1 +#define BRW_CULLMODE_FRONT 2 +#define BRW_CULLMODE_BACK 3 + +#define BRW_DEFAULTCOLOR_R8G8B8A8_UNORM 0 +#define BRW_DEFAULTCOLOR_R32G32B32A32_FLOAT 1 + +#define BRW_DEPTHFORMAT_D32_FLOAT_S8X24_UINT 0 +#define BRW_DEPTHFORMAT_D32_FLOAT 1 +#define BRW_DEPTHFORMAT_D24_UNORM_S8_UINT 2 +#define BRW_DEPTHFORMAT_D16_UNORM 5 + +#define BRW_FLOATING_POINT_IEEE_754 0 +#define BRW_FLOATING_POINT_NON_IEEE_754 1 + +#define BRW_FRONTWINDING_CW 0 +#define BRW_FRONTWINDING_CCW 1 + +#define BRW_INDEX_BYTE 0 +#define BRW_INDEX_WORD 1 +#define BRW_INDEX_DWORD 2 + +#define BRW_LOGICOPFUNCTION_CLEAR 0 +#define BRW_LOGICOPFUNCTION_NOR 1 +#define BRW_LOGICOPFUNCTION_AND_INVERTED 2 +#define BRW_LOGICOPFUNCTION_COPY_INVERTED 3 +#define BRW_LOGICOPFUNCTION_AND_REVERSE 4 +#define BRW_LOGICOPFUNCTION_INVERT 5 +#define BRW_LOGICOPFUNCTION_XOR 6 +#define BRW_LOGICOPFUNCTION_NAND 7 +#define BRW_LOGICOPFUNCTION_AND 8 +#define BRW_LOGICOPFUNCTION_EQUIV 9 +#define BRW_LOGICOPFUNCTION_NOOP 10 +#define BRW_LOGICOPFUNCTION_OR_INVERTED 11 +#define BRW_LOGICOPFUNCTION_COPY 12 +#define BRW_LOGICOPFUNCTION_OR_REVERSE 13 +#define BRW_LOGICOPFUNCTION_OR 14 +#define BRW_LOGICOPFUNCTION_SET 15 + +#define BRW_MAPFILTER_NEAREST 0x0 +#define BRW_MAPFILTER_LINEAR 0x1 +#define BRW_MAPFILTER_ANISOTROPIC 0x2 + +#define BRW_MIPFILTER_NONE 0 +#define BRW_MIPFILTER_NEAREST 1 +#define BRW_MIPFILTER_LINEAR 3 + +#define BRW_POLYGON_FRONT_FACING 0 +#define BRW_POLYGON_BACK_FACING 1 + +#define BRW_PREFILTER_ALWAYS 0x0 +#define BRW_PREFILTER_NEVER 0x1 +#define BRW_PREFILTER_LESS 0x2 +#define BRW_PREFILTER_EQUAL 0x3 +#define BRW_PREFILTER_LEQUAL 0x4 +#define BRW_PREFILTER_GREATER 0x5 +#define BRW_PREFILTER_NOTEQUAL 0x6 +#define BRW_PREFILTER_GEQUAL 0x7 + +#define BRW_PROVOKING_VERTEX_0 0 +#define BRW_PROVOKING_VERTEX_1 1 +#define BRW_PROVOKING_VERTEX_2 2 + +#define BRW_RASTRULE_UPPER_LEFT 0 +#define BRW_RASTRULE_UPPER_RIGHT 1 + +#define BRW_RENDERTARGET_CLAMPRANGE_UNORM 0 +#define BRW_RENDERTARGET_CLAMPRANGE_SNORM 1 +#define BRW_RENDERTARGET_CLAMPRANGE_FORMAT 2 + +#define BRW_STENCILOP_KEEP 0 +#define BRW_STENCILOP_ZERO 1 +#define BRW_STENCILOP_REPLACE 2 +#define BRW_STENCILOP_INCRSAT 3 +#define BRW_STENCILOP_DECRSAT 4 +#define BRW_STENCILOP_INCR 5 +#define BRW_STENCILOP_DECR 6 +#define BRW_STENCILOP_INVERT 7 + +#define BRW_SURFACE_MIPMAPLAYOUT_BELOW 0 +#define BRW_SURFACE_MIPMAPLAYOUT_RIGHT 1 + +#define BRW_SURFACEFORMAT_R32G32B32A32_FLOAT 0x000 +#define BRW_SURFACEFORMAT_R32G32B32A32_SINT 0x001 +#define BRW_SURFACEFORMAT_R32G32B32A32_UINT 0x002 +#define BRW_SURFACEFORMAT_R32G32B32A32_UNORM 0x003 +#define BRW_SURFACEFORMAT_R32G32B32A32_SNORM 0x004 +#define BRW_SURFACEFORMAT_R64G64_FLOAT 0x005 +#define BRW_SURFACEFORMAT_R32G32B32X32_FLOAT 0x006 +#define BRW_SURFACEFORMAT_R32G32B32A32_SSCALED 0x007 +#define BRW_SURFACEFORMAT_R32G32B32A32_USCALED 0x008 +#define BRW_SURFACEFORMAT_R32G32B32_FLOAT 0x040 +#define BRW_SURFACEFORMAT_R32G32B32_SINT 0x041 +#define BRW_SURFACEFORMAT_R32G32B32_UINT 0x042 +#define BRW_SURFACEFORMAT_R32G32B32_UNORM 0x043 +#define BRW_SURFACEFORMAT_R32G32B32_SNORM 0x044 +#define BRW_SURFACEFORMAT_R32G32B32_SSCALED 0x045 +#define BRW_SURFACEFORMAT_R32G32B32_USCALED 0x046 +#define BRW_SURFACEFORMAT_R16G16B16A16_UNORM 0x080 +#define BRW_SURFACEFORMAT_R16G16B16A16_SNORM 0x081 +#define BRW_SURFACEFORMAT_R16G16B16A16_SINT 0x082 +#define BRW_SURFACEFORMAT_R16G16B16A16_UINT 0x083 +#define BRW_SURFACEFORMAT_R16G16B16A16_FLOAT 0x084 +#define BRW_SURFACEFORMAT_R32G32_FLOAT 0x085 +#define BRW_SURFACEFORMAT_R32G32_SINT 0x086 +#define BRW_SURFACEFORMAT_R32G32_UINT 0x087 +#define BRW_SURFACEFORMAT_R32_FLOAT_X8X24_TYPELESS 0x088 +#define BRW_SURFACEFORMAT_X32_TYPELESS_G8X24_UINT 0x089 +#define BRW_SURFACEFORMAT_L32A32_FLOAT 0x08A +#define BRW_SURFACEFORMAT_R32G32_UNORM 0x08B +#define BRW_SURFACEFORMAT_R32G32_SNORM 0x08C +#define BRW_SURFACEFORMAT_R64_FLOAT 0x08D +#define BRW_SURFACEFORMAT_R16G16B16X16_UNORM 0x08E +#define BRW_SURFACEFORMAT_R16G16B16X16_FLOAT 0x08F +#define BRW_SURFACEFORMAT_A32X32_FLOAT 0x090 +#define BRW_SURFACEFORMAT_L32X32_FLOAT 0x091 +#define BRW_SURFACEFORMAT_I32X32_FLOAT 0x092 +#define BRW_SURFACEFORMAT_R16G16B16A16_SSCALED 0x093 +#define BRW_SURFACEFORMAT_R16G16B16A16_USCALED 0x094 +#define BRW_SURFACEFORMAT_R32G32_SSCALED 0x095 +#define BRW_SURFACEFORMAT_R32G32_USCALED 0x096 +#define BRW_SURFACEFORMAT_B8G8R8A8_UNORM 0x0C0 +#define BRW_SURFACEFORMAT_B8G8R8A8_UNORM_SRGB 0x0C1 +#define BRW_SURFACEFORMAT_R10G10B10A2_UNORM 0x0C2 +#define BRW_SURFACEFORMAT_R10G10B10A2_UNORM_SRGB 0x0C3 +#define BRW_SURFACEFORMAT_R10G10B10A2_UINT 0x0C4 +#define BRW_SURFACEFORMAT_R10G10B10_SNORM_A2_UNORM 0x0C5 +#define BRW_SURFACEFORMAT_R8G8B8A8_UNORM 0x0C7 +#define BRW_SURFACEFORMAT_R8G8B8A8_UNORM_SRGB 0x0C8 +#define BRW_SURFACEFORMAT_R8G8B8A8_SNORM 0x0C9 +#define BRW_SURFACEFORMAT_R8G8B8A8_SINT 0x0CA +#define BRW_SURFACEFORMAT_R8G8B8A8_UINT 0x0CB +#define BRW_SURFACEFORMAT_R16G16_UNORM 0x0CC +#define BRW_SURFACEFORMAT_R16G16_SNORM 0x0CD +#define BRW_SURFACEFORMAT_R16G16_SINT 0x0CE +#define BRW_SURFACEFORMAT_R16G16_UINT 0x0CF +#define BRW_SURFACEFORMAT_R16G16_FLOAT 0x0D0 +#define BRW_SURFACEFORMAT_B10G10R10A2_UNORM 0x0D1 +#define BRW_SURFACEFORMAT_B10G10R10A2_UNORM_SRGB 0x0D2 +#define BRW_SURFACEFORMAT_R11G11B10_FLOAT 0x0D3 +#define BRW_SURFACEFORMAT_R32_SINT 0x0D6 +#define BRW_SURFACEFORMAT_R32_UINT 0x0D7 +#define BRW_SURFACEFORMAT_R32_FLOAT 0x0D8 +#define BRW_SURFACEFORMAT_R24_UNORM_X8_TYPELESS 0x0D9 +#define BRW_SURFACEFORMAT_X24_TYPELESS_G8_UINT 0x0DA +#define BRW_SURFACEFORMAT_L16A16_UNORM 0x0DF +#define BRW_SURFACEFORMAT_I24X8_UNORM 0x0E0 +#define BRW_SURFACEFORMAT_L24X8_UNORM 0x0E1 +#define BRW_SURFACEFORMAT_A24X8_UNORM 0x0E2 +#define BRW_SURFACEFORMAT_I32_FLOAT 0x0E3 +#define BRW_SURFACEFORMAT_L32_FLOAT 0x0E4 +#define BRW_SURFACEFORMAT_A32_FLOAT 0x0E5 +#define BRW_SURFACEFORMAT_B8G8R8X8_UNORM 0x0E9 +#define BRW_SURFACEFORMAT_B8G8R8X8_UNORM_SRGB 0x0EA +#define BRW_SURFACEFORMAT_R8G8B8X8_UNORM 0x0EB +#define BRW_SURFACEFORMAT_R8G8B8X8_UNORM_SRGB 0x0EC +#define BRW_SURFACEFORMAT_R9G9B9E5_SHAREDEXP 0x0ED +#define BRW_SURFACEFORMAT_B10G10R10X2_UNORM 0x0EE +#define BRW_SURFACEFORMAT_L16A16_FLOAT 0x0F0 +#define BRW_SURFACEFORMAT_R32_UNORM 0x0F1 +#define BRW_SURFACEFORMAT_R32_SNORM 0x0F2 +#define BRW_SURFACEFORMAT_R10G10B10X2_USCALED 0x0F3 +#define BRW_SURFACEFORMAT_R8G8B8A8_SSCALED 0x0F4 +#define BRW_SURFACEFORMAT_R8G8B8A8_USCALED 0x0F5 +#define BRW_SURFACEFORMAT_R16G16_SSCALED 0x0F6 +#define BRW_SURFACEFORMAT_R16G16_USCALED 0x0F7 +#define BRW_SURFACEFORMAT_R32_SSCALED 0x0F8 +#define BRW_SURFACEFORMAT_R32_USCALED 0x0F9 +#define BRW_SURFACEFORMAT_B5G6R5_UNORM 0x100 +#define BRW_SURFACEFORMAT_B5G6R5_UNORM_SRGB 0x101 +#define BRW_SURFACEFORMAT_B5G5R5A1_UNORM 0x102 +#define BRW_SURFACEFORMAT_B5G5R5A1_UNORM_SRGB 0x103 +#define BRW_SURFACEFORMAT_B4G4R4A4_UNORM 0x104 +#define BRW_SURFACEFORMAT_B4G4R4A4_UNORM_SRGB 0x105 +#define BRW_SURFACEFORMAT_R8G8_UNORM 0x106 +#define BRW_SURFACEFORMAT_R8G8_SNORM 0x107 +#define BRW_SURFACEFORMAT_R8G8_SINT 0x108 +#define BRW_SURFACEFORMAT_R8G8_UINT 0x109 +#define BRW_SURFACEFORMAT_R16_UNORM 0x10A +#define BRW_SURFACEFORMAT_R16_SNORM 0x10B +#define BRW_SURFACEFORMAT_R16_SINT 0x10C +#define BRW_SURFACEFORMAT_R16_UINT 0x10D +#define BRW_SURFACEFORMAT_R16_FLOAT 0x10E +#define BRW_SURFACEFORMAT_I16_UNORM 0x111 +#define BRW_SURFACEFORMAT_L16_UNORM 0x112 +#define BRW_SURFACEFORMAT_A16_UNORM 0x113 +#define BRW_SURFACEFORMAT_L8A8_UNORM 0x114 +#define BRW_SURFACEFORMAT_I16_FLOAT 0x115 +#define BRW_SURFACEFORMAT_L16_FLOAT 0x116 +#define BRW_SURFACEFORMAT_A16_FLOAT 0x117 +#define BRW_SURFACEFORMAT_R5G5_SNORM_B6_UNORM 0x119 +#define BRW_SURFACEFORMAT_B5G5R5X1_UNORM 0x11A +#define BRW_SURFACEFORMAT_B5G5R5X1_UNORM_SRGB 0x11B +#define BRW_SURFACEFORMAT_R8G8_SSCALED 0x11C +#define BRW_SURFACEFORMAT_R8G8_USCALED 0x11D +#define BRW_SURFACEFORMAT_R16_SSCALED 0x11E +#define BRW_SURFACEFORMAT_R16_USCALED 0x11F +#define BRW_SURFACEFORMAT_R8_UNORM 0x140 +#define BRW_SURFACEFORMAT_R8_SNORM 0x141 +#define BRW_SURFACEFORMAT_R8_SINT 0x142 +#define BRW_SURFACEFORMAT_R8_UINT 0x143 +#define BRW_SURFACEFORMAT_A8_UNORM 0x144 +#define BRW_SURFACEFORMAT_I8_UNORM 0x145 +#define BRW_SURFACEFORMAT_L8_UNORM 0x146 +#define BRW_SURFACEFORMAT_P4A4_UNORM 0x147 +#define BRW_SURFACEFORMAT_A4P4_UNORM 0x148 +#define BRW_SURFACEFORMAT_R8_SSCALED 0x149 +#define BRW_SURFACEFORMAT_R8_USCALED 0x14A +#define BRW_SURFACEFORMAT_R1_UINT 0x181 +#define BRW_SURFACEFORMAT_YCRCB_NORMAL 0x182 +#define BRW_SURFACEFORMAT_YCRCB_SWAPUVY 0x183 +#define BRW_SURFACEFORMAT_BC1_UNORM 0x186 +#define BRW_SURFACEFORMAT_BC2_UNORM 0x187 +#define BRW_SURFACEFORMAT_BC3_UNORM 0x188 +#define BRW_SURFACEFORMAT_BC4_UNORM 0x189 +#define BRW_SURFACEFORMAT_BC5_UNORM 0x18A +#define BRW_SURFACEFORMAT_BC1_UNORM_SRGB 0x18B +#define BRW_SURFACEFORMAT_BC2_UNORM_SRGB 0x18C +#define BRW_SURFACEFORMAT_BC3_UNORM_SRGB 0x18D +#define BRW_SURFACEFORMAT_MONO8 0x18E +#define BRW_SURFACEFORMAT_YCRCB_SWAPUV 0x18F +#define BRW_SURFACEFORMAT_YCRCB_SWAPY 0x190 +#define BRW_SURFACEFORMAT_DXT1_RGB 0x191 +#define BRW_SURFACEFORMAT_FXT1 0x192 +#define BRW_SURFACEFORMAT_R8G8B8_UNORM 0x193 +#define BRW_SURFACEFORMAT_R8G8B8_SNORM 0x194 +#define BRW_SURFACEFORMAT_R8G8B8_SSCALED 0x195 +#define BRW_SURFACEFORMAT_R8G8B8_USCALED 0x196 +#define BRW_SURFACEFORMAT_R64G64B64A64_FLOAT 0x197 +#define BRW_SURFACEFORMAT_R64G64B64_FLOAT 0x198 +#define BRW_SURFACEFORMAT_BC4_SNORM 0x199 +#define BRW_SURFACEFORMAT_BC5_SNORM 0x19A +#define BRW_SURFACEFORMAT_R16G16B16_UNORM 0x19C +#define BRW_SURFACEFORMAT_R16G16B16_SNORM 0x19D +#define BRW_SURFACEFORMAT_R16G16B16_SSCALED 0x19E +#define BRW_SURFACEFORMAT_R16G16B16_USCALED 0x19F + +#define BRW_SURFACERETURNFORMAT_FLOAT32 0 +#define BRW_SURFACERETURNFORMAT_S1 1 + +#define BRW_SURFACE_1D 0 +#define BRW_SURFACE_2D 1 +#define BRW_SURFACE_3D 2 +#define BRW_SURFACE_CUBE 3 +#define BRW_SURFACE_BUFFER 4 +#define BRW_SURFACE_NULL 7 + +#define BRW_BORDER_COLOR_MODE_DEFAULT 0 +#define BRW_BORDER_COLOR_MODE_LEGACY 1 + +#define BRW_TEXCOORDMODE_WRAP 0 +#define BRW_TEXCOORDMODE_MIRROR 1 +#define BRW_TEXCOORDMODE_CLAMP 2 +#define BRW_TEXCOORDMODE_CUBE 3 +#define BRW_TEXCOORDMODE_CLAMP_BORDER 4 +#define BRW_TEXCOORDMODE_MIRROR_ONCE 5 + +#define BRW_THREAD_PRIORITY_NORMAL 0 +#define BRW_THREAD_PRIORITY_HIGH 1 + +#define BRW_TILEWALK_XMAJOR 0 +#define BRW_TILEWALK_YMAJOR 1 + +#define BRW_VERTEX_SUBPIXEL_PRECISION_8BITS 0 +#define BRW_VERTEX_SUBPIXEL_PRECISION_4BITS 1 + +#define BRW_VERTEXBUFFER_ACCESS_VERTEXDATA 0 +#define BRW_VERTEXBUFFER_ACCESS_INSTANCEDATA 1 + +#define BRW_VFCOMPONENT_NOSTORE 0 +#define BRW_VFCOMPONENT_STORE_SRC 1 +#define BRW_VFCOMPONENT_STORE_0 2 +#define BRW_VFCOMPONENT_STORE_1_FLT 3 +#define BRW_VFCOMPONENT_STORE_1_INT 4 +#define BRW_VFCOMPONENT_STORE_VID 5 +#define BRW_VFCOMPONENT_STORE_IID 6 +#define BRW_VFCOMPONENT_STORE_PID 7 + + + +/* Execution Unit (EU) defines */ + +#define BRW_ALIGN_1 0 +#define BRW_ALIGN_16 1 + +#define BRW_ADDRESS_DIRECT 0 +#define BRW_ADDRESS_REGISTER_INDIRECT_REGISTER 1 + +#define BRW_CHANNEL_X 0 +#define BRW_CHANNEL_Y 1 +#define BRW_CHANNEL_Z 2 +#define BRW_CHANNEL_W 3 + +#define BRW_COMPRESSION_NONE 0 +#define BRW_COMPRESSION_2NDHALF 1 +#define BRW_COMPRESSION_COMPRESSED 2 + +#define BRW_CONDITIONAL_NONE 0 +#define BRW_CONDITIONAL_Z 1 +#define BRW_CONDITIONAL_NZ 2 +#define BRW_CONDITIONAL_EQ 1 /* Z */ +#define BRW_CONDITIONAL_NEQ 2 /* NZ */ +#define BRW_CONDITIONAL_G 3 +#define BRW_CONDITIONAL_GE 4 +#define BRW_CONDITIONAL_L 5 +#define BRW_CONDITIONAL_LE 6 +#define BRW_CONDITIONAL_C 7 +#define BRW_CONDITIONAL_O 8 + +#define BRW_DEBUG_NONE 0 +#define BRW_DEBUG_BREAKPOINT 1 + +#define BRW_DEPENDENCY_NORMAL 0 +#define BRW_DEPENDENCY_NOTCLEARED 1 +#define BRW_DEPENDENCY_NOTCHECKED 2 +#define BRW_DEPENDENCY_DISABLE 3 + +#define BRW_EXECUTE_1 0 +#define BRW_EXECUTE_2 1 +#define BRW_EXECUTE_4 2 +#define BRW_EXECUTE_8 3 +#define BRW_EXECUTE_16 4 +#define BRW_EXECUTE_32 5 + +#define BRW_HORIZONTAL_STRIDE_0 0 +#define BRW_HORIZONTAL_STRIDE_1 1 +#define BRW_HORIZONTAL_STRIDE_2 2 +#define BRW_HORIZONTAL_STRIDE_4 3 + +#define BRW_INSTRUCTION_NORMAL 0 +#define BRW_INSTRUCTION_SATURATE 1 + +#define BRW_MASK_ENABLE 0 +#define BRW_MASK_DISABLE 1 + +#define BRW_OPCODE_MOV 1 +#define BRW_OPCODE_SEL 2 +#define BRW_OPCODE_NOT 4 +#define BRW_OPCODE_AND 5 +#define BRW_OPCODE_OR 6 +#define BRW_OPCODE_XOR 7 +#define BRW_OPCODE_SHR 8 +#define BRW_OPCODE_SHL 9 +#define BRW_OPCODE_RSR 10 +#define BRW_OPCODE_RSL 11 +#define BRW_OPCODE_ASR 12 +#define BRW_OPCODE_CMP 16 +#define BRW_OPCODE_JMPI 32 +#define BRW_OPCODE_IF 34 +#define BRW_OPCODE_IFF 35 +#define BRW_OPCODE_ELSE 36 +#define BRW_OPCODE_ENDIF 37 +#define BRW_OPCODE_DO 38 +#define BRW_OPCODE_WHILE 39 +#define BRW_OPCODE_BREAK 40 +#define BRW_OPCODE_CONTINUE 41 +#define BRW_OPCODE_HALT 42 +#define BRW_OPCODE_MSAVE 44 +#define BRW_OPCODE_MRESTORE 45 +#define BRW_OPCODE_PUSH 46 +#define BRW_OPCODE_POP 47 +#define BRW_OPCODE_WAIT 48 +#define BRW_OPCODE_SEND 49 +#define BRW_OPCODE_ADD 64 +#define BRW_OPCODE_MUL 65 +#define BRW_OPCODE_AVG 66 +#define BRW_OPCODE_FRC 67 +#define BRW_OPCODE_RNDU 68 +#define BRW_OPCODE_RNDD 69 +#define BRW_OPCODE_RNDE 70 +#define BRW_OPCODE_RNDZ 71 +#define BRW_OPCODE_MAC 72 +#define BRW_OPCODE_MACH 73 +#define BRW_OPCODE_LZD 74 +#define BRW_OPCODE_SAD2 80 +#define BRW_OPCODE_SADA2 81 +#define BRW_OPCODE_DP4 84 +#define BRW_OPCODE_DPH 85 +#define BRW_OPCODE_DP3 86 +#define BRW_OPCODE_DP2 87 +#define BRW_OPCODE_DPA2 88 +#define BRW_OPCODE_LINE 89 +#define BRW_OPCODE_NOP 126 + +#define BRW_PREDICATE_NONE 0 +#define BRW_PREDICATE_NORMAL 1 +#define BRW_PREDICATE_ALIGN1_ANYV 2 +#define BRW_PREDICATE_ALIGN1_ALLV 3 +#define BRW_PREDICATE_ALIGN1_ANY2H 4 +#define BRW_PREDICATE_ALIGN1_ALL2H 5 +#define BRW_PREDICATE_ALIGN1_ANY4H 6 +#define BRW_PREDICATE_ALIGN1_ALL4H 7 +#define BRW_PREDICATE_ALIGN1_ANY8H 8 +#define BRW_PREDICATE_ALIGN1_ALL8H 9 +#define BRW_PREDICATE_ALIGN1_ANY16H 10 +#define BRW_PREDICATE_ALIGN1_ALL16H 11 +#define BRW_PREDICATE_ALIGN16_REPLICATE_X 2 +#define BRW_PREDICATE_ALIGN16_REPLICATE_Y 3 +#define BRW_PREDICATE_ALIGN16_REPLICATE_Z 4 +#define BRW_PREDICATE_ALIGN16_REPLICATE_W 5 +#define BRW_PREDICATE_ALIGN16_ANY4H 6 +#define BRW_PREDICATE_ALIGN16_ALL4H 7 + +#define BRW_ARCHITECTURE_REGISTER_FILE 0 +#define BRW_GENERAL_REGISTER_FILE 1 +#define BRW_MESSAGE_REGISTER_FILE 2 +#define BRW_IMMEDIATE_VALUE 3 + +#define BRW_REGISTER_TYPE_UD 0 +#define BRW_REGISTER_TYPE_D 1 +#define BRW_REGISTER_TYPE_UW 2 +#define BRW_REGISTER_TYPE_W 3 +#define BRW_REGISTER_TYPE_UB 4 +#define BRW_REGISTER_TYPE_B 5 +#define BRW_REGISTER_TYPE_VF 5 /* packed float vector, immediates only? */ +#define BRW_REGISTER_TYPE_HF 6 +#define BRW_REGISTER_TYPE_V 6 /* packed int vector, immediates only, uword dest only */ +#define BRW_REGISTER_TYPE_F 7 + +#define BRW_ARF_NULL 0x00 +#define BRW_ARF_ADDRESS 0x10 +#define BRW_ARF_ACCUMULATOR 0x20 +#define BRW_ARF_FLAG 0x30 +#define BRW_ARF_MASK 0x40 +#define BRW_ARF_MASK_STACK 0x50 +#define BRW_ARF_MASK_STACK_DEPTH 0x60 +#define BRW_ARF_STATE 0x70 +#define BRW_ARF_CONTROL 0x80 +#define BRW_ARF_NOTIFICATION_COUNT 0x90 +#define BRW_ARF_IP 0xA0 + +#define BRW_AMASK 0 +#define BRW_IMASK 1 +#define BRW_LMASK 2 +#define BRW_CMASK 3 + + + +#define BRW_THREAD_NORMAL 0 +#define BRW_THREAD_ATOMIC 1 +#define BRW_THREAD_SWITCH 2 + +#define BRW_VERTICAL_STRIDE_0 0 +#define BRW_VERTICAL_STRIDE_1 1 +#define BRW_VERTICAL_STRIDE_2 2 +#define BRW_VERTICAL_STRIDE_4 3 +#define BRW_VERTICAL_STRIDE_8 4 +#define BRW_VERTICAL_STRIDE_16 5 +#define BRW_VERTICAL_STRIDE_32 6 +#define BRW_VERTICAL_STRIDE_64 7 +#define BRW_VERTICAL_STRIDE_128 8 +#define BRW_VERTICAL_STRIDE_256 9 +#define BRW_VERTICAL_STRIDE_ONE_DIMENSIONAL 0xF + +#define BRW_WIDTH_1 0 +#define BRW_WIDTH_2 1 +#define BRW_WIDTH_4 2 +#define BRW_WIDTH_8 3 +#define BRW_WIDTH_16 4 + +#define BRW_STATELESS_BUFFER_BOUNDARY_1K 0 +#define BRW_STATELESS_BUFFER_BOUNDARY_2K 1 +#define BRW_STATELESS_BUFFER_BOUNDARY_4K 2 +#define BRW_STATELESS_BUFFER_BOUNDARY_8K 3 +#define BRW_STATELESS_BUFFER_BOUNDARY_16K 4 +#define BRW_STATELESS_BUFFER_BOUNDARY_32K 5 +#define BRW_STATELESS_BUFFER_BOUNDARY_64K 6 +#define BRW_STATELESS_BUFFER_BOUNDARY_128K 7 +#define BRW_STATELESS_BUFFER_BOUNDARY_256K 8 +#define BRW_STATELESS_BUFFER_BOUNDARY_512K 9 +#define BRW_STATELESS_BUFFER_BOUNDARY_1M 10 +#define BRW_STATELESS_BUFFER_BOUNDARY_2M 11 + +#define BRW_POLYGON_FACING_FRONT 0 +#define BRW_POLYGON_FACING_BACK 1 + +#define BRW_MESSAGE_TARGET_NULL 0 +#define BRW_MESSAGE_TARGET_MATH 1 +#define BRW_MESSAGE_TARGET_SAMPLER 2 +#define BRW_MESSAGE_TARGET_GATEWAY 3 +#define BRW_MESSAGE_TARGET_DATAPORT_READ 4 +#define BRW_MESSAGE_TARGET_DATAPORT_WRITE 5 +#define BRW_MESSAGE_TARGET_URB 6 +#define BRW_MESSAGE_TARGET_THREAD_SPAWNER 7 + +#define BRW_SAMPLER_RETURN_FORMAT_FLOAT32 0 +#define BRW_SAMPLER_RETURN_FORMAT_UINT32 2 +#define BRW_SAMPLER_RETURN_FORMAT_SINT32 3 + +#define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE 0 +#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE 0 +#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_BIAS 0 +#define BRW_SAMPLER_MESSAGE_SIMD8_KILLPIX 1 +#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_LOD 1 +#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_LOD 1 +#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_GRADIENTS 2 +#define BRW_SAMPLER_MESSAGE_SIMD8_SAMPLE_GRADIENTS 2 +#define BRW_SAMPLER_MESSAGE_SIMD4X2_SAMPLE_COMPARE 0 +#define BRW_SAMPLER_MESSAGE_SIMD16_SAMPLE_COMPARE 2 +#define BRW_SAMPLER_MESSAGE_SIMD4X2_RESINFO 2 +#define BRW_SAMPLER_MESSAGE_SIMD8_RESINFO 2 +#define BRW_SAMPLER_MESSAGE_SIMD16_RESINFO 2 +#define BRW_SAMPLER_MESSAGE_SIMD4X2_LD 3 +#define BRW_SAMPLER_MESSAGE_SIMD8_LD 3 +#define BRW_SAMPLER_MESSAGE_SIMD16_LD 3 + +#define BRW_DATAPORT_OWORD_BLOCK_1_OWORDLOW 0 +#define BRW_DATAPORT_OWORD_BLOCK_1_OWORDHIGH 1 +#define BRW_DATAPORT_OWORD_BLOCK_2_OWORDS 2 +#define BRW_DATAPORT_OWORD_BLOCK_4_OWORDS 3 +#define BRW_DATAPORT_OWORD_BLOCK_8_OWORDS 4 + +#define BRW_DATAPORT_OWORD_DUAL_BLOCK_1OWORD 0 +#define BRW_DATAPORT_OWORD_DUAL_BLOCK_4OWORDS 2 + +#define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_8DWORDS 2 +#define BRW_DATAPORT_DWORD_SCATTERED_BLOCK_16DWORDS 3 + +#define BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ 0 +#define BRW_DATAPORT_READ_MESSAGE_OWORD_DUAL_BLOCK_READ 1 +#define BRW_DATAPORT_READ_MESSAGE_DWORD_BLOCK_READ 2 +#define BRW_DATAPORT_READ_MESSAGE_DWORD_SCATTERED_READ 3 + +#define BRW_DATAPORT_READ_TARGET_DATA_CACHE 0 +#define BRW_DATAPORT_READ_TARGET_RENDER_CACHE 1 +#define BRW_DATAPORT_READ_TARGET_SAMPLER_CACHE 2 + +#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE 0 +#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE_REPLICATED 1 +#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN01 2 +#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_DUAL_SOURCE_SUBSPAN23 3 +#define BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD8_SINGLE_SOURCE_SUBSPAN01 4 + +#define BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE 0 +#define BRW_DATAPORT_WRITE_MESSAGE_OWORD_DUAL_BLOCK_WRITE 1 +#define BRW_DATAPORT_WRITE_MESSAGE_DWORD_BLOCK_WRITE 2 +#define BRW_DATAPORT_WRITE_MESSAGE_DWORD_SCATTERED_WRITE 3 +#define BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE 4 +#define BRW_DATAPORT_WRITE_MESSAGE_STREAMED_VERTEX_BUFFER_WRITE 5 +#define BRW_DATAPORT_WRITE_MESSAGE_FLUSH_RENDER_CACHE 7 + +#define BRW_MATH_FUNCTION_INV 1 +#define BRW_MATH_FUNCTION_LOG 2 +#define BRW_MATH_FUNCTION_EXP 3 +#define BRW_MATH_FUNCTION_SQRT 4 +#define BRW_MATH_FUNCTION_RSQ 5 +#define BRW_MATH_FUNCTION_SIN 6 /* was 7 */ +#define BRW_MATH_FUNCTION_COS 7 /* was 8 */ +#define BRW_MATH_FUNCTION_SINCOS 8 /* was 6 */ +#define BRW_MATH_FUNCTION_TAN 9 +#define BRW_MATH_FUNCTION_POW 10 +#define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT_AND_REMAINDER 11 +#define BRW_MATH_FUNCTION_INT_DIV_QUOTIENT 12 +#define BRW_MATH_FUNCTION_INT_DIV_REMAINDER 13 + +#define BRW_MATH_INTEGER_UNSIGNED 0 +#define BRW_MATH_INTEGER_SIGNED 1 + +#define BRW_MATH_PRECISION_FULL 0 +#define BRW_MATH_PRECISION_PARTIAL 1 + +#define BRW_MATH_SATURATE_NONE 0 +#define BRW_MATH_SATURATE_SATURATE 1 + +#define BRW_MATH_DATA_VECTOR 0 +#define BRW_MATH_DATA_SCALAR 1 + +#define BRW_URB_OPCODE_WRITE 0 + +#define BRW_URB_SWIZZLE_NONE 0 +#define BRW_URB_SWIZZLE_INTERLEAVE 1 +#define BRW_URB_SWIZZLE_TRANSPOSE 2 + +#define BRW_SCRATCH_SPACE_SIZE_1K 0 +#define BRW_SCRATCH_SPACE_SIZE_2K 1 +#define BRW_SCRATCH_SPACE_SIZE_4K 2 +#define BRW_SCRATCH_SPACE_SIZE_8K 3 +#define BRW_SCRATCH_SPACE_SIZE_16K 4 +#define BRW_SCRATCH_SPACE_SIZE_32K 5 +#define BRW_SCRATCH_SPACE_SIZE_64K 6 +#define BRW_SCRATCH_SPACE_SIZE_128K 7 +#define BRW_SCRATCH_SPACE_SIZE_256K 8 +#define BRW_SCRATCH_SPACE_SIZE_512K 9 +#define BRW_SCRATCH_SPACE_SIZE_1M 10 +#define BRW_SCRATCH_SPACE_SIZE_2M 11 + + + + +#define CMD_URB_FENCE 0x6000 +#define CMD_CONST_BUFFER_STATE 0x6001 +#define CMD_CONST_BUFFER 0x6002 + +#define CMD_STATE_BASE_ADDRESS 0x6101 +#define CMD_STATE_INSN_POINTER 0x6102 +#define CMD_PIPELINE_SELECT 0x6104 + +#define CMD_PIPELINED_STATE_POINTERS 0x7800 +#define CMD_BINDING_TABLE_PTRS 0x7801 +#define CMD_VERTEX_BUFFER 0x7808 +#define CMD_VERTEX_ELEMENT 0x7809 +#define CMD_INDEX_BUFFER 0x780a +#define CMD_VF_STATISTICS 0x780b + +#define CMD_DRAW_RECT 0x7900 +#define CMD_BLEND_CONSTANT_COLOR 0x7901 +#define CMD_CHROMA_KEY 0x7904 +#define CMD_DEPTH_BUFFER 0x7905 +#define CMD_POLY_STIPPLE_OFFSET 0x7906 +#define CMD_POLY_STIPPLE_PATTERN 0x7907 +#define CMD_LINE_STIPPLE_PATTERN 0x7908 +#define CMD_GLOBAL_DEPTH_OFFSET_CLAMP 0x7908 + +#define CMD_PIPE_CONTROL 0x7a00 + +#define CMD_3D_PRIM 0x7b00 + +#define CMD_MI_FLUSH 0x0200 + + +/* Various values from the R0 vertex header: + */ +#define R02_PRIM_END 0x1 +#define R02_PRIM_START 0x2 + +/* media pipeline */ + +#define BRW_VFE_MODE_GENERIC 0x0 +#define BRW_VFE_MODE_VLD_MPEG2 0x1 +#define BRW_VFE_MODE_IS 0x2 +#define BRW_VFE_MODE_AVC_MC 0x4 +#define BRW_VFE_MODE_AVC_IT 0x7 +#define BRW_VFE_MODE_VC1_IT 0xB + +#define BRW_VFE_DEBUG_COUNTER_FREE 0 +#define BRW_VFE_DEBUG_COUNTER_FROZEN 1 +#define BRW_VFE_DEBUG_COUNTER_ONCE 2 +#define BRW_VFE_DEBUG_COUNTER_ALWAYS 3 + +/* VLD_STATE */ +#define BRW_MPEG_TOP_FIELD 1 +#define BRW_MPEG_BOTTOM_FIELD 2 +#define BRW_MPEG_FRAME 3 +#define BRW_MPEG_QSCALE_LINEAR 0 +#define BRW_MPEG_QSCALE_NONLINEAR 1 +#define BRW_MPEG_ZIGZAG_SCAN 0 +#define BRW_MPEG_ALTER_VERTICAL_SCAN 1 +#define BRW_MPEG_I_PICTURE 1 +#define BRW_MPEG_P_PICTURE 2 +#define BRW_MPEG_B_PICTURE 3 + +#endif diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-emit.c b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-emit.c new file mode 100644 index 000000000000..f27b238048a9 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-emit.c @@ -0,0 +1,1089 @@ +/* + Copyright (C) Intel Corp. 2006. All Rights Reserved. + Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + develop this 3D driver. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **********************************************************************/ +/* + * Authors: + * Keith Whitwell + */ + +#include "cairoint.h" +#include "cairo-drm-intel-brw-eu.h" + +#include + +/*********************************************************************** + * Internal helper for constructing instructions + */ + +static void guess_execution_size( struct brw_instruction *insn, + struct brw_reg reg ) +{ + if (reg.width == BRW_WIDTH_8 && + insn->header.compression_control == BRW_COMPRESSION_COMPRESSED) + insn->header.execution_size = BRW_EXECUTE_16; + else + insn->header.execution_size = reg.width; /* note - definitions are compatible */ +} + + +void +brw_instruction_set_destination (struct brw_instruction *insn, + struct brw_reg dest) +{ + insn->bits1.da1.dest_reg_file = dest.file; + insn->bits1.da1.dest_reg_type = dest.type; + insn->bits1.da1.dest_address_mode = dest.address_mode; + + if (dest.address_mode == BRW_ADDRESS_DIRECT) { + insn->bits1.da1.dest_reg_nr = dest.nr; + + if (insn->header.access_mode == BRW_ALIGN_1) { + insn->bits1.da1.dest_subreg_nr = dest.subnr; + if (dest.hstride == BRW_HORIZONTAL_STRIDE_0) + dest.hstride = BRW_HORIZONTAL_STRIDE_1; + insn->bits1.da1.dest_horiz_stride = dest.hstride; + } else { + insn->bits1.da16.dest_subreg_nr = dest.subnr / 16; + insn->bits1.da16.dest_writemask = dest.dw1.bits.writemask; + } + } else { + insn->bits1.ia1.dest_subreg_nr = dest.subnr; + + /* These are different sizes in align1 vs align16: + */ + if (insn->header.access_mode == BRW_ALIGN_1) { + insn->bits1.ia1.dest_indirect_offset = dest.dw1.bits.indirect_offset; + if (dest.hstride == BRW_HORIZONTAL_STRIDE_0) + dest.hstride = BRW_HORIZONTAL_STRIDE_1; + insn->bits1.ia1.dest_horiz_stride = dest.hstride; + } else { + insn->bits1.ia16.dest_indirect_offset = dest.dw1.bits.indirect_offset; + } + } + + /* NEW: Set the execution size based on dest.width and + * insn->compression_control: + */ + guess_execution_size(insn, dest); +} + +void +brw_instruction_set_source0 (struct brw_instruction *insn, + struct brw_reg reg) +{ + assert(reg.file != BRW_MESSAGE_REGISTER_FILE); + + insn->bits1.da1.src0_reg_file = reg.file; + insn->bits1.da1.src0_reg_type = reg.type; + insn->bits2.da1.src0_abs = reg.abs; + insn->bits2.da1.src0_negate = reg.negate; + insn->bits2.da1.src0_address_mode = reg.address_mode; + + if (reg.file == BRW_IMMEDIATE_VALUE) { + insn->bits3.ud = reg.dw1.ud; + + /* Required to set some fields in src1 as well: + */ + insn->bits1.da1.src1_reg_file = 0; /* arf */ + insn->bits1.da1.src1_reg_type = reg.type; + } else { + if (reg.address_mode == BRW_ADDRESS_DIRECT) { + if (insn->header.access_mode == BRW_ALIGN_1) { + insn->bits2.da1.src0_subreg_nr = reg.subnr; + insn->bits2.da1.src0_reg_nr = reg.nr; + } else { + insn->bits2.da16.src0_subreg_nr = reg.subnr / 16; + insn->bits2.da16.src0_reg_nr = reg.nr; + } + } else { + insn->bits2.ia1.src0_subreg_nr = reg.subnr; + + if (insn->header.access_mode == BRW_ALIGN_1) { + insn->bits2.ia1.src0_indirect_offset = reg.dw1.bits.indirect_offset; + } else { + insn->bits2.ia16.src0_subreg_nr = reg.dw1.bits.indirect_offset; + } + } + + if (insn->header.access_mode == BRW_ALIGN_1) { + if (reg.width == BRW_WIDTH_1 && + insn->header.execution_size == BRW_EXECUTE_1) { + insn->bits2.da1.src0_horiz_stride = BRW_HORIZONTAL_STRIDE_0; + insn->bits2.da1.src0_width = BRW_WIDTH_1; + insn->bits2.da1.src0_vert_stride = BRW_VERTICAL_STRIDE_0; + } else { + insn->bits2.da1.src0_horiz_stride = reg.hstride; + insn->bits2.da1.src0_width = reg.width; + insn->bits2.da1.src0_vert_stride = reg.vstride; + } + } else { + insn->bits2.da16.src0_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X); + insn->bits2.da16.src0_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y); + insn->bits2.da16.src0_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z); + insn->bits2.da16.src0_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W); + + /* This is an oddity of the fact we're using the same + * descriptions for registers in align_16 as align_1: + */ + if (reg.vstride == BRW_VERTICAL_STRIDE_8) + insn->bits2.da16.src0_vert_stride = BRW_VERTICAL_STRIDE_4; + else + insn->bits2.da16.src0_vert_stride = reg.vstride; + } + } +} + + +void brw_set_src1( struct brw_instruction *insn, + struct brw_reg reg ) +{ + assert(reg.file != BRW_MESSAGE_REGISTER_FILE); + + insn->bits1.da1.src1_reg_file = reg.file; + insn->bits1.da1.src1_reg_type = reg.type; + insn->bits3.da1.src1_abs = reg.abs; + insn->bits3.da1.src1_negate = reg.negate; + + /* Only src1 can be immediate in two-argument instructions. + */ + assert(insn->bits1.da1.src0_reg_file != BRW_IMMEDIATE_VALUE); + + if (reg.file == BRW_IMMEDIATE_VALUE) { + insn->bits3.ud = reg.dw1.ud; + } + else { + /* This is a hardware restriction, which may or may not be lifted + * in the future: + */ + assert (reg.address_mode == BRW_ADDRESS_DIRECT); + //assert (reg.file == BRW_GENERAL_REGISTER_FILE); + + if (insn->header.access_mode == BRW_ALIGN_1) { + insn->bits3.da1.src1_subreg_nr = reg.subnr; + insn->bits3.da1.src1_reg_nr = reg.nr; + } + else { + insn->bits3.da16.src1_subreg_nr = reg.subnr / 16; + insn->bits3.da16.src1_reg_nr = reg.nr; + } + + if (insn->header.access_mode == BRW_ALIGN_1) { + if (reg.width == BRW_WIDTH_1 && + insn->header.execution_size == BRW_EXECUTE_1) { + insn->bits3.da1.src1_horiz_stride = BRW_HORIZONTAL_STRIDE_0; + insn->bits3.da1.src1_width = BRW_WIDTH_1; + insn->bits3.da1.src1_vert_stride = BRW_VERTICAL_STRIDE_0; + } + else { + insn->bits3.da1.src1_horiz_stride = reg.hstride; + insn->bits3.da1.src1_width = reg.width; + insn->bits3.da1.src1_vert_stride = reg.vstride; + } + } + else { + insn->bits3.da16.src1_swz_x = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_X); + insn->bits3.da16.src1_swz_y = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Y); + insn->bits3.da16.src1_swz_z = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_Z); + insn->bits3.da16.src1_swz_w = BRW_GET_SWZ(reg.dw1.bits.swizzle, BRW_CHANNEL_W); + + /* This is an oddity of the fact we're using the same + * descriptions for registers in align_16 as align_1: + */ + if (reg.vstride == BRW_VERTICAL_STRIDE_8) + insn->bits3.da16.src1_vert_stride = BRW_VERTICAL_STRIDE_4; + else + insn->bits3.da16.src1_vert_stride = reg.vstride; + } + } +} + + + +static void brw_set_math_message( struct brw_instruction *insn, + uint32_t msg_length, + uint32_t response_length, + uint32_t function, + uint32_t integer_type, + int low_precision, + int saturate, + uint32_t dataType ) +{ + brw_set_src1 (insn, brw_imm_d (0)); + + insn->bits3.math.function = function; + insn->bits3.math.int_type = integer_type; + insn->bits3.math.precision = low_precision; + insn->bits3.math.saturate = saturate; + insn->bits3.math.data_type = dataType; + insn->bits3.math.response_length = response_length; + insn->bits3.math.msg_length = msg_length; + insn->bits3.math.msg_target = BRW_MESSAGE_TARGET_MATH; + insn->bits3.math.end_of_thread = 0; +} + +static void brw_set_urb_message( struct brw_instruction *insn, + int allocate, + int used, + uint32_t msg_length, + uint32_t response_length, + int end_of_thread, + int complete, + uint32_t offset, + uint32_t swizzle_control ) +{ + brw_set_src1 (insn, brw_imm_d (0)); + + insn->bits3.urb.opcode = 0; /* ? */ + insn->bits3.urb.offset = offset; + insn->bits3.urb.swizzle_control = swizzle_control; + insn->bits3.urb.allocate = allocate; + insn->bits3.urb.used = used; /* ? */ + insn->bits3.urb.complete = complete; + insn->bits3.urb.response_length = response_length; + insn->bits3.urb.msg_length = msg_length; + insn->bits3.urb.msg_target = BRW_MESSAGE_TARGET_URB; + insn->bits3.urb.end_of_thread = end_of_thread; +} + +void +brw_instruction_set_dp_write_message (struct brw_instruction *insn, + uint32_t binding_table_index, + uint32_t msg_control, + uint32_t msg_type, + uint32_t msg_length, + uint32_t pixel_scoreboard_clear, + uint32_t response_length, + uint32_t end_of_thread) +{ + brw_set_src1 (insn, brw_imm_d (0)); + + insn->bits3.dp_write.binding_table_index = binding_table_index; + insn->bits3.dp_write.msg_control = msg_control; + insn->bits3.dp_write.pixel_scoreboard_clear = pixel_scoreboard_clear; + insn->bits3.dp_write.msg_type = msg_type; + insn->bits3.dp_write.send_commit_msg = 0; + insn->bits3.dp_write.response_length = response_length; + insn->bits3.dp_write.msg_length = msg_length; + insn->bits3.dp_write.msg_target = BRW_MESSAGE_TARGET_DATAPORT_WRITE; + insn->bits3.urb.end_of_thread = end_of_thread; +} + +static void brw_set_dp_read_message( struct brw_instruction *insn, + uint32_t binding_table_index, + uint32_t msg_control, + uint32_t msg_type, + uint32_t target_cache, + uint32_t msg_length, + uint32_t response_length, + uint32_t end_of_thread ) +{ + brw_set_src1 (insn, brw_imm_d (0)); + + insn->bits3.dp_read.binding_table_index = binding_table_index; + insn->bits3.dp_read.msg_control = msg_control; + insn->bits3.dp_read.msg_type = msg_type; + insn->bits3.dp_read.target_cache = target_cache; + insn->bits3.dp_read.response_length = response_length; + insn->bits3.dp_read.msg_length = msg_length; + insn->bits3.dp_read.msg_target = BRW_MESSAGE_TARGET_DATAPORT_READ; + insn->bits3.dp_read.end_of_thread = end_of_thread; +} + +static void +brw_set_sampler_message (struct brw_instruction *insn, + cairo_bool_t is_g4x, + uint32_t binding_table_index, + uint32_t sampler, + uint32_t msg_type, + uint32_t response_length, + uint32_t msg_length, + cairo_bool_t eot) +{ + brw_set_src1 (insn, brw_imm_d (0)); + + if (is_g4x) { + /* XXX presume the driver is sane! */ + insn->bits3.sampler_g4x.binding_table_index = binding_table_index; + insn->bits3.sampler_g4x.sampler = sampler; + insn->bits3.sampler_g4x.msg_type = msg_type; + insn->bits3.sampler_g4x.response_length = response_length; + insn->bits3.sampler_g4x.msg_length = msg_length; + insn->bits3.sampler_g4x.end_of_thread = eot; + insn->bits3.sampler_g4x.msg_target = BRW_MESSAGE_TARGET_SAMPLER; + } else { + insn->bits3.sampler.binding_table_index = binding_table_index; + insn->bits3.sampler.sampler = sampler; + insn->bits3.sampler.msg_type = msg_type; + insn->bits3.sampler.return_format = BRW_SAMPLER_RETURN_FORMAT_FLOAT32; + insn->bits3.sampler.response_length = response_length; + insn->bits3.sampler.msg_length = msg_length; + insn->bits3.sampler.end_of_thread = eot; + insn->bits3.sampler.msg_target = BRW_MESSAGE_TARGET_SAMPLER; + } +} + +struct brw_instruction * +brw_next_instruction (struct brw_compile *p, + uint32_t opcode) +{ + struct brw_instruction *insn; + + assert(p->nr_insn + 1 < BRW_EU_MAX_INSN); + + insn = &p->store[p->nr_insn++]; + memcpy(insn, p->current, sizeof(*insn)); + + /* Reset this one-shot flag: */ + if (p->current->header.destreg__conditonalmod) { + p->current->header.destreg__conditonalmod = 0; + p->current->header.predicate_control = BRW_PREDICATE_NORMAL; + } + + insn->header.opcode = opcode; + return insn; +} + +static struct brw_instruction *brw_alu1( struct brw_compile *p, + uint32_t opcode, + struct brw_reg dest, + struct brw_reg src ) +{ + struct brw_instruction *insn = brw_next_instruction(p, opcode); + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src); + return insn; +} + +static struct brw_instruction *brw_alu2(struct brw_compile *p, + uint32_t opcode, + struct brw_reg dest, + struct brw_reg src0, + struct brw_reg src1 ) +{ + struct brw_instruction *insn = brw_next_instruction(p, opcode); + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src0); + brw_set_src1(insn, src1); + return insn; +} + + +/*********************************************************************** + * Convenience routines. + */ +#define ALU1(OP) \ + struct brw_instruction *brw_##OP(struct brw_compile *p, \ + struct brw_reg dest, \ + struct brw_reg src0) \ +{ \ + return brw_alu1(p, BRW_OPCODE_##OP, dest, src0); \ +} + +#define ALU2(OP) \ + struct brw_instruction *brw_##OP(struct brw_compile *p, \ + struct brw_reg dest, \ + struct brw_reg src0, \ + struct brw_reg src1) \ +{ \ + return brw_alu2(p, BRW_OPCODE_##OP, dest, src0, src1); \ +} + + + ALU1(MOV) + ALU2(SEL) + ALU1(NOT) + ALU2(AND) + ALU2(OR) + ALU2(XOR) + ALU2(SHR) + ALU2(SHL) + ALU2(RSR) + ALU2(RSL) + ALU2(ASR) + ALU2(ADD) + ALU2(MUL) + ALU1(FRC) + ALU1(RNDD) + ALU1(RNDZ) + ALU2(MAC) + ALU2(MACH) + ALU1(LZD) + ALU2(DP4) + ALU2(DPH) + ALU2(DP3) + ALU2(DP2) +ALU2(LINE) + + + + +void brw_NOP(struct brw_compile *p) +{ + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_NOP); + brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); + brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); + brw_set_src1(insn, brw_imm_ud(0x0)); +} + + + + + +/*********************************************************************** + * Comparisons, if/else/endif + */ + +struct brw_instruction *brw_JMPI(struct brw_compile *p, + struct brw_reg dest, + struct brw_reg src0, + struct brw_reg src1) +{ + struct brw_instruction *insn = brw_alu2(p, BRW_OPCODE_JMPI, dest, src0, src1); + + p->current->header.predicate_control = BRW_PREDICATE_NONE; + + return insn; +} + +/* EU takes the value from the flag register and pushes it onto some + * sort of a stack (presumably merging with any flag value already on + * the stack). Within an if block, the flags at the top of the stack + * control execution on each channel of the unit, eg. on each of the + * 16 pixel values in our wm programs. + * + * When the matching 'else' instruction is reached (presumably by + * countdown of the instruction count patched in by our ELSE/ENDIF + * functions), the relevant flags are inverted. + * + * When the matching 'endif' instruction is reached, the flags are + * popped off. If the stack is now empty, normal execution resumes. + * + * No attempt is made to deal with stack overflow (14 elements?). + */ +struct brw_instruction *brw_IF(struct brw_compile *p, uint32_t execute_size) +{ + struct brw_instruction *insn; + + if (p->single_program_flow) { + assert(execute_size == BRW_EXECUTE_1); + + insn = brw_next_instruction(p, BRW_OPCODE_ADD); + insn->header.predicate_inverse = 1; + } else { + insn = brw_next_instruction(p, BRW_OPCODE_IF); + } + + /* Override the defaults for this instruction: + */ + brw_instruction_set_destination (insn, brw_ip_reg ()); + brw_instruction_set_source0 (insn, brw_ip_reg ()); + brw_set_src1 (insn, brw_imm_d (0)); + + insn->header.execution_size = execute_size; + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.predicate_control = BRW_PREDICATE_NORMAL; + insn->header.mask_control = BRW_MASK_ENABLE; + if (!p->single_program_flow) + insn->header.thread_control = BRW_THREAD_SWITCH; + + p->current->header.predicate_control = BRW_PREDICATE_NONE; + + return insn; +} + + +struct brw_instruction *brw_ELSE(struct brw_compile *p, + struct brw_instruction *if_insn) +{ + struct brw_instruction *insn; + + if (p->single_program_flow) { + insn = brw_next_instruction(p, BRW_OPCODE_ADD); + } else { + insn = brw_next_instruction(p, BRW_OPCODE_ELSE); + } + + brw_instruction_set_destination (insn, brw_ip_reg ()); + brw_instruction_set_source0 (insn, brw_ip_reg ()); + brw_set_src1 (insn, brw_imm_d (0)); + + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.execution_size = if_insn->header.execution_size; + insn->header.mask_control = BRW_MASK_ENABLE; + if (!p->single_program_flow) + insn->header.thread_control = BRW_THREAD_SWITCH; + + /* Patch the if instruction to point at this instruction. + */ + if (p->single_program_flow) { + assert(if_insn->header.opcode == BRW_OPCODE_ADD); + + if_insn->bits3.ud = (insn - if_insn + 1) * 16; + } else { + assert(if_insn->header.opcode == BRW_OPCODE_IF); + + if_insn->bits3.if_else.jump_count = insn - if_insn; + if_insn->bits3.if_else.pop_count = 1; + if_insn->bits3.if_else.pad0 = 0; + } + + return insn; +} + +void brw_ENDIF(struct brw_compile *p, + struct brw_instruction *patch_insn) +{ + if (p->single_program_flow) { + /* In single program flow mode, there's no need to execute an ENDIF, + * since we don't need to do any stack operations, and if we're executing + * currently, we want to just continue executing. + */ + struct brw_instruction *next = &p->store[p->nr_insn]; + + assert(patch_insn->header.opcode == BRW_OPCODE_ADD); + + patch_insn->bits3.ud = (next - patch_insn) * 16; + } else { + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_ENDIF); + + brw_instruction_set_destination(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); + brw_instruction_set_source0(insn, retype(brw_vec4_grf(0,0), BRW_REGISTER_TYPE_UD)); + brw_set_src1 (insn, brw_imm_d (0)); + + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.execution_size = patch_insn->header.execution_size; + insn->header.mask_control = BRW_MASK_ENABLE; + insn->header.thread_control = BRW_THREAD_SWITCH; + + assert(patch_insn->bits3.if_else.jump_count == 0); + + /* Patch the if or else instructions to point at this or the next + * instruction respectively. + */ + if (patch_insn->header.opcode == BRW_OPCODE_IF) { + /* Automagically turn it into an IFF: + */ + patch_insn->header.opcode = BRW_OPCODE_IFF; + patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1; + patch_insn->bits3.if_else.pop_count = 0; + patch_insn->bits3.if_else.pad0 = 0; + } else if (patch_insn->header.opcode == BRW_OPCODE_ELSE) { + patch_insn->bits3.if_else.jump_count = insn - patch_insn + 1; + patch_insn->bits3.if_else.pop_count = 1; + patch_insn->bits3.if_else.pad0 = 0; + } else { + assert(0); + } + + /* Also pop item off the stack in the endif instruction: + */ + insn->bits3.if_else.jump_count = 0; + insn->bits3.if_else.pop_count = 1; + insn->bits3.if_else.pad0 = 0; + } +} + +struct brw_instruction *brw_BREAK(struct brw_compile *p) +{ + struct brw_instruction *insn; + insn = brw_next_instruction(p, BRW_OPCODE_BREAK); + brw_instruction_set_destination(insn, brw_ip_reg()); + brw_instruction_set_source0(insn, brw_ip_reg()); + brw_set_src1(insn, brw_imm_d (0)); + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.execution_size = BRW_EXECUTE_8; + /* insn->header.mask_control = BRW_MASK_DISABLE; */ + insn->bits3.if_else.pad0 = 0; + return insn; +} + +struct brw_instruction *brw_CONT(struct brw_compile *p) +{ + struct brw_instruction *insn; + insn = brw_next_instruction(p, BRW_OPCODE_CONTINUE); + brw_instruction_set_destination(insn, brw_ip_reg()); + brw_instruction_set_source0(insn, brw_ip_reg()); + brw_set_src1 (insn, brw_imm_d (0)); + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.execution_size = BRW_EXECUTE_8; + /* insn->header.mask_control = BRW_MASK_DISABLE; */ + insn->bits3.if_else.pad0 = 0; + return insn; +} + +/* DO/WHILE loop: +*/ +struct brw_instruction *brw_DO(struct brw_compile *p, uint32_t execute_size) +{ + if (p->single_program_flow) { + return &p->store[p->nr_insn]; + } else { + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_DO); + + /* Override the defaults for this instruction: + */ + brw_instruction_set_destination(insn, brw_null_reg()); + brw_instruction_set_source0(insn, brw_null_reg()); + brw_set_src1(insn, brw_null_reg()); + + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.execution_size = execute_size; + insn->header.predicate_control = BRW_PREDICATE_NONE; + /* insn->header.mask_control = BRW_MASK_ENABLE; */ + /* insn->header.mask_control = BRW_MASK_DISABLE; */ + + return insn; + } +} + + + +struct brw_instruction *brw_WHILE(struct brw_compile *p, + struct brw_instruction *do_insn) +{ + struct brw_instruction *insn; + + if (p->single_program_flow) + insn = brw_next_instruction(p, BRW_OPCODE_ADD); + else + insn = brw_next_instruction(p, BRW_OPCODE_WHILE); + + brw_instruction_set_destination(insn, brw_ip_reg()); + brw_instruction_set_source0(insn, brw_ip_reg()); + brw_set_src1 (insn, brw_imm_d (0)); + + insn->header.compression_control = BRW_COMPRESSION_NONE; + + if (p->single_program_flow) { + insn->header.execution_size = BRW_EXECUTE_1; + + insn->bits3.d = (do_insn - insn) * 16; + } else { + insn->header.execution_size = do_insn->header.execution_size; + + assert(do_insn->header.opcode == BRW_OPCODE_DO); + insn->bits3.if_else.jump_count = do_insn - insn + 1; + insn->bits3.if_else.pop_count = 0; + insn->bits3.if_else.pad0 = 0; + } + + /* insn->header.mask_control = BRW_MASK_ENABLE; */ + + /* insn->header.mask_control = BRW_MASK_DISABLE; */ + p->current->header.predicate_control = BRW_PREDICATE_NONE; + return insn; +} + + +/* FORWARD JUMPS: +*/ +void brw_land_fwd_jump(struct brw_compile *p, + struct brw_instruction *jmp_insn) +{ + struct brw_instruction *landing = &p->store[p->nr_insn]; + + assert(jmp_insn->header.opcode == BRW_OPCODE_JMPI); + assert(jmp_insn->bits1.da1.src1_reg_file = BRW_IMMEDIATE_VALUE); + + jmp_insn->bits3.ud = (landing - jmp_insn) - 1; +} + + + +/* To integrate with the above, it makes sense that the comparison + * instruction should populate the flag register. It might be simpler + * just to use the flag reg for most WM tasks? + */ +void brw_CMP(struct brw_compile *p, + struct brw_reg dest, + uint32_t conditional, + struct brw_reg src0, + struct brw_reg src1) +{ + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_CMP); + + insn->header.destreg__conditonalmod = conditional; + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src0); + brw_set_src1(insn, src1); + + /* guess_execution_size(insn, src0); */ + + + /* Make it so that future instructions will use the computed flag + * value until brw_set_predicate_control_flag_value() is called + * again. + */ + if (dest.file == BRW_ARCHITECTURE_REGISTER_FILE && + dest.nr == 0) { + p->current->header.predicate_control = BRW_PREDICATE_NORMAL; + p->flag_value = 0xff; + } +} + + + +/*********************************************************************** + * Helpers for the various SEND message types: + */ + +/* Invert 8 values +*/ +void brw_math( struct brw_compile *p, + struct brw_reg dest, + uint32_t function, + uint32_t saturate, + uint32_t msg_reg_nr, + struct brw_reg src, + uint32_t data_type, + uint32_t precision ) +{ + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); + uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1; + uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1; + + /* Example code doesn't set predicate_control for send + * instructions. + */ + insn->header.predicate_control = 0; + insn->header.destreg__conditonalmod = msg_reg_nr; + + response_length = 1; + + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src); + brw_set_math_message(insn, + msg_length, response_length, + function, + BRW_MATH_INTEGER_UNSIGNED, + precision, + saturate, + data_type); +} + +/* Use 2 send instructions to invert 16 elements +*/ +void brw_math_16( struct brw_compile *p, + struct brw_reg dest, + uint32_t function, + uint32_t saturate, + uint32_t msg_reg_nr, + struct brw_reg src, + uint32_t precision ) +{ + struct brw_instruction *insn; + uint32_t msg_length = (function == BRW_MATH_FUNCTION_POW) ? 2 : 1; + uint32_t response_length = (function == BRW_MATH_FUNCTION_SINCOS) ? 2 : 1; + + /* First instruction: + */ + brw_push_insn_state(p); + brw_set_predicate_control_flag_value(p, 0xff); + brw_set_compression_control(p, BRW_COMPRESSION_NONE); + + insn = brw_next_instruction(p, BRW_OPCODE_SEND); + insn->header.destreg__conditonalmod = msg_reg_nr; + + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src); + brw_set_math_message(insn, + msg_length, response_length, + function, + BRW_MATH_INTEGER_UNSIGNED, + precision, + saturate, + BRW_MATH_DATA_VECTOR); + + /* Second instruction: + */ + insn = brw_next_instruction(p, BRW_OPCODE_SEND); + insn->header.compression_control = BRW_COMPRESSION_2NDHALF; + insn->header.destreg__conditonalmod = msg_reg_nr+1; + + brw_instruction_set_destination(insn, offset(dest,1)); + brw_instruction_set_source0(insn, src); + brw_set_math_message(insn, + msg_length, response_length, + function, + BRW_MATH_INTEGER_UNSIGNED, + precision, + saturate, + BRW_MATH_DATA_VECTOR); + + brw_pop_insn_state(p); +} + + + + +void brw_dp_WRITE_16( struct brw_compile *p, + struct brw_reg src, + uint32_t msg_reg_nr, + uint32_t scratch_offset ) +{ + { + brw_push_insn_state(p); + brw_set_mask_control(p, BRW_MASK_DISABLE); + brw_set_compression_control(p, BRW_COMPRESSION_NONE); + + brw_MOV (p, + retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D), + brw_imm_d (scratch_offset)); + + brw_pop_insn_state(p); + } + + { + uint32_t msg_length = 3; + struct brw_reg dest = retype(brw_null_reg(), BRW_REGISTER_TYPE_UW); + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); + + insn->header.predicate_control = 0; /* XXX */ + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.destreg__conditonalmod = msg_reg_nr; + + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src); + + brw_instruction_set_dp_write_message(insn, + 255, /* bti */ + BRW_DATAPORT_OWORD_BLOCK_4_OWORDS, /* msg_control */ + BRW_DATAPORT_WRITE_MESSAGE_OWORD_BLOCK_WRITE, /* msg_type */ + msg_length, + 0, /* pixel scoreboard */ + 0, /* response_length */ + 0); /* eot */ + } + +} + + +void brw_dp_READ_16( struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + uint32_t scratch_offset ) +{ + { + brw_push_insn_state(p); + brw_set_compression_control(p, BRW_COMPRESSION_NONE); + brw_set_mask_control(p, BRW_MASK_DISABLE); + + brw_MOV (p, + retype (brw_vec1_grf (0, 2), BRW_REGISTER_TYPE_D), + brw_imm_d (scratch_offset)); + + brw_pop_insn_state(p); + } + + { + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); + + insn->header.predicate_control = 0; /* XXX */ + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.destreg__conditonalmod = msg_reg_nr; + + brw_instruction_set_destination(insn, dest); /* UW? */ + brw_instruction_set_source0(insn, retype(brw_vec8_grf(0), BRW_REGISTER_TYPE_UW)); + + brw_set_dp_read_message(insn, + 255, /* bti */ + 3, /* msg_control */ + BRW_DATAPORT_READ_MESSAGE_OWORD_BLOCK_READ, /* msg_type */ + 1, /* target cache */ + 1, /* msg_length */ + 2, /* response_length */ + 0); /* eot */ + } +} + + +void brw_fb_WRITE(struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + struct brw_reg src0, + uint32_t binding_table_index, + uint32_t msg_length, + uint32_t response_length, + int eot) +{ + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); + + insn->header.predicate_control = 0; /* XXX */ + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.destreg__conditonalmod = msg_reg_nr; + + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src0); + brw_instruction_set_dp_write_message(insn, + binding_table_index, + BRW_DATAPORT_RENDER_TARGET_WRITE_SIMD16_SINGLE_SOURCE, /* msg_control */ + BRW_DATAPORT_WRITE_MESSAGE_RENDER_TARGET_WRITE, /* msg_type */ + msg_length, + 1, /* pixel scoreboard */ + response_length, + eot); +} + + + +void brw_SAMPLE (struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + struct brw_reg src0, + uint32_t binding_table_index, + uint32_t sampler, + uint32_t writemask, + uint32_t msg_type, + uint32_t response_length, + uint32_t msg_length, + cairo_bool_t eot) +{ + int need_stall = 0; + + if(writemask == 0) { + /* printf("%s: zero writemask??\n", __FUNCTION__); */ + return; + } + + /* Hardware doesn't do destination dependency checking on send + * instructions properly. Add a workaround which generates the + * dependency by other means. In practice it seems like this bug + * only crops up for texture samples, and only where registers are + * written by the send and then written again later without being + * read in between. Luckily for us, we already track that + * information and use it to modify the writemask for the + * instruction, so that is a guide for whether a workaround is + * needed. + */ + if (writemask != WRITEMASK_XYZW) { + uint32_t dst_offset = 0; + uint32_t i, newmask = 0, len = 0; + + for (i = 0; i < 4; i++) { + if (writemask & (1<header.predicate_control = 0; /* XXX */ + insn->header.compression_control = BRW_COMPRESSION_NONE; + insn->header.destreg__conditonalmod = msg_reg_nr; + + brw_instruction_set_destination(insn, dest); + brw_instruction_set_source0(insn, src0); + brw_set_sampler_message (insn, p->is_g4x, + binding_table_index, + sampler, + msg_type, + response_length, + msg_length, + eot); + } + + if (need_stall) + { + struct brw_reg reg = vec8(offset(dest, response_length-1)); + + /* mov (8) r9.0<1>:f r9.0<8;8,1>:f { Align1 } + */ + brw_push_insn_state(p); + brw_set_compression_control(p, 0); + brw_MOV(p, reg, reg); + brw_pop_insn_state(p); + } +} + +/* All these variables are pretty confusing - we might be better off + * using bitmasks and macros for this, in the old style. Or perhaps + * just having the caller instantiate the fields in dword3 itself. + */ +void brw_urb_WRITE(struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + struct brw_reg src0, + int allocate, + int used, + uint32_t msg_length, + uint32_t response_length, + int eot, + int writes_complete, + uint32_t offset, + uint32_t swizzle) +{ + struct brw_instruction *insn = brw_next_instruction(p, BRW_OPCODE_SEND); + + assert(msg_length < 16); + + brw_instruction_set_destination (insn, dest); + brw_instruction_set_source0 (insn, src0); + brw_set_src1 (insn, brw_imm_d (0)); + + insn->header.destreg__conditonalmod = msg_reg_nr; + + brw_set_urb_message (insn, + allocate, + used, + msg_length, + response_length, + eot, + writes_complete, + offset, + swizzle); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-util.c b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-util.c new file mode 100644 index 000000000000..592235b12bf5 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu-util.c @@ -0,0 +1,121 @@ +/* + Copyright (C) Intel Corp. 2006. All Rights Reserved. + Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + develop this 3D driver. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **********************************************************************/ +/* + * Authors: + * Keith Whitwell + */ + + +#include "cairoint.h" +#include "cairo-drm-intel-brw-eu.h" + + +void brw_math_invert( struct brw_compile *p, + struct brw_reg dst, + struct brw_reg src) +{ + brw_math( p, + dst, + BRW_MATH_FUNCTION_INV, + BRW_MATH_SATURATE_NONE, + 0, + src, + BRW_MATH_PRECISION_FULL, + BRW_MATH_DATA_VECTOR ); +} + + + +void brw_copy4(struct brw_compile *p, + struct brw_reg dst, + struct brw_reg src, + uint32_t count) +{ + uint32_t i; + + dst = vec4(dst); + src = vec4(src); + + for (i = 0; i < count; i++) + { + uint32_t delta = i*32; + brw_MOV(p, byte_offset(dst, delta), byte_offset(src, delta)); + brw_MOV(p, byte_offset(dst, delta+16), byte_offset(src, delta+16)); + } +} + + +void brw_copy8(struct brw_compile *p, + struct brw_reg dst, + struct brw_reg src, + uint32_t count) +{ + uint32_t i; + + dst = vec8(dst); + src = vec8(src); + + for (i = 0; i < count; i++) + { + uint32_t delta = i*32; + brw_MOV(p, byte_offset(dst, delta), byte_offset(src, delta)); + } +} + + +void brw_copy_indirect_to_indirect(struct brw_compile *p, + struct brw_indirect dst_ptr, + struct brw_indirect src_ptr, + uint32_t count) +{ + uint32_t i; + + for (i = 0; i < count; i++) + { + uint32_t delta = i*32; + brw_MOV(p, deref_4f(dst_ptr, delta), deref_4f(src_ptr, delta)); + brw_MOV(p, deref_4f(dst_ptr, delta+16), deref_4f(src_ptr, delta+16)); + } +} + + +void brw_copy_from_indirect(struct brw_compile *p, + struct brw_reg dst, + struct brw_indirect ptr, + uint32_t count) +{ + uint32_t i; + + dst = vec4(dst); + + for (i = 0; i < count; i++) + { + uint32_t delta = i*32; + brw_MOV(p, byte_offset(dst, delta), deref_4f(ptr, delta)); + brw_MOV(p, byte_offset(dst, delta+16), deref_4f(ptr, delta+16)); + } +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.c b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.c new file mode 100644 index 000000000000..2b47d8c37e3b --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.c @@ -0,0 +1,250 @@ +/* + Copyright (C) Intel Corp. 2006. All Rights Reserved. + Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + develop this 3D driver. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **********************************************************************/ +/* + * Authors: + * Keith Whitwell + */ + +#include "cairoint.h" +#include "cairo-drm-intel-brw-eu.h" + +#include +#include +#include + + +/* How does predicate control work when execution_size != 8? Do I + * need to test/set for 0xffff when execution_size is 16? + */ +void brw_set_predicate_control_flag_value( struct brw_compile *p, uint32_t value ) +{ + p->current->header.predicate_control = BRW_PREDICATE_NONE; + + if (value != 0xff) { + if (value != p->flag_value) { + brw_push_insn_state(p); + brw_MOV(p, brw_flag_reg(), brw_imm_uw(value)); + p->flag_value = value; + brw_pop_insn_state(p); + } + + p->current->header.predicate_control = BRW_PREDICATE_NORMAL; + } +} + +void brw_set_predicate_control( struct brw_compile *p, uint32_t pc ) +{ + p->current->header.predicate_control = pc; +} + +void brw_set_conditionalmod( struct brw_compile *p, uint32_t conditional ) +{ + p->current->header.destreg__conditonalmod = conditional; +} + +void brw_set_access_mode( struct brw_compile *p, uint32_t access_mode ) +{ + p->current->header.access_mode = access_mode; +} + +void brw_set_compression_control( struct brw_compile *p, int compression_control ) +{ + p->current->header.compression_control = compression_control; +} + +void brw_set_mask_control( struct brw_compile *p, uint32_t value ) +{ + p->current->header.mask_control = value; +} + +void brw_set_saturate( struct brw_compile *p, uint32_t value ) +{ + p->current->header.saturate = value; +} + +void brw_push_insn_state( struct brw_compile *p ) +{ + assert(p->current != &p->stack[BRW_EU_MAX_INSN_STACK-1]); + memcpy(p->current+1, p->current, sizeof(struct brw_instruction)); + p->current++; +} + +void brw_pop_insn_state( struct brw_compile *p ) +{ + assert(p->current != p->stack); + p->current--; +} + +/************************************************************************/ +void +brw_compile_init (struct brw_compile *p, + cairo_bool_t is_g4x) +{ + p->nr_insn = 0; + p->current = p->stack; + memset (p->current, 0, sizeof (p->current[0])); + + p->is_g4x = is_g4x; + + /* Some defaults? */ + brw_set_mask_control (p, BRW_MASK_ENABLE); /* what does this do? */ + brw_set_saturate (p, 0); + brw_set_compression_control (p, BRW_COMPRESSION_NONE); + brw_set_predicate_control_flag_value (p, 0xff); +} + +const uint32_t * +brw_get_program (struct brw_compile *p, + uint32_t *sz) +{ + *sz = p->nr_insn * sizeof (struct brw_instruction); + return (const uint32_t *)p->store; +} + + + +/* + * Subroutine calls require special attention. + * Mesa instructions may be expanded into multiple hardware instructions + * so the prog_instruction::BranchTarget field can't be used as an index + * into the hardware instructions. + * + * The BranchTarget field isn't needed, however. Mesa's GLSL compiler + * emits CAL and BGNSUB instructions with labels that can be used to map + * subroutine calls to actual subroutine code blocks. + * + * The structures and function here implement patching of CAL instructions + * so they jump to the right subroutine code... + */ + + +/* + * For each OPCODE_BGNSUB we create one of these. + */ +struct brw_glsl_label +{ + const char *name; /*< the label string */ + uint32_t position; /*< the position of the brw instruction for this label */ + struct brw_glsl_label *next; /*< next in linked list */ +}; + + +/* + * For each OPCODE_CAL we create one of these. + */ +struct brw_glsl_call +{ + uint32_t call_inst_pos; /*< location of the CAL instruction */ + const char *sub_name; /*< name of subroutine to call */ + struct brw_glsl_call *next; /*< next in linked list */ +}; + + +/* + * Called for each OPCODE_BGNSUB. + */ + void +brw_save_label(struct brw_compile *c, const char *name, uint32_t position) +{ + struct brw_glsl_label *label = calloc(1, sizeof *label); + label->name = name; + label->position = position; + label->next = c->first_label; + c->first_label = label; +} + + +/* + * Called for each OPCODE_CAL. + */ + void +brw_save_call(struct brw_compile *c, const char *name, uint32_t call_pos) +{ + struct brw_glsl_call *call = calloc(1, sizeof *call); + call->call_inst_pos = call_pos; + call->sub_name = name; + call->next = c->first_call; + c->first_call = call; +} + + +/* + * Lookup a label, return label's position/offset. + */ + static uint32_t +brw_lookup_label(struct brw_compile *c, const char *name) +{ + const struct brw_glsl_label *label; + for (label = c->first_label; label; label = label->next) { + if (strcmp(name, label->name) == 0) { + return label->position; + } + } + abort(); /* should never happen */ + return ~0; +} + + +/* + * When we're done generating code, this function is called to resolve + * subroutine calls. + */ + void +brw_resolve_cals(struct brw_compile *c) +{ + const struct brw_glsl_call *call; + + for (call = c->first_call; call; call = call->next) { + const uint32_t sub_loc = brw_lookup_label(c, call->sub_name); + struct brw_instruction *brw_call_inst = &c->store[call->call_inst_pos]; + struct brw_instruction *brw_sub_inst = &c->store[sub_loc]; + int32_t offset = brw_sub_inst - brw_call_inst; + + /* patch brw_inst1 to point to brw_inst2 */ + brw_set_src1(brw_call_inst, brw_imm_d(offset * 16)); + } + + /* free linked list of calls */ + { + struct brw_glsl_call *call, *next; + for (call = c->first_call; call; call = next) { + next = call->next; + free(call); + } + c->first_call = NULL; + } + + /* free linked list of labels */ + { + struct brw_glsl_label *label, *next; + for (label = c->first_label; label; label = next) { + next = label->next; + free(label); + } + c->first_label = NULL; + } +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.h new file mode 100644 index 000000000000..ef6e9771e3ca --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.h @@ -0,0 +1,1044 @@ +/* + Copyright (C) Intel Corp. 2006. All Rights Reserved. + Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + develop this 3D driver. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **********************************************************************/ +/* + * Authors: + * Keith Whitwell + */ + +#ifndef CAIRO_DRM_INTEL_BRW_EU_H +#define CAIRO_DRM_INTEL_BRW_EU_H + +#include "cairo.h" +#include "cairo-drm-intel-brw-structs.h" +#include "cairo-drm-intel-brw-defines.h" + +#include + + +/* + * Writemask values, 1 bit per component. + */ +#define WRITEMASK_X 0x1 +#define WRITEMASK_Y 0x2 +#define WRITEMASK_Z 0x4 +#define WRITEMASK_W 0x8 +#define WRITEMASK_XY (WRITEMASK_X | WRITEMASK_Y) +#define WRITEMASK_XZ (WRITEMASK_X | WRITEMASK_Z) +#define WRITEMASK_YZ (WRITEMASK_Y | WRITEMASK_Z) +#define WRITEMASK_XYZ (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z) +#define WRITEMASK_XW (WRITEMASK_X | WRITEMASK_W) +#define WRITEMASK_YW (WRITEMASK_Y | WRITEMASK_W) +#define WRITEMASK_XYW (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_W) +#define WRITEMASK_ZW (WRITEMASK_Z | WRITEMASK_W) +#define WRITEMASK_XZW (WRITEMASK_X | WRITEMASK_Z | WRITEMASK_W) +#define WRITEMASK_YZW (WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W) +#define WRITEMASK_XYZW (WRITEMASK_X | WRITEMASK_Y | WRITEMASK_Z | WRITEMASK_W) + +#define BRW_SWIZZLE4(a,b,c,d) (((a)<<0) | ((b)<<2) | ((c)<<4) | ((d)<<6)) +#define BRW_GET_SWZ(swz, idx) (((swz) >> ((idx)*2)) & 0x3) + +#define BRW_SWIZZLE_NOOP BRW_SWIZZLE4 (0,1,2,3) +#define BRW_SWIZZLE_XYZW BRW_SWIZZLE4 (0,1,2,3) +#define BRW_SWIZZLE_XXXX BRW_SWIZZLE4 (0,0,0,0) +#define BRW_SWIZZLE_XYXY BRW_SWIZZLE4 (0,1,0,1) + +#define REG_SIZE (8*4) + +/* These aren't hardware structs, just something useful for us to pass around: + * + * Align1 operation has a lot of control over input ranges. Used in + * WM programs to implement shaders decomposed into "channel serial" + * or "structure of array" form: + */ +struct brw_reg { + uint32_t type:4; + uint32_t file:2; + uint32_t nr:8; + uint32_t subnr:5; /* :1 in align16 */ + uint32_t negate:1; /* source only */ + uint32_t abs:1; /* source only */ + uint32_t vstride:4; /* source only */ + uint32_t width:3; /* src only, align1 only */ + uint32_t hstride:2; /* align1 only */ + uint32_t address_mode:1; /* relative addressing, hopefully! */ + uint32_t pad0:1; + + union { + struct { + uint32_t swizzle:8; /* src only, align16 only */ + uint32_t writemask:4; /* dest only, align16 only */ + int32_t indirect_offset:10; /* relative addressing offset */ + uint32_t pad1:10; /* two dwords total */ + } bits; + + float f; + int32_t d; + uint32_t ud; + } dw1; +}; + +struct brw_indirect { + uint32_t addr_subnr:4; + int32_t addr_offset:10; + uint32_t pad:18; +}; + +struct brw_glsl_label; +struct brw_glsl_call; + +#define BRW_EU_MAX_INSN_STACK 5 +#define BRW_EU_MAX_INSN 200 + +struct brw_compile { + struct brw_instruction store[BRW_EU_MAX_INSN]; + uint32_t nr_insn; + + cairo_bool_t is_g4x; + + /* Allow clients to push/pop instruction state: + */ + struct brw_instruction stack[BRW_EU_MAX_INSN_STACK]; + struct brw_instruction *current; + + uint32_t flag_value; + int single_program_flow; + struct brw_context *brw; + + struct brw_glsl_label *first_label; /*< linked list of labels */ + struct brw_glsl_call *first_call; /*< linked list of CALs */ +}; + +cairo_private void +brw_save_label (struct brw_compile *c, + const char *name, + uint32_t position); + +cairo_private void +brw_save_call (struct brw_compile *c, + const char *name, + uint32_t call_pos); + +cairo_private void +brw_resolve_cals (struct brw_compile *c); + +static cairo_always_inline int +type_sz (uint32_t type) +{ + switch (type) { + case BRW_REGISTER_TYPE_UD: + case BRW_REGISTER_TYPE_D: + case BRW_REGISTER_TYPE_F: + return 4; + case BRW_REGISTER_TYPE_HF: + case BRW_REGISTER_TYPE_UW: + case BRW_REGISTER_TYPE_W: + return 2; + case BRW_REGISTER_TYPE_UB: + case BRW_REGISTER_TYPE_B: + return 1; + default: + return 0; + } +} + +/* + * Construct a brw_reg. + * \param file one of the BRW_x_REGISTER_FILE values + * \param nr register number/index + * \param subnr register sub number + * \param type one of BRW_REGISTER_TYPE_x + * \param vstride one of BRW_VERTICAL_STRIDE_x + * \param width one of BRW_WIDTH_x + * \param hstride one of BRW_HORIZONTAL_STRIDE_x + * \param swizzle one of BRW_SWIZZLE_x + * \param writemask WRITEMASK_X/Y/Z/W bitfield + */ +static cairo_always_inline struct brw_reg +brw_reg (uint32_t file, + uint32_t nr, + uint32_t subnr, + uint32_t type, + uint32_t vstride, + uint32_t width, + uint32_t hstride, + uint32_t swizzle, + uint32_t writemask) +{ + struct brw_reg reg; + + if (type == BRW_GENERAL_REGISTER_FILE) + assert(nr < 128); + else if (type == BRW_MESSAGE_REGISTER_FILE) + assert(nr < 9); + else if (type == BRW_ARCHITECTURE_REGISTER_FILE) + assert(nr <= BRW_ARF_IP); + + reg.type = type; + reg.file = file; + reg.nr = nr; + reg.subnr = subnr * type_sz(type); + reg.negate = 0; + reg.abs = 0; + reg.vstride = vstride; + reg.width = width; + reg.hstride = hstride; + reg.address_mode = BRW_ADDRESS_DIRECT; + reg.pad0 = 0; + + /* Could do better: If the reg is r5.3<0;1,0>, we probably want to + * set swizzle and writemask to W, as the lower bits of subnr will + * be lost when converted to align16. This is probably too much to + * keep track of as you'd want it adjusted by suboffset(), etc. + * Perhaps fix up when converting to align16? + */ + reg.dw1.bits.swizzle = swizzle; + reg.dw1.bits.writemask = writemask; + reg.dw1.bits.indirect_offset = 0; + reg.dw1.bits.pad1 = 0; + + return reg; +} + +/* Construct float[16] register */ +static cairo_always_inline struct brw_reg +brw_vec16_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return brw_reg (file, nr, subnr, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_16, + BRW_WIDTH_16, + BRW_HORIZONTAL_STRIDE_1, + BRW_SWIZZLE_XYZW, + WRITEMASK_XYZW); +} + +/* Construct float[8] register */ +static cairo_always_inline struct brw_reg +brw_vec8_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return brw_reg (file, nr, subnr, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_8, + BRW_WIDTH_8, + BRW_HORIZONTAL_STRIDE_1, + BRW_SWIZZLE_XYZW, + WRITEMASK_XYZW); +} + +/* Construct float[4] register */ +static cairo_always_inline struct brw_reg +brw_vec4_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return brw_reg (file, nr, subnr, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_4, + BRW_WIDTH_4, + BRW_HORIZONTAL_STRIDE_1, + BRW_SWIZZLE_XYZW, + WRITEMASK_XYZW); +} + +/* Construct float[2] register */ +static cairo_always_inline struct brw_reg +brw_vec2_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return brw_reg (file, nr, subnr, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_2, + BRW_WIDTH_2, + BRW_HORIZONTAL_STRIDE_1, + BRW_SWIZZLE_XYXY, + WRITEMASK_XY); +} + +/* Construct float[1] register */ +static cairo_always_inline struct brw_reg +brw_vec1_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return brw_reg (file, nr, subnr, + BRW_REGISTER_TYPE_F, + BRW_VERTICAL_STRIDE_0, + BRW_WIDTH_1, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_XXXX, + WRITEMASK_X); +} + +static cairo_always_inline struct brw_reg +retype (struct brw_reg reg, + uint32_t type) +{ + reg.type = type; + return reg; +} + +static cairo_always_inline struct brw_reg +suboffset (struct brw_reg reg, + uint32_t delta) +{ + reg.subnr += delta * type_sz (reg.type); + return reg; +} + +static cairo_always_inline struct brw_reg +offset (struct brw_reg reg, + uint32_t delta) +{ + reg.nr += delta; + return reg; +} + +static cairo_always_inline struct brw_reg +byte_offset (struct brw_reg reg, + uint32_t bytes) +{ + uint32_t newoffset = reg.nr * REG_SIZE + reg.subnr + bytes; + reg.nr = newoffset / REG_SIZE; + reg.subnr = newoffset % REG_SIZE; + return reg; +} + +/* Construct unsigned word[16] register */ +static cairo_always_inline struct brw_reg +brw_uw16_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return suboffset (retype (brw_vec16_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); +} + +/* Construct unsigned word[8] register */ +static cairo_always_inline struct brw_reg +brw_uw8_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return suboffset (retype (brw_vec8_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); +} + +/* Construct unsigned word[2] register */ +static cairo_always_inline struct brw_reg +brw_uw2_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return suboffset (retype (brw_vec2_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); +} + +/* Construct unsigned word[1] register */ +static cairo_always_inline struct brw_reg +brw_uw1_reg (uint32_t file, + uint32_t nr, + uint32_t subnr) +{ + return suboffset (retype (brw_vec1_reg (file, nr, 0), BRW_REGISTER_TYPE_UW), subnr); +} + +static cairo_always_inline struct brw_reg +brw_imm_reg (uint32_t type) +{ + return brw_reg (BRW_IMMEDIATE_VALUE, + 0, + 0, + type, + BRW_VERTICAL_STRIDE_0, + BRW_WIDTH_1, + BRW_HORIZONTAL_STRIDE_0, + 0, + 0); +} + +/* Construct float immediate register */ +static cairo_always_inline struct brw_reg brw_imm_f( float f ) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_F); + imm.dw1.f = f; + return imm; +} + +/* Construct integer immediate register */ +static cairo_always_inline struct brw_reg brw_imm_d( int32_t d ) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_D); + imm.dw1.d = d; + return imm; +} + +/* Construct uint immediate register */ +static cairo_always_inline struct brw_reg brw_imm_ud( uint32_t ud ) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UD); + imm.dw1.ud = ud; + return imm; +} + +/* Construct ushort immediate register */ +static cairo_always_inline struct brw_reg brw_imm_uw( uint16_t uw ) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_UW); + imm.dw1.ud = uw | (uw << 16); + return imm; +} + +/* Construct short immediate register */ +static cairo_always_inline struct brw_reg brw_imm_w( int16_t w ) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_W); + imm.dw1.d = w | (w << 16); + return imm; +} + +/* brw_imm_b and brw_imm_ub aren't supported by hardware - the type + * numbers alias with _V and _VF below: + */ + +/* Construct vector of eight signed half-byte values */ +static cairo_always_inline +struct brw_reg brw_imm_v (uint32_t v) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_V); + imm.vstride = BRW_VERTICAL_STRIDE_0; + imm.width = BRW_WIDTH_8; + imm.hstride = BRW_HORIZONTAL_STRIDE_1; + imm.dw1.ud = v; + return imm; +} + +/* Construct vector of four 8-bit float values */ +static cairo_always_inline struct brw_reg +brw_imm_vf (uint32_t v) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF); + imm.vstride = BRW_VERTICAL_STRIDE_0; + imm.width = BRW_WIDTH_4; + imm.hstride = BRW_HORIZONTAL_STRIDE_1; + imm.dw1.ud = v; + return imm; +} + +#define VF_ZERO 0x0 +#define VF_ONE 0x30 +#define VF_NEG (1<<7) + +static cairo_always_inline struct brw_reg +brw_imm_vf4 (uint32_t v0, + uint32_t v1, + uint32_t v2, + uint32_t v3) +{ + struct brw_reg imm = brw_imm_reg(BRW_REGISTER_TYPE_VF); + imm.vstride = BRW_VERTICAL_STRIDE_0; + imm.width = BRW_WIDTH_4; + imm.hstride = BRW_HORIZONTAL_STRIDE_1; + imm.dw1.ud = ((v0 << 0) | + (v1 << 8) | + (v2 << 16) | + (v3 << 24)); + return imm; +} + +static cairo_always_inline struct brw_reg +brw_address (struct brw_reg reg) +{ + return brw_imm_uw (reg.nr * REG_SIZE + reg.subnr); +} + +/* Construct float[1] general-purpose register */ +static cairo_always_inline struct brw_reg +brw_vec1_grf (uint32_t nr, uint32_t subnr) +{ + return brw_vec1_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); +} + +/* Construct float[2] general-purpose register */ +static cairo_always_inline struct brw_reg +brw_vec2_grf (uint32_t nr, uint32_t subnr) +{ + return brw_vec2_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); +} + +/* Construct float[4] general-purpose register */ +static cairo_always_inline struct brw_reg +brw_vec4_grf (uint32_t nr, uint32_t subnr) +{ + return brw_vec4_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); +} + +/* Construct float[8] general-purpose register */ +static cairo_always_inline struct brw_reg +brw_vec8_grf (uint32_t nr) +{ + return brw_vec8_reg (BRW_GENERAL_REGISTER_FILE, nr, 0); +} + +static cairo_always_inline struct brw_reg +brw_uw8_grf (uint32_t nr, uint32_t subnr) +{ + return brw_uw8_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); +} + +static cairo_always_inline struct brw_reg +brw_uw16_grf (uint32_t nr, uint32_t subnr) +{ + return brw_uw16_reg (BRW_GENERAL_REGISTER_FILE, nr, subnr); +} + +/* Construct null register (usually used for setting condition codes) */ +static cairo_always_inline struct brw_reg +brw_null_reg (void) +{ + return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE, + BRW_ARF_NULL, + 0); +} + +static cairo_always_inline struct brw_reg +brw_address_reg (uint32_t subnr) +{ + return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE, + BRW_ARF_ADDRESS, + subnr); +} + +/* If/else instructions break in align16 mode if writemask & swizzle + * aren't xyzw. This goes against the convention for other scalar + * regs: + */ +static cairo_always_inline struct brw_reg +brw_ip_reg (void) +{ + return brw_reg (BRW_ARCHITECTURE_REGISTER_FILE, + BRW_ARF_IP, + 0, + BRW_REGISTER_TYPE_UD, + BRW_VERTICAL_STRIDE_4, /* ? */ + BRW_WIDTH_1, + BRW_HORIZONTAL_STRIDE_0, + BRW_SWIZZLE_XYZW, + WRITEMASK_XYZW); +} + +static cairo_always_inline struct brw_reg +brw_acc_reg (void) +{ + return brw_vec8_reg (BRW_ARCHITECTURE_REGISTER_FILE, + BRW_ARF_ACCUMULATOR, + 0); +} + +static cairo_always_inline struct brw_reg +brw_flag_reg (void) +{ + return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE, + BRW_ARF_FLAG, + 0); +} + +static cairo_always_inline struct brw_reg +brw_mask_reg (uint32_t subnr) +{ + return brw_uw1_reg (BRW_ARCHITECTURE_REGISTER_FILE, + BRW_ARF_MASK, + subnr); +} + +static cairo_always_inline struct brw_reg +brw_message4_reg (uint32_t nr) +{ + return brw_vec4_reg (BRW_MESSAGE_REGISTER_FILE, + nr, + 0); +} + +static cairo_always_inline struct brw_reg +brw_message_reg (uint32_t nr) +{ + return brw_vec8_reg (BRW_MESSAGE_REGISTER_FILE, + nr, + 0); +} + +/* This is almost always called with a numeric constant argument, so + * make things easy to evaluate at compile time: + */ +static cairo_always_inline uint32_t +cvt (uint32_t val) +{ + switch (val) { + case 0: return 0; + case 1: return 1; + case 2: return 2; + case 4: return 3; + case 8: return 4; + case 16: return 5; + case 32: return 6; + } + return 0; +} + +static cairo_always_inline struct brw_reg +stride (struct brw_reg reg, + uint32_t vstride, + uint32_t width, + uint32_t hstride) +{ + reg.vstride = cvt (vstride); + reg.width = cvt (width) - 1; + reg.hstride = cvt (hstride); + return reg; +} + +static cairo_always_inline struct brw_reg +vec16 (struct brw_reg reg) +{ + return stride (reg, 16,16,1); +} + +static cairo_always_inline struct brw_reg +vec8 (struct brw_reg reg) +{ + return stride (reg, 8,8,1); +} + +static cairo_always_inline struct brw_reg +vec4 (struct brw_reg reg) +{ + return stride (reg, 4,4,1); +} + +static cairo_always_inline struct brw_reg +vec2 (struct brw_reg reg) +{ + return stride (reg, 2,2,1); +} + +static cairo_always_inline struct brw_reg +vec1 (struct brw_reg reg) +{ + return stride (reg, 0,1,0); +} + +static cairo_always_inline struct brw_reg +get_element (struct brw_reg reg, uint32_t elt) +{ + return vec1 (suboffset (reg, elt)); +} + +static cairo_always_inline struct brw_reg +get_element_ud (struct brw_reg reg, uint32_t elt) +{ + return vec1 (suboffset (retype (reg, BRW_REGISTER_TYPE_UD), elt)); +} + +static cairo_always_inline struct brw_reg +brw_swizzle (struct brw_reg reg, + uint32_t x, + uint32_t y, + uint32_t z, + uint32_t w) +{ + reg.dw1.bits.swizzle = BRW_SWIZZLE4 (BRW_GET_SWZ (reg.dw1.bits.swizzle, x), + BRW_GET_SWZ (reg.dw1.bits.swizzle, y), + BRW_GET_SWZ (reg.dw1.bits.swizzle, z), + BRW_GET_SWZ (reg.dw1.bits.swizzle, w)); + return reg; +} + +static cairo_always_inline struct brw_reg +brw_swizzle1 (struct brw_reg reg, + uint32_t x) +{ + return brw_swizzle (reg, x, x, x, x); +} + +static cairo_always_inline struct brw_reg +brw_writemask (struct brw_reg reg, + uint32_t mask) +{ + reg.dw1.bits.writemask &= mask; + return reg; +} + +static cairo_always_inline struct brw_reg +brw_set_writemask (struct brw_reg reg, + uint32_t mask) +{ + reg.dw1.bits.writemask = mask; + return reg; +} + +static cairo_always_inline struct brw_reg +negate (struct brw_reg reg) +{ + reg.negate ^= 1; + return reg; +} + +static cairo_always_inline struct brw_reg +brw_abs (struct brw_reg reg) +{ + reg.abs = 1; + return reg; +} + +static cairo_always_inline struct brw_reg +brw_vec4_indirect (uint32_t subnr, + int32_t offset) +{ + struct brw_reg reg = brw_vec4_grf (0, 0); + reg.subnr = subnr; + reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER; + reg.dw1.bits.indirect_offset = offset; + return reg; +} + +static cairo_always_inline struct brw_reg +brw_vec1_indirect (uint32_t subnr, + int32_t offset) +{ + struct brw_reg reg = brw_vec1_grf (0, 0); + reg.subnr = subnr; + reg.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER; + reg.dw1.bits.indirect_offset = offset; + return reg; +} + +static cairo_always_inline struct brw_reg +deref_4f (struct brw_indirect ptr, int32_t offset) +{ + return brw_vec4_indirect (ptr.addr_subnr, ptr.addr_offset + offset); +} + +static cairo_always_inline struct brw_reg +deref_1f(struct brw_indirect ptr, int32_t offset) +{ + return brw_vec1_indirect (ptr.addr_subnr, ptr.addr_offset + offset); +} + +static cairo_always_inline struct brw_reg +deref_4b(struct brw_indirect ptr, int32_t offset) +{ + return retype (deref_4f (ptr, offset), BRW_REGISTER_TYPE_B); +} + +static cairo_always_inline struct brw_reg +deref_1uw(struct brw_indirect ptr, int32_t offset) +{ + return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UW); +} + +static cairo_always_inline struct brw_reg +deref_1d (struct brw_indirect ptr, int32_t offset) +{ + return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_D); +} + +static cairo_always_inline struct brw_reg +deref_1ud (struct brw_indirect ptr, int32_t offset) +{ + return retype (deref_1f (ptr, offset), BRW_REGISTER_TYPE_UD); +} + +static cairo_always_inline struct brw_reg +get_addr_reg (struct brw_indirect ptr) +{ + return brw_address_reg (ptr.addr_subnr); +} + +static cairo_always_inline struct brw_indirect +brw_indirect_offset (struct brw_indirect ptr, int32_t offset) +{ + ptr.addr_offset += offset; + return ptr; +} + +static cairo_always_inline struct brw_indirect +brw_indirect (uint32_t addr_subnr, int32_t offset) +{ + struct brw_indirect ptr; + ptr.addr_subnr = addr_subnr; + ptr.addr_offset = offset; + ptr.pad = 0; + return ptr; +} + +static cairo_always_inline struct brw_instruction * +current_insn (struct brw_compile *p) +{ + return &p->store[p->nr_insn]; +} + +cairo_private void brw_pop_insn_state (struct brw_compile *p); +cairo_private void brw_push_insn_state (struct brw_compile *p); +cairo_private void brw_set_mask_control (struct brw_compile *p, uint32_t value); +cairo_private void brw_set_saturate (struct brw_compile *p, uint32_t value); +cairo_private void brw_set_access_mode (struct brw_compile *p, uint32_t access_mode); +cairo_private void brw_set_compression_control (struct brw_compile *p, int control); +cairo_private void brw_set_predicate_control_flag_value (struct brw_compile *p, uint32_t value); +cairo_private void brw_set_predicate_control (struct brw_compile *p, uint32_t pc); +cairo_private void brw_set_conditionalmod (struct brw_compile *p, uint32_t conditional); + +cairo_private void +brw_compile_init (struct brw_compile *p, + cairo_bool_t is_g4x); +cairo_private const uint32_t *brw_get_program (struct brw_compile *p, uint32_t *sz); + +/* Helpers for regular instructions: + */ +#define ALU1(OP) \ +cairo_private_no_warn struct brw_instruction * \ +brw_##OP(struct brw_compile *p, \ + struct brw_reg dest, \ + struct brw_reg src0); + +#define ALU2(OP) \ +cairo_private_no_warn struct brw_instruction * \ +brw_##OP(struct brw_compile *p, \ + struct brw_reg dest, \ + struct brw_reg src0, \ + struct brw_reg src1); + +ALU1(MOV) +ALU2(SEL) +ALU1(NOT) +ALU2(AND) +ALU2(OR) +ALU2(XOR) +ALU2(SHR) +ALU2(SHL) +ALU2(RSR) +ALU2(RSL) +ALU2(ASR) +ALU2(JMPI) +ALU2(ADD) +ALU2(MUL) +ALU1(FRC) +ALU1(RNDD) +ALU1(RNDZ) +ALU2(MAC) +ALU2(MACH) +ALU1(LZD) +ALU2(DP4) +ALU2(DPH) +ALU2(DP3) +ALU2(DP2) +ALU2(LINE) + +#undef ALU1 +#undef ALU2 + +/* Helpers for SEND instruction: */ +cairo_private void +brw_urb_WRITE (struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + struct brw_reg src0, + int allocate, + int used, + uint32_t msg_length, + uint32_t response_length, + int eot, + int writes_complete, + uint32_t offset, + uint32_t swizzle); + +cairo_private void +brw_fb_WRITE (struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + struct brw_reg src0, + uint32_t binding_table_index, + uint32_t msg_length, + uint32_t response_length, + int eot); + +cairo_private void +brw_SAMPLE (struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + struct brw_reg src0, + uint32_t binding_table_index, + uint32_t sampler, + uint32_t writemask, + uint32_t msg_type, + uint32_t response_length, + uint32_t msg_length, + cairo_bool_t eot); + +cairo_private void +brw_math_16 (struct brw_compile *p, + struct brw_reg dest, + uint32_t function, + uint32_t saturate, + uint32_t msg_reg_nr, + struct brw_reg src, + uint32_t precision); + +cairo_private void +brw_math (struct brw_compile *p, + struct brw_reg dest, + uint32_t function, + uint32_t saturate, + uint32_t msg_reg_nr, + struct brw_reg src, + uint32_t data_type, + uint32_t precision); + +cairo_private void +brw_dp_READ_16 (struct brw_compile *p, + struct brw_reg dest, + uint32_t msg_reg_nr, + uint32_t scratch_offset); + +cairo_private void +brw_dp_WRITE_16 (struct brw_compile *p, + struct brw_reg src, + uint32_t msg_reg_nr, + uint32_t scratch_offset); + +/* If/else/endif. Works by manipulating the execution flags on each + * channel. + */ +cairo_private struct brw_instruction * +brw_IF (struct brw_compile *p, + uint32_t execute_size); + +cairo_private struct brw_instruction * +brw_ELSE (struct brw_compile *p, + struct brw_instruction *if_insn); + +cairo_private void +brw_ENDIF (struct brw_compile *p, + struct brw_instruction *if_or_else_insn); + + +/* DO/WHILE loops: */ +cairo_private struct brw_instruction * +brw_DO (struct brw_compile *p, + uint32_t execute_size); + +cairo_private struct brw_instruction * +brw_WHILE (struct brw_compile *p, + struct brw_instruction *patch_insn); + +cairo_private struct brw_instruction * +brw_BREAK (struct brw_compile *p); + +cairo_private struct brw_instruction * +brw_CONT (struct brw_compile *p); + +/* Forward jumps: */ +cairo_private void +brw_land_fwd_jump (struct brw_compile *p, + struct brw_instruction *jmp_insn); + +cairo_private void +brw_NOP (struct brw_compile *p); + +/* Special case: there is never a destination, execution size will be + * taken from src0: + */ +cairo_private void +brw_CMP (struct brw_compile *p, + struct brw_reg dest, + uint32_t conditional, + struct brw_reg src0, + struct brw_reg src1); + +cairo_private void +brw_print_reg (struct brw_reg reg); + +cairo_private struct brw_instruction * +brw_next_instruction (struct brw_compile *p, + uint32_t opcode); + +cairo_private void +brw_instruction_set_destination (struct brw_instruction *insn, + struct brw_reg dest); + +cairo_private void +brw_instruction_set_source0 (struct brw_instruction *insn, + struct brw_reg reg); + +cairo_private void +brw_instruction_set_dp_write_message (struct brw_instruction *insn, + uint32_t binding_table_index, + uint32_t msg_control, + uint32_t msg_type, + uint32_t msg_length, + uint32_t pixel_scoreboard_clear, + uint32_t response_length, + uint32_t end_of_thread); + +/*********************************************************************** + * brw_eu_util.c: + */ + +cairo_private void +brw_copy_indirect_to_indirect (struct brw_compile *p, + struct brw_indirect dst_ptr, + struct brw_indirect src_ptr, + uint32_t count); + +cairo_private void +brw_copy_from_indirect (struct brw_compile *p, + struct brw_reg dst, + struct brw_indirect ptr, + uint32_t count); + +cairo_private void +brw_copy4 (struct brw_compile *p, + struct brw_reg dst, + struct brw_reg src, + uint32_t count); + +cairo_private void +brw_copy8 (struct brw_compile *p, + struct brw_reg dst, + struct brw_reg src, + uint32_t count); + +cairo_private void +brw_math_invert (struct brw_compile *p, + struct brw_reg dst, + struct brw_reg src); + +cairo_private void +brw_set_src1 (struct brw_instruction *insn, + struct brw_reg reg); + +#endif diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-structs.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-structs.h new file mode 100644 index 000000000000..3ea9c6c362cd --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-structs.h @@ -0,0 +1,1329 @@ +/************************************************************************** + * + * Copyright 2005 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef CAIRO_DRM_INTEL_BRW_STRUCTS_H +#define CAIRO_DRM_INTEL_BRW_STRUCTS_H + +#include "cairo.h" +#include "cairo-types-private.h" + +/* Command packets: +*/ +struct header { + unsigned int length:16; + unsigned int opcode:16; +}; + +union header_union { + struct header bits; + unsigned int dword; +}; + +struct brw_3d_control { + struct { + unsigned int length:8; + unsigned int notify_enable:1; + unsigned int pad:3; + unsigned int wc_flush_enable:1; + unsigned int depth_stall_enable:1; + unsigned int operation:2; + unsigned int opcode:16; + } header; + + struct { + unsigned int pad:2; + unsigned int dest_addr_type:1; + unsigned int dest_addr:29; + } dest; + + unsigned int dword2; + unsigned int dword3; +}; + + +struct brw_3d_primitive { + struct { + unsigned int length:8; + unsigned int pad:2; + unsigned int topology:5; + unsigned int indexed:1; + unsigned int opcode:16; + } header; + + unsigned int verts_per_instance; + unsigned int start_vert_location; + unsigned int instance_count; + unsigned int start_instance_location; + unsigned int base_vert_location; +}; + +/* These seem to be passed around as function args, so it works out + * better to keep them as #defines: + */ +#define BRW_FLUSH_READ_CACHE 0x1 +#define BRW_FLUSH_STATE_CACHE 0x2 +#define BRW_INHIBIT_FLUSH_RENDER_CACHE 0x4 +#define BRW_FLUSH_SNAPSHOT_COUNTERS 0x8 + +struct brw_mi_flush { + unsigned int flags:4; + unsigned int pad:12; + unsigned int opcode:16; +}; + +struct brw_vf_statistics { + unsigned int statistics_enable:1; + unsigned int pad:15; + unsigned int opcode:16; +}; + + +struct brw_binding_table_pointers { + struct header header; + unsigned int vs; + unsigned int gs; + unsigned int clp; + unsigned int sf; + unsigned int wm; +}; + +struct brw_blend_constant_color { + struct header header; + float blend_constant_color[4]; +}; + +struct brw_depthbuffer { + union header_union header; + + union { + struct { + unsigned int pitch:18; + unsigned int format:3; + unsigned int pad:4; + unsigned int depth_offset_disable:1; + unsigned int tile_walk:1; + unsigned int tiled_surface:1; + unsigned int pad2:1; + unsigned int surface_type:3; + } bits; + unsigned int dword; + } dword1; + + unsigned int dword2_base_addr; + + union { + struct { + unsigned int pad:1; + unsigned int mipmap_layout:1; + unsigned int lod:4; + unsigned int width:13; + unsigned int height:13; + } bits; + unsigned int dword; + } dword3; + + union { + struct { + unsigned int pad:12; + unsigned int min_array_element:9; + unsigned int depth:11; + } bits; + unsigned int dword; + } dword4; +}; + +struct brw_drawrect { + struct header header; + unsigned int xmin:16; + unsigned int ymin:16; + unsigned int xmax:16; + unsigned int ymax:16; + unsigned int xorg:16; + unsigned int yorg:16; +}; + +struct brw_global_depth_offset_clamp { + struct header header; + float depth_offset_clamp; +}; + +struct brw_indexbuffer { + union { + struct { + unsigned int length:8; + unsigned int index_format:2; + unsigned int cut_index_enable:1; + unsigned int pad:5; + unsigned int opcode:16; + } bits; + unsigned int dword; + } header; + unsigned int buffer_start; + unsigned int buffer_end; +}; + + +struct brw_line_stipple { + struct header header; + + struct { + unsigned int pattern:16; + unsigned int pad:16; + } bits0; + + struct { + unsigned int repeat_count:9; + unsigned int pad:7; + unsigned int inverse_repeat_count:16; + } bits1; +}; + +struct brw_pipelined_state_pointers { + struct header header; + + struct { + unsigned int pad:5; + unsigned int offset:27; + } vs; + + struct { + unsigned int enable:1; + unsigned int pad:4; + unsigned int offset:27; + } gs; + + struct { + unsigned int enable:1; + unsigned int pad:4; + unsigned int offset:27; + } clp; + + struct { + unsigned int pad:5; + unsigned int offset:27; + } sf; + + struct { + unsigned int pad:5; + unsigned int offset:27; + } wm; + + struct { + unsigned int pad:6; + unsigned int offset:26; + } cc; +}; + +struct brw_polygon_stipple_offset { + struct header header; + + struct { + unsigned int y_offset:5; + unsigned int pad:3; + unsigned int x_offset:5; + unsigned int pad0:19; + } bits0; +}; + +struct brw_polygon_stipple { + struct header header; + unsigned int stipple[32]; +}; + +struct brw_pipeline_select { + struct { + unsigned int pipeline_select:1; + unsigned int pad:15; + unsigned int opcode:16; + } header; +}; + +struct brw_pipe_control { + struct { + unsigned int length:8; + unsigned int notify_enable:1; + unsigned int pad:2; + unsigned int instruction_state_cache_flush_enable:1; + unsigned int write_cache_flush_enable:1; + unsigned int depth_stall_enable:1; + unsigned int post_sync_operation:2; + + unsigned int opcode:16; + } header; + + struct { + unsigned int pad:2; + unsigned int dest_addr_type:1; + unsigned int dest_addr:29; + } bits1; + + unsigned int data0; + unsigned int data1; +}; + + +struct brw_urb_fence { + struct { + unsigned int length:8; + unsigned int vs_realloc:1; + unsigned int gs_realloc:1; + unsigned int clp_realloc:1; + unsigned int sf_realloc:1; + unsigned int vfe_realloc:1; + unsigned int cs_realloc:1; + unsigned int pad:2; + unsigned int opcode:16; + } header; + + struct { + unsigned int vs_fence:10; + unsigned int gs_fence:10; + unsigned int clp_fence:10; + unsigned int pad:2; + } bits0; + + struct { + unsigned int sf_fence:10; + unsigned int vf_fence:10; + unsigned int cs_fence:10; + unsigned int pad:2; + } bits1; +}; + +struct brw_constant_buffer_state { + struct header header; + + struct { + unsigned int nr_urb_entries:3; + unsigned int pad:1; + unsigned int urb_entry_size:5; + unsigned int pad0:23; + } bits0; +}; + +struct brw_constant_buffer { + struct { + unsigned int length:8; + unsigned int valid:1; + unsigned int pad:7; + unsigned int opcode:16; + } header; + + struct { + unsigned int buffer_length:6; + unsigned int buffer_address:26; + } bits0; +}; + +struct brw_state_base_address { + struct header header; + + struct { + unsigned int modify_enable:1; + unsigned int pad:4; + unsigned int general_state_address:27; + } bits0; + + struct { + unsigned int modify_enable:1; + unsigned int pad:4; + unsigned int surface_state_address:27; + } bits1; + + struct { + unsigned int modify_enable:1; + unsigned int pad:4; + unsigned int indirect_object_state_address:27; + } bits2; + + struct { + unsigned int modify_enable:1; + unsigned int pad:11; + unsigned int general_state_upper_bound:20; + } bits3; + + struct { + unsigned int modify_enable:1; + unsigned int pad:11; + unsigned int indirect_object_state_upper_bound:20; + } bits4; +}; + +struct brw_state_prefetch { + struct header header; + + struct { + unsigned int prefetch_count:3; + unsigned int pad:3; + unsigned int prefetch_pointer:26; + } bits0; +}; + +struct brw_system_instruction_pointer { + struct header header; + + struct { + unsigned int pad:4; + unsigned int system_instruction_pointer:28; + } bits0; +}; + + +/* State structs for the various fixed function units: +*/ + +struct thread0 { + unsigned int pad0:1; + unsigned int grf_reg_count:3; + unsigned int pad1:2; + unsigned int kernel_start_pointer:26; +}; + +struct thread1 { + unsigned int ext_halt_exception_enable:1; + unsigned int sw_exception_enable:1; + unsigned int mask_stack_exception_enable:1; + unsigned int timeout_exception_enable:1; + unsigned int illegal_op_exception_enable:1; + unsigned int pad0:3; + unsigned int depth_coef_urb_read_offset:6; /* WM only */ + unsigned int pad1:2; + unsigned int floating_point_mode:1; + unsigned int thread_priority:1; + unsigned int binding_table_entry_count:8; + unsigned int pad3:5; + unsigned int single_program_flow:1; +}; + +struct thread2 { + unsigned int per_thread_scratch_space:4; + unsigned int pad0:6; + unsigned int scratch_space_base_pointer:22; +}; + +struct thread3 { + unsigned int dispatch_grf_start_reg:4; + unsigned int urb_entry_read_offset:6; + unsigned int pad0:1; + unsigned int urb_entry_read_length:6; + unsigned int pad1:1; + unsigned int const_urb_entry_read_offset:6; + unsigned int pad2:1; + unsigned int const_urb_entry_read_length:6; + unsigned int pad3:1; +}; + +struct brw_clip_unit_state { + struct thread0 thread0; + struct thread1 thread1; + struct thread2 thread2; + struct thread3 thread3; + + struct { + unsigned int pad0:9; + unsigned int gs_output_stats:1; /* not always */ + unsigned int stats_enable:1; + unsigned int nr_urb_entries:7; + unsigned int pad1:1; + unsigned int urb_entry_allocation_size:5; + unsigned int pad2:1; + unsigned int max_threads:6; /* may be less */ + unsigned int pad3:1; + } thread4; + + struct { + unsigned int pad0:13; + unsigned int clip_mode:3; + unsigned int userclip_enable_flags:8; + unsigned int userclip_must_clip:1; + unsigned int pad1:1; + unsigned int guard_band_enable:1; + unsigned int viewport_z_clip_enable:1; + unsigned int viewport_xy_clip_enable:1; + unsigned int vertex_position_space:1; + unsigned int api_mode:1; + unsigned int pad2:1; + } clip5; + + struct { + unsigned int pad0:5; + unsigned int clipper_viewport_state_ptr:27; + } clip6; + + float viewport_xmin; + float viewport_xmax; + float viewport_ymin; + float viewport_ymax; +}; + +struct brw_cc_unit_state { + struct { + unsigned int pad0:3; + unsigned int bf_stencil_pass_depth_pass_op:3; + unsigned int bf_stencil_pass_depth_fail_op:3; + unsigned int bf_stencil_fail_op:3; + unsigned int bf_stencil_func:3; + unsigned int bf_stencil_enable:1; + unsigned int pad1:2; + unsigned int stencil_write_enable:1; + unsigned int stencil_pass_depth_pass_op:3; + unsigned int stencil_pass_depth_fail_op:3; + unsigned int stencil_fail_op:3; + unsigned int stencil_func:3; + unsigned int stencil_enable:1; + } cc0; + + struct { + unsigned int bf_stencil_ref:8; + unsigned int stencil_write_mask:8; + unsigned int stencil_test_mask:8; + unsigned int stencil_ref:8; + } cc1; + + struct { + unsigned int logicop_enable:1; + unsigned int pad0:10; + unsigned int depth_write_enable:1; + unsigned int depth_test_function:3; + unsigned int depth_test:1; + unsigned int bf_stencil_write_mask:8; + unsigned int bf_stencil_test_mask:8; + } cc2; + + struct { + unsigned int pad0:8; + unsigned int alpha_test_func:3; + unsigned int alpha_test:1; + unsigned int blend_enable:1; + unsigned int ia_blend_enable:1; + unsigned int pad1:1; + unsigned int alpha_test_format:1; + unsigned int pad2:16; + } cc3; + + struct { + unsigned int pad0:5; + unsigned int cc_viewport_state_offset:27; + } cc4; + + struct { + unsigned int pad0:2; + unsigned int ia_dest_blend_factor:5; + unsigned int ia_src_blend_factor:5; + unsigned int ia_blend_function:3; + unsigned int statistics_enable:1; + unsigned int logicop_func:4; + unsigned int pad1:11; + unsigned int dither_enable:1; + } cc5; + + struct { + unsigned int clamp_post_alpha_blend:1; + unsigned int clamp_pre_alpha_blend:1; + unsigned int clamp_range:2; + unsigned int pad0:11; + unsigned int y_dither_offset:2; + unsigned int x_dither_offset:2; + unsigned int dest_blend_factor:5; + unsigned int src_blend_factor:5; + unsigned int blend_function:3; + } cc6; + + struct { + union { + float f; + unsigned char ub[4]; + } alpha_ref; + } cc7; +}; + +struct brw_sf_unit_state { + struct thread0 thread0; + struct { + unsigned int pad0:7; + unsigned int sw_exception_enable:1; + unsigned int pad1:3; + unsigned int mask_stack_exception_enable:1; + unsigned int pad2:1; + unsigned int illegal_op_exception_enable:1; + unsigned int pad3:2; + unsigned int floating_point_mode:1; + unsigned int thread_priority:1; + unsigned int binding_table_entry_count:8; + unsigned int pad4:5; + unsigned int single_program_flow:1; + } sf1; + + struct thread2 thread2; + struct thread3 thread3; + + struct { + unsigned int pad0:10; + unsigned int stats_enable:1; + unsigned int nr_urb_entries:7; + unsigned int pad1:1; + unsigned int urb_entry_allocation_size:5; + unsigned int pad2:1; + unsigned int max_threads:6; + unsigned int pad3:1; + } thread4; + + struct { + unsigned int front_winding:1; + unsigned int viewport_transform:1; + unsigned int pad0:3; + unsigned int sf_viewport_state_offset:27; + } sf5; + + struct { + unsigned int pad0:9; + unsigned int dest_org_vbias:4; + unsigned int dest_org_hbias:4; + unsigned int scissor:1; + unsigned int disable_2x2_trifilter:1; + unsigned int disable_zero_pix_trifilter:1; + unsigned int point_rast_rule:2; + unsigned int line_endcap_aa_region_width:2; + unsigned int line_width:4; + unsigned int fast_scissor_disable:1; + unsigned int cull_mode:2; + unsigned int aa_enable:1; + } sf6; + + struct { + unsigned int point_size:11; + unsigned int use_point_size_state:1; + unsigned int subpixel_precision:1; + unsigned int sprite_point:1; + unsigned int pad0:11; + unsigned int trifan_pv:2; + unsigned int linestrip_pv:2; + unsigned int tristrip_pv:2; + unsigned int line_last_pixel_enable:1; + } sf7; +}; + +struct brw_gs_unit_state { + struct thread0 thread0; + struct thread1 thread1; + struct thread2 thread2; + struct thread3 thread3; + + struct { + unsigned int pad0:10; + unsigned int stats_enable:1; + unsigned int nr_urb_entries:7; + unsigned int pad1:1; + unsigned int urb_entry_allocation_size:5; + unsigned int pad2:1; + unsigned int max_threads:1; + unsigned int pad3:6; + } thread4; + + struct { + unsigned int sampler_count:3; + unsigned int pad0:2; + unsigned int sampler_state_pointer:27; + } gs5; + + struct { + unsigned int max_vp_index:4; + unsigned int pad0:26; + unsigned int reorder_enable:1; + unsigned int pad1:1; + } gs6; +}; + +struct brw_vs_unit_state { + struct thread0 thread0; + struct thread1 thread1; + struct thread2 thread2; + struct thread3 thread3; + + struct { + unsigned int pad0:10; + unsigned int stats_enable:1; + unsigned int nr_urb_entries:7; + unsigned int pad1:1; + unsigned int urb_entry_allocation_size:5; + unsigned int pad2:1; + unsigned int max_threads:4; + unsigned int pad3:3; + } thread4; + + struct { + unsigned int sampler_count:3; + unsigned int pad0:2; + unsigned int sampler_state_pointer:27; + } vs5; + + struct { + unsigned int vs_enable:1; + unsigned int vert_cache_disable:1; + unsigned int pad0:30; + } vs6; +}; + +struct brw_wm_unit_state { + struct thread0 thread0; + struct thread1 thread1; + struct thread2 thread2; + struct thread3 thread3; + + struct { + unsigned int stats_enable:1; + unsigned int pad0:1; + unsigned int sampler_count:3; + unsigned int sampler_state_pointer:27; + } wm4; + + struct { + unsigned int enable_8_pix:1; + unsigned int enable_16_pix:1; + unsigned int enable_32_pix:1; + unsigned int pad0:7; + unsigned int legacy_global_depth_bias:1; + unsigned int line_stipple:1; + unsigned int depth_offset:1; + unsigned int polygon_stipple:1; + unsigned int line_aa_region_width:2; + unsigned int line_endcap_aa_region_width:2; + unsigned int early_depth_test:1; + unsigned int thread_dispatch_enable:1; + unsigned int program_uses_depth:1; + unsigned int program_computes_depth:1; + unsigned int program_uses_killpixel:1; + unsigned int legacy_line_rast: 1; + unsigned int transposed_urb_read:1; + unsigned int max_threads:7; + } wm5; + + float global_depth_offset_constant; + float global_depth_offset_scale; +}; + +/* The hardware supports two different modes for border color. The + * default (OpenGL) mode uses floating-point color channels, while the + * legacy mode uses 4 bytes. + * + * More significantly, the legacy mode respects the components of the + * border color for channels not present in the source, (whereas the + * default mode will ignore the border color's alpha channel and use + * alpha==1 for an RGB source, for example). + * + * The legacy mode matches the semantics specified by the Render + * extension. + */ +struct brw_sampler_default_border_color { + float color[4]; +}; + +struct brw_sampler_legacy_border_color { + uint8_t color[4]; +}; + +struct brw_sampler_state { + struct { + unsigned int shadow_function:3; + unsigned int lod_bias:11; + unsigned int min_filter:3; + unsigned int mag_filter:3; + unsigned int mip_filter:2; + unsigned int base_level:5; + unsigned int pad:1; + unsigned int lod_preclamp:1; + unsigned int border_color_mode:1; + unsigned int pad0:1; + unsigned int disable:1; + } ss0; + + struct { + unsigned int r_wrap_mode:3; + unsigned int t_wrap_mode:3; + unsigned int s_wrap_mode:3; + unsigned int pad:3; + unsigned int max_lod:10; + unsigned int min_lod:10; + } ss1; + + struct { + unsigned int pad:5; + unsigned int border_color_pointer:27; + } ss2; + + struct { + unsigned int pad:19; + unsigned int max_aniso:3; + unsigned int chroma_key_mode:1; + unsigned int chroma_key_index:2; + unsigned int chroma_key_enable:1; + unsigned int monochrome_filter_width:3; + unsigned int monochrome_filter_height:3; + } ss3; +}; + +struct brw_clipper_viewport { + float xmin; + float xmax; + float ymin; + float ymax; +}; + +struct brw_cc_viewport { + float min_depth; + float max_depth; +}; + +struct brw_sf_viewport { + struct { + float m00; + float m11; + float m22; + float m30; + float m31; + float m32; + } viewport; + + struct { + short xmin; + short ymin; + short xmax; + short ymax; + } scissor; +}; + +/* Documented in the subsystem/shared-functions/sampler chapter... +*/ +struct brw_surface_state { + struct { + unsigned int cube_pos_z:1; + unsigned int cube_neg_z:1; + unsigned int cube_pos_y:1; + unsigned int cube_neg_y:1; + unsigned int cube_pos_x:1; + unsigned int cube_neg_x:1; + unsigned int pad:3; + unsigned int render_cache_read_mode:1; + unsigned int mipmap_layout_mode:1; + unsigned int vert_line_stride_ofs:1; + unsigned int vert_line_stride:1; + unsigned int color_blend:1; + unsigned int writedisable_blue:1; + unsigned int writedisable_green:1; + unsigned int writedisable_red:1; + unsigned int writedisable_alpha:1; + unsigned int surface_format:9; + unsigned int data_return_format:1; + unsigned int pad0:1; + unsigned int surface_type:3; + } ss0; + + struct { + unsigned int base_addr; + } ss1; + + struct { + unsigned int render_target_rotation:2; + unsigned int mip_count:4; + unsigned int width:13; + unsigned int height:13; + } ss2; + + struct { + unsigned int tile_walk:1; + unsigned int tiled_surface:1; + unsigned int pad:1; + unsigned int pitch:18; + unsigned int depth:11; + } ss3; + + struct { + unsigned int pad:19; + unsigned int min_array_elt:9; + unsigned int min_lod:4; + } ss4; + + struct { + unsigned int pad:20; + unsigned int y_offset:4; + unsigned int pad2:1; + unsigned int x_offset:7; + } ss5; +}; + +struct brw_vertex_buffer_state { + struct { + unsigned int pitch:11; + unsigned int pad:15; + unsigned int access_type:1; + unsigned int vb_index:5; + } vb0; + + unsigned int start_addr; + unsigned int max_index; +#if 1 + unsigned int instance_data_step_rate; /* not included for sequential/random vertices? */ +#endif +}; + +#define BRW_VBP_MAX 17 + +struct brw_vb_array_state { + struct header header; + struct brw_vertex_buffer_state vb[BRW_VBP_MAX]; +}; + +struct brw_vertex_element_state { + struct { + unsigned int src_offset:11; + unsigned int pad:5; + unsigned int src_format:9; + unsigned int pad0:1; + unsigned int valid:1; + unsigned int vertex_buffer_index:5; + } ve0; + + struct { + unsigned int dst_offset:8; + unsigned int pad:8; + unsigned int vfcomponent3:4; + unsigned int vfcomponent2:4; + unsigned int vfcomponent1:4; + unsigned int vfcomponent0:4; + } ve1; +}; + +#define BRW_VEP_MAX 18 + +struct brw_vertex_element_packet { + struct header header; + struct brw_vertex_element_state ve[BRW_VEP_MAX]; +}; + +struct brw_urb_immediate { + unsigned int opcode:4; + unsigned int offset:6; + unsigned int swizzle_control:2; + unsigned int pad:1; + unsigned int allocate:1; + unsigned int used:1; + unsigned int complete:1; + unsigned int response_length:4; + unsigned int msg_length:4; + unsigned int msg_target:4; + unsigned int pad1:3; + unsigned int end_of_thread:1; +}; + +/* Instruction format for the execution units: */ + +struct brw_instruction { + struct { + unsigned int opcode:7; + unsigned int pad:1; + unsigned int access_mode:1; + unsigned int mask_control:1; + unsigned int dependency_control:2; + unsigned int compression_control:2; + unsigned int thread_control:2; + unsigned int predicate_control:4; + unsigned int predicate_inverse:1; + unsigned int execution_size:3; + unsigned int destreg__conditonalmod:4; /* destreg - send, conditionalmod - others */ + unsigned int pad0:2; + unsigned int debug_control:1; + unsigned int saturate:1; + } header; + + union { + struct { + unsigned int dest_reg_file:2; + unsigned int dest_reg_type:3; + unsigned int src0_reg_file:2; + unsigned int src0_reg_type:3; + unsigned int src1_reg_file:2; + unsigned int src1_reg_type:3; + unsigned int pad:1; + unsigned int dest_subreg_nr:5; + unsigned int dest_reg_nr:8; + unsigned int dest_horiz_stride:2; + unsigned int dest_address_mode:1; + } da1; + + struct { + unsigned int dest_reg_file:2; + unsigned int dest_reg_type:3; + unsigned int src0_reg_file:2; + unsigned int src0_reg_type:3; + unsigned int pad:6; + int dest_indirect_offset:10; /* offset against the deref'd address reg */ + unsigned int dest_subreg_nr:3; /* subnr for the address reg a0.x */ + unsigned int dest_horiz_stride:2; + unsigned int dest_address_mode:1; + } ia1; + + struct { + unsigned int dest_reg_file:2; + unsigned int dest_reg_type:3; + unsigned int src0_reg_file:2; + unsigned int src0_reg_type:3; + unsigned int src1_reg_file:2; + unsigned int src1_reg_type:3; + unsigned int pad0:1; + unsigned int dest_writemask:4; + unsigned int dest_subreg_nr:1; + unsigned int dest_reg_nr:8; + unsigned int pad1:2; + unsigned int dest_address_mode:1; + } da16; + + struct { + unsigned int dest_reg_file:2; + unsigned int dest_reg_type:3; + unsigned int src0_reg_file:2; + unsigned int src0_reg_type:3; + unsigned int pad0:6; + unsigned int dest_writemask:4; + int dest_indirect_offset:6; + unsigned int dest_subreg_nr:3; + unsigned int pad1:2; + unsigned int dest_address_mode:1; + } ia16; + } bits1; + + + union { + struct { + unsigned int src0_subreg_nr:5; + unsigned int src0_reg_nr:8; + unsigned int src0_abs:1; + unsigned int src0_negate:1; + unsigned int src0_address_mode:1; + unsigned int src0_horiz_stride:2; + unsigned int src0_width:3; + unsigned int src0_vert_stride:4; + unsigned int flag_reg_nr:1; + unsigned int pad:6; + } da1; + + struct { + int src0_indirect_offset:10; + unsigned int src0_subreg_nr:3; + unsigned int src0_abs:1; + unsigned int src0_negate:1; + unsigned int src0_address_mode:1; + unsigned int src0_horiz_stride:2; + unsigned int src0_width:3; + unsigned int src0_vert_stride:4; + unsigned int flag_reg_nr:1; + unsigned int pad:6; + } ia1; + + struct { + unsigned int src0_swz_x:2; + unsigned int src0_swz_y:2; + unsigned int src0_subreg_nr:1; + unsigned int src0_reg_nr:8; + unsigned int src0_abs:1; + unsigned int src0_negate:1; + unsigned int src0_address_mode:1; + unsigned int src0_swz_z:2; + unsigned int src0_swz_w:2; + unsigned int pad0:1; + unsigned int src0_vert_stride:4; + unsigned int flag_reg_nr:1; + unsigned int pad1:6; + } da16; + + struct { + unsigned int src0_swz_x:2; + unsigned int src0_swz_y:2; + int src0_indirect_offset:6; + unsigned int src0_subreg_nr:3; + unsigned int src0_abs:1; + unsigned int src0_negate:1; + unsigned int src0_address_mode:1; + unsigned int src0_swz_z:2; + unsigned int src0_swz_w:2; + unsigned int pad0:1; + unsigned int src0_vert_stride:4; + unsigned int flag_reg_nr:1; + unsigned int pad1:6; + } ia16; + + } bits2; + + union { + struct { + unsigned int src1_subreg_nr:5; + unsigned int src1_reg_nr:8; + unsigned int src1_abs:1; + unsigned int src1_negate:1; + unsigned int pad:1; + unsigned int src1_horiz_stride:2; + unsigned int src1_width:3; + unsigned int src1_vert_stride:4; + unsigned int pad0:7; + } da1; + + struct { + unsigned int src1_swz_x:2; + unsigned int src1_swz_y:2; + unsigned int src1_subreg_nr:1; + unsigned int src1_reg_nr:8; + unsigned int src1_abs:1; + unsigned int src1_negate:1; + unsigned int pad0:1; + unsigned int src1_swz_z:2; + unsigned int src1_swz_w:2; + unsigned int pad1:1; + unsigned int src1_vert_stride:4; + unsigned int pad2:7; + } da16; + + struct { + int src1_indirect_offset:10; + unsigned int src1_subreg_nr:3; + unsigned int src1_abs:1; + unsigned int src1_negate:1; + unsigned int pad0:1; + unsigned int src1_horiz_stride:2; + unsigned int src1_width:3; + unsigned int src1_vert_stride:4; + unsigned int flag_reg_nr:1; + unsigned int pad1:6; + } ia1; + + struct { + unsigned int src1_swz_x:2; + unsigned int src1_swz_y:2; + int src1_indirect_offset:6; + unsigned int src1_subreg_nr:3; + unsigned int src1_abs:1; + unsigned int src1_negate:1; + unsigned int pad0:1; + unsigned int src1_swz_z:2; + unsigned int src1_swz_w:2; + unsigned int pad1:1; + unsigned int src1_vert_stride:4; + unsigned int flag_reg_nr:1; + unsigned int pad2:6; + } ia16; + + struct { + int jump_count:16; /* note: signed */ + unsigned int pop_count:4; + unsigned int pad0:12; + } if_else; + + struct { + unsigned int function:4; + unsigned int int_type:1; + unsigned int precision:1; + unsigned int saturate:1; + unsigned int data_type:1; + unsigned int pad0:8; + unsigned int response_length:4; + unsigned int msg_length:4; + unsigned int msg_target:4; + unsigned int pad1:3; + unsigned int end_of_thread:1; + } math; + + struct { + unsigned int binding_table_index:8; + unsigned int sampler:4; + unsigned int return_format:2; + unsigned int msg_type:2; + unsigned int response_length:4; + unsigned int msg_length:4; + unsigned int msg_target:4; + unsigned int pad1:3; + unsigned int end_of_thread:1; + } sampler; + + struct { + uint32_t binding_table_index:8; + uint32_t sampler:4; + uint32_t msg_type:4; + uint32_t response_length:4; + uint32_t msg_length:4; + uint32_t msg_target:4; + uint32_t pad1:3; + uint32_t end_of_thread:1; + } sampler_g4x; + + struct brw_urb_immediate urb; + + struct { + unsigned int binding_table_index:8; + unsigned int msg_control:4; + unsigned int msg_type:2; + unsigned int target_cache:2; + unsigned int response_length:4; + unsigned int msg_length:4; + unsigned int msg_target:4; + unsigned int pad1:3; + unsigned int end_of_thread:1; + } dp_read; + + struct { + unsigned int binding_table_index:8; + unsigned int msg_control:3; + unsigned int pixel_scoreboard_clear:1; + unsigned int msg_type:3; + unsigned int send_commit_msg:1; + unsigned int response_length:4; + unsigned int msg_length:4; + unsigned int msg_target:4; + unsigned int pad1:3; + unsigned int end_of_thread:1; + } dp_write; + + struct { + unsigned int pad:16; + unsigned int response_length:4; + unsigned int msg_length:4; + unsigned int msg_target:4; + unsigned int pad1:3; + unsigned int end_of_thread:1; + } generic; + + uint32_t ud; + int32_t d; + } bits3; +}; + +/* media pipeline */ + +struct brw_vfe_state { + struct { + unsigned int per_thread_scratch_space:4; + unsigned int pad3:3; + unsigned int extend_vfe_state_present:1; + unsigned int pad2:2; + unsigned int scratch_base:22; + } vfe0; + + struct { + unsigned int debug_counter_control:2; + unsigned int children_present:1; + unsigned int vfe_mode:4; + unsigned int pad2:2; + unsigned int num_urb_entries:7; + unsigned int urb_entry_alloc_size:9; + unsigned int max_threads:7; + } vfe1; + + struct { + unsigned int pad4:4; + unsigned int interface_descriptor_base:28; + } vfe2; +}; + +struct brw_vld_state { + struct { + unsigned int pad6:6; + unsigned int scan_order:1; + unsigned int intra_vlc_format:1; + unsigned int quantizer_scale_type:1; + unsigned int concealment_motion_vector:1; + unsigned int frame_predict_frame_dct:1; + unsigned int top_field_first:1; + unsigned int picture_structure:2; + unsigned int intra_dc_precision:2; + unsigned int f_code_0_0:4; + unsigned int f_code_0_1:4; + unsigned int f_code_1_0:4; + unsigned int f_code_1_1:4; + } vld0; + + struct { + unsigned int pad2:9; + unsigned int picture_coding_type:2; + unsigned int pad:21; + } vld1; + + struct { + unsigned int index_0:4; + unsigned int index_1:4; + unsigned int index_2:4; + unsigned int index_3:4; + unsigned int index_4:4; + unsigned int index_5:4; + unsigned int index_6:4; + unsigned int index_7:4; + } desc_remap_table0; + + struct { + unsigned int index_8:4; + unsigned int index_9:4; + unsigned int index_10:4; + unsigned int index_11:4; + unsigned int index_12:4; + unsigned int index_13:4; + unsigned int index_14:4; + unsigned int index_15:4; + } desc_remap_table1; +}; + +struct brw_interface_descriptor { + struct { + unsigned int grf_reg_blocks:4; + unsigned int pad:2; + unsigned int kernel_start_pointer:26; + } desc0; + + struct { + unsigned int pad:7; + unsigned int software_exception:1; + unsigned int pad2:3; + unsigned int maskstack_exception:1; + unsigned int pad3:1; + unsigned int illegal_opcode_exception:1; + unsigned int pad4:2; + unsigned int floating_point_mode:1; + unsigned int thread_priority:1; + unsigned int single_program_flow:1; + unsigned int pad5:1; + unsigned int const_urb_entry_read_offset:6; + unsigned int const_urb_entry_read_len:6; + } desc1; + + struct { + unsigned int pad:2; + unsigned int sampler_count:3; + unsigned int sampler_state_pointer:27; + } desc2; + + struct { + unsigned int binding_table_entry_count:5; + unsigned int binding_table_pointer:27; + } desc3; +}; + +#endif diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-command-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-command-private.h new file mode 100644 index 000000000000..a93ac12ab574 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-command-private.h @@ -0,0 +1,909 @@ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef CAIRO_DRM_INTEL_COMMAND_PRIVATE_H +#define CAIRO_DRM_INTEL_COMMAND_PRIVATE_H + +#include "cairo-types-private.h" + +#define CMD_MI (0x0 << 29) +#define CMD_misc (0x1 << 29) +#define CMD_2D (0x2 << 29) +#define CMD_3D (0x3 << 29) +/* 4-7 reserved */ + +#define MI_NOOP (CMD_MI | 0) +/* Batch */ +#define MI_BATCH_BUFFER (CMD_MI | (0x30 << 23) | 1) +#define MI_BATCH_BUFFER_START (CMD_MI | (0x31 << 23)) +#define MI_BATCH_BUFFER_END (CMD_MI | (0x0a << 23)) +#define MI_BATCH_NON_SECURE (1) +#define MI_BATCH_NON_SECURE_I965 (1 << 8) +/* Flush */ +#define MI_FLUSH (CMD_MI | (0x04 << 23)) +#define MI_WRITE_DIRTY_STATE (1<<4) +#define MI_END_SCENE (1<<3) +#define MI_GLOBAL_SNAPSHOT_COUNT_RESET (1<<3) +#define MI_INHIBIT_RENDER_CACHE_FLUSH (1<<2) +#define MI_STATE_INSTRUCTION_CACHE_FLUSH (1<<1) +#define MI_INVALIDATE_MAP_CACHE (1<<0) + +#define PRIM3D (CMD_3D | (0x1f<<24)) +#define PRIM3D_TRILIST (PRIM3D | (0x0<<18)) +#define PRIM3D_TRISTRIP (PRIM3D | (0x1<<18)) +#define PRIM3D_TRISTRIP_RVRSE (PRIM3D | (0x2<<18)) +#define PRIM3D_TRIFAN (PRIM3D | (0x3<<18)) +#define PRIM3D_POLY (PRIM3D | (0x4<<18)) +#define PRIM3D_LINELIST (PRIM3D | (0x5<<18)) +#define PRIM3D_LINESTRIP (PRIM3D | (0x6<<18)) +#define PRIM3D_RECTLIST (PRIM3D | (0x7<<18)) +#define PRIM3D_POINTLIST (PRIM3D | (0x8<<18)) +#define PRIM3D_DIB (PRIM3D | (0x9<<18)) +#define PRIM3D_CLEAR_RECT (PRIM3D | (0xa<<18)) +#define PRIM3D_ZONE_INIT (PRIM3D | (0xd<<18)) +#define PRIM3D_MASK (0x1f<<18) +#define PRIM3D_INDIRECT_SEQUENTIAL ((1<<23) | (0<<17)) +#define PRIM3D_INDIRECT_ELTS ((1<<23) | (1<<17)) + +/* p137 */ +#define _3DSTATE_AA_CMD (CMD_3D | (0x06<<24)) +#define AA_LINE_ECAAR_WIDTH_ENABLE (1<<16) +#define AA_LINE_ECAAR_WIDTH_0_5 0 +#define AA_LINE_ECAAR_WIDTH_1_0 (1<<14) +#define AA_LINE_ECAAR_WIDTH_2_0 (2<<14) +#define AA_LINE_ECAAR_WIDTH_4_0 (3<<14) +#define AA_LINE_REGION_WIDTH_ENABLE (1<<8) +#define AA_LINE_REGION_WIDTH_0_5 0 +#define AA_LINE_REGION_WIDTH_1_0 (1<<6) +#define AA_LINE_REGION_WIDTH_2_0 (2<<6) +#define AA_LINE_REGION_WIDTH_4_0 (3<<6) + +/* 3DSTATE_BACKFACE_STENCIL_OPS, p138*/ +#define _3DSTATE_BACKFACE_STENCIL_OPS (CMD_3D | (0x8<<24)) +#define BFO_ENABLE_STENCIL_REF (1<<23) +#define BFO_STENCIL_REF_SHIFT 15 +#define BFO_STENCIL_REF_MASK (0xff<<15) +#define BFO_ENABLE_STENCIL_FUNCS (1<<14) +#define BFO_STENCIL_TEST_SHIFT 11 +#define BFO_STENCIL_TEST_MASK (0x7<<11) +#define BFO_STENCIL_FAIL_SHIFT 8 +#define BFO_STENCIL_FAIL_MASK (0x7<<8) +#define BFO_STENCIL_PASS_Z_FAIL_SHIFT 5 +#define BFO_STENCIL_PASS_Z_FAIL_MASK (0x7<<5) +#define BFO_STENCIL_PASS_Z_PASS_SHIFT 2 +#define BFO_STENCIL_PASS_Z_PASS_MASK (0x7<<2) +#define BFO_ENABLE_STENCIL_TWO_SIDE (1<<1) +#define BFO_STENCIL_TWO_SIDE (1<<0) + +/* 3DSTATE_BACKFACE_STENCIL_MASKS, p140 */ +#define _3DSTATE_BACKFACE_STENCIL_MASKS (CMD_3D | (0x9<<24)) +#define BFM_ENABLE_STENCIL_TEST_MASK (1<<17) +#define BFM_ENABLE_STENCIL_WRITE_MASK (1<<16) +#define BFM_STENCIL_TEST_MASK_SHIFT 8 +#define BFM_STENCIL_TEST_MASK_MASK (0xff<<8) +#define BFM_STENCIL_WRITE_MASK_SHIFT 0 +#define BFM_STENCIL_WRITE_MASK_MASK (0xff<<0) + +/* 3DSTATE_BIN_CONTROL p141 */ + +/* p143 */ +#define _3DSTATE_BUF_INFO_CMD (CMD_3D | (0x1d<<24) | (0x8e<<16) | 1) +/* Dword 1 */ +#define BUF_3D_ID_COLOR_BACK (0x3<<24) +#define BUF_3D_ID_DEPTH (0x7<<24) +#define BUF_3D_USE_FENCE (1<<23) +#define BUF_3D_TILED_SURFACE (1<<22) +#define BUF_3D_TILE_WALK_X 0 +#define BUF_3D_TILE_WALK_Y (1<<21) +#define BUF_3D_PITCH(x) (x) +/* Dword 2 */ +#define BUF_3D_ADDR(x) ((x) & ~0x3) + +/* 3DSTATE_CHROMA_KEY */ + +/* 3DSTATE_CLEAR_PARAMETERS, p150 */ +#define _3DSTATE_CLEAR_PARAMETERS (CMD_3D | (0x1d<<24) | (0x9c<<16) | 5) +/* Dword 1 */ +#define CLEARPARAM_CLEAR_RECT (1 << 16) +#define CLEARPARAM_ZONE_INIT (0 << 16) +#define CLEARPARAM_WRITE_COLOR (1 << 2) +#define CLEARPARAM_WRITE_DEPTH (1 << 1) +#define CLEARPARAM_WRITE_STENCIL (1 << 0) + +/* 3DSTATE_CONSTANT_BLEND_COLOR, p153 */ +#define _3DSTATE_CONST_BLEND_COLOR_CMD (CMD_3D | (0x1d<<24) | (0x88<<16)) + +/* 3DSTATE_COORD_SET_BINDINGS, p154 */ +#define _3DSTATE_COORD_SET_BINDINGS (CMD_3D | (0x16<<24)) +#define CSB_TCB(iunit, eunit) ((eunit)<<(iunit*3)) + +/* p156 */ +#define _3DSTATE_DFLT_DIFFUSE_CMD (CMD_3D | (0x1d<<24) | (0x99<<16)) + +/* p157 */ +#define _3DSTATE_DFLT_SPEC_CMD (CMD_3D | (0x1d<<24) | (0x9a<<16)) + +/* p158 */ +#define _3DSTATE_DFLT_Z_CMD (CMD_3D | (0x1d<<24) | (0x98<<16)) + +/* 3DSTATE_DEPTH_OFFSET_SCALE, p159 */ +#define _3DSTATE_DEPTH_OFFSET_SCALE (CMD_3D | (0x1d<<24) | (0x97<<16)) +/* scale in dword 1 */ + +/* The depth subrectangle is not supported, but must be disabled. */ +/* 3DSTATE_DEPTH_SUBRECT_DISABLE, p160 */ +#define _3DSTATE_DEPTH_SUBRECT_DISABLE (CMD_3D | (0x1c<<24) | (0x11<<19) | (1 << 1) | (0 << 0)) + +/* p161 */ +#define _3DSTATE_DST_BUF_VARS_CMD (CMD_3D | (0x1d<<24) | (0x85<<16)) +/* Dword 1 */ +#define TEX_DEFAULT_COLOR_OGL (0<<30) +#define TEX_DEFAULT_COLOR_D3D (1<<30) +#define ZR_EARLY_DEPTH (1<<29) +#define LOD_PRECLAMP_OGL (1<<28) +#define LOD_PRECLAMP_D3D (0<<28) +#define DITHER_FULL_ALWAYS (0<<26) +#define DITHER_FULL_ON_FB_BLEND (1<<26) +#define DITHER_CLAMPED_ALWAYS (2<<26) +#define LINEAR_GAMMA_BLEND_32BPP (1<<25) +#define DEBUG_DISABLE_ENH_DITHER (1<<24) +#define DSTORG_HORT_BIAS(x) ((x)<<20) +#define DSTORG_VERT_BIAS(x) ((x)<<16) +#define COLOR_4_2_2_CHNL_WRT_ALL 0 +#define COLOR_4_2_2_CHNL_WRT_Y (1<<12) +#define COLOR_4_2_2_CHNL_WRT_CR (2<<12) +#define COLOR_4_2_2_CHNL_WRT_CB (3<<12) +#define COLOR_4_2_2_CHNL_WRT_CRCB (4<<12) +#define COLR_BUF_8BIT 0 +#define COLR_BUF_RGB555 (1<<8) +#define COLR_BUF_RGB565 (2<<8) +#define COLR_BUF_ARGB8888 (3<<8) +#define COLR_BUF_ARGB4444 (8<<8) +#define COLR_BUF_ARGB1555 (9<<8) +#define COLR_BUF_ARGB2AAA (0xa<<8) +#define DEPTH_FRMT_16_FIXED 0 +#define DEPTH_FRMT_16_FLOAT (1<<2) +#define DEPTH_FRMT_24_FIXED_8_OTHER (2<<2) +#define VERT_LINE_STRIDE_1 (1<<1) +#define VERT_LINE_STRIDE_0 (0<<1) +#define VERT_LINE_STRIDE_OFS_1 1 +#define VERT_LINE_STRIDE_OFS_0 0 + +/* p166 */ +#define _3DSTATE_DRAW_RECT_CMD (CMD_3D|(0x1d<<24)|(0x80<<16)|3) +/* Dword 1 */ +#define DRAW_RECT_DIS_DEPTH_OFS (1<<30) +#define DRAW_DITHER_OFS_X(x) ((x)<<26) +#define DRAW_DITHER_OFS_Y(x) ((x)<<24) +/* Dword 2 */ +#define DRAW_YMIN(x) ((x)<<16) +#define DRAW_XMIN(x) (x) +/* Dword 3 */ +#define DRAW_YMAX(x) ((x-1)<<16) +#define DRAW_XMAX(x) (x-1) +/* Dword 4 */ +#define DRAW_YORG(x) ((x)<<16) +#define DRAW_XORG(x) (x) + +/* 3DSTATE_FILTER_COEFFICIENTS_4X4, p170 */ + +/* 3DSTATE_FILTER_COEFFICIENTS_6X5, p172 */ + +/* _3DSTATE_FOG_COLOR, p173 */ +#define _3DSTATE_FOG_COLOR_CMD (CMD_3D|(0x15<<24)) +#define FOG_COLOR_RED(x) ((x)<<16) +#define FOG_COLOR_GREEN(x) ((x)<<8) +#define FOG_COLOR_BLUE(x) (x) + +/* _3DSTATE_FOG_MODE, p174 */ +#define _3DSTATE_FOG_MODE_CMD (CMD_3D|(0x1d<<24)|(0x89<<16)|2) +/* Dword 1 */ +#define FMC1_FOGFUNC_MODIFY_ENABLE (1<<31) +#define FMC1_FOGFUNC_VERTEX (0<<28) +#define FMC1_FOGFUNC_PIXEL_EXP (1<<28) +#define FMC1_FOGFUNC_PIXEL_EXP2 (2<<28) +#define FMC1_FOGFUNC_PIXEL_LINEAR (3<<28) +#define FMC1_FOGFUNC_MASK (3<<28) +#define FMC1_FOGINDEX_MODIFY_ENABLE (1<<27) +#define FMC1_FOGINDEX_Z (0<<25) +#define FMC1_FOGINDEX_W (1<<25) +#define FMC1_C1_C2_MODIFY_ENABLE (1<<24) +#define FMC1_DENSITY_MODIFY_ENABLE (1<<23) +#define FMC1_C1_ONE (1<<13) +#define FMC1_C1_MASK (0xffff<<4) +/* Dword 2 */ +#define FMC2_C2_ONE (1<<16) +/* Dword 3 */ +#define FMC3_D_ONE (1<<16) + +/* _3DSTATE_INDEPENDENT_ALPHA_BLEND, p177 */ +#define _3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD (CMD_3D|(0x0b<<24)) +#define IAB_MODIFY_ENABLE (1<<23) +#define IAB_ENABLE (1<<22) +#define IAB_MODIFY_FUNC (1<<21) +#define IAB_FUNC_SHIFT 16 +#define IAB_MODIFY_SRC_FACTOR (1<<11) +#define IAB_SRC_FACTOR_SHIFT 6 +#define IAB_SRC_FACTOR_MASK (BLENDFACT_MASK<<6) +#define IAB_MODIFY_DST_FACTOR (1<<5) +#define IAB_DST_FACTOR_SHIFT 0 +#define IAB_DST_FACTOR_MASK (BLENDFACT_MASK<<0) + +#define BLENDFACT_ZERO 0x01 +#define BLENDFACT_ONE 0x02 +#define BLENDFACT_SRC_COLR 0x03 +#define BLENDFACT_INV_SRC_COLR 0x04 +#define BLENDFACT_SRC_ALPHA 0x05 +#define BLENDFACT_INV_SRC_ALPHA 0x06 +#define BLENDFACT_DST_ALPHA 0x07 +#define BLENDFACT_INV_DST_ALPHA 0x08 +#define BLENDFACT_DST_COLR 0x09 +#define BLENDFACT_INV_DST_COLR 0x0a +#define BLENDFACT_SRC_ALPHA_SATURATE 0x0b +#define BLENDFACT_CONST_COLOR 0x0c +#define BLENDFACT_INV_CONST_COLOR 0x0d +#define BLENDFACT_CONST_ALPHA 0x0e +#define BLENDFACT_INV_CONST_ALPHA 0x0f +#define BLENDFACT_MASK 0x0f + +#define BLENDFUNC_ADD 0x0 +#define BLENDFUNC_SUBTRACT 0x1 +#define BLENDFUNC_REVERSE_SUBTRACT 0x2 +#define BLENDFUNC_MIN 0x3 +#define BLENDFUNC_MAX 0x4 +#define BLENDFUNC_MASK 0x7 + +/* 3DSTATE_LOAD_INDIRECT, p180 */ + +#define _3DSTATE_LOAD_INDIRECT (CMD_3D|(0x1d<<24)|(0x7<<16)) +#define LI0_STATE_STATIC_INDIRECT (0x01<<8) +#define LI0_STATE_DYNAMIC_INDIRECT (0x02<<8) +#define LI0_STATE_SAMPLER (0x04<<8) +#define LI0_STATE_MAP (0x08<<8) +#define LI0_STATE_PROGRAM (0x10<<8) +#define LI0_STATE_CONSTANTS (0x20<<8) + +#define SIS0_BUFFER_ADDRESS(x) ((x)&~0x3) +#define SIS0_FORCE_LOAD (1<<1) +#define SIS0_BUFFER_VALID (1<<0) +#define SIS1_BUFFER_LENGTH(x) ((x)&0xff) + +#define DIS0_BUFFER_ADDRESS(x) ((x)&~0x3) +#define DIS0_BUFFER_RESET (1<<1) +#define DIS0_BUFFER_VALID (1<<0) + +#define SSB0_BUFFER_ADDRESS(x) ((x)&~0x3) +#define SSB0_FORCE_LOAD (1<<1) +#define SSB0_BUFFER_VALID (1<<0) +#define SSB1_BUFFER_LENGTH(x) ((x)&0xff) + +#define MSB0_BUFFER_ADDRESS(x) ((x)&~0x3) +#define MSB0_FORCE_LOAD (1<<1) +#define MSB0_BUFFER_VALID (1<<0) +#define MSB1_BUFFER_LENGTH(x) ((x)&0xff) + +#define PSP0_BUFFER_ADDRESS(x) ((x)&~0x3) +#define PSP0_FORCE_LOAD (1<<1) +#define PSP0_BUFFER_VALID (1<<0) +#define PSP1_BUFFER_LENGTH(x) ((x)&0xff) + +#define PSC0_BUFFER_ADDRESS(x) ((x)&~0x3) +#define PSC0_FORCE_LOAD (1<<1) +#define PSC0_BUFFER_VALID (1<<0) +#define PSC1_BUFFER_LENGTH(x) ((x)&0xff) + +/* _3DSTATE_RASTERIZATION_RULES */ +#define _3DSTATE_RASTER_RULES_CMD (CMD_3D|(0x07<<24)) +#define ENABLE_POINT_RASTER_RULE (1<<15) +#define OGL_POINT_RASTER_RULE (1<<13) +#define ENABLE_TEXKILL_3D_4D (1<<10) +#define TEXKILL_3D (0<<9) +#define TEXKILL_4D (1<<9) +#define ENABLE_LINE_STRIP_PROVOKE_VRTX (1<<8) +#define ENABLE_TRI_FAN_PROVOKE_VRTX (1<<5) +#define LINE_STRIP_PROVOKE_VRTX(x) ((x)<<6) +#define TRI_FAN_PROVOKE_VRTX(x) ((x)<<3) + +/* _3DSTATE_SCISSOR_ENABLE, p256 */ +#define _3DSTATE_SCISSOR_ENABLE_CMD (CMD_3D|(0x1c<<24)|(0x10<<19)) +#define ENABLE_SCISSOR_RECT ((1<<1) | 1) +#define DISABLE_SCISSOR_RECT (1<<1) + +/* _3DSTATE_SCISSOR_RECTANGLE_0, p257 */ +#define _3DSTATE_SCISSOR_RECT_0_CMD (CMD_3D|(0x1d<<24)|(0x81<<16)|1) +/* Dword 1 */ +#define SCISSOR_RECT_0_YMIN(x) ((x)<<16) +#define SCISSOR_RECT_0_XMIN(x) (x) +/* Dword 2 */ +#define SCISSOR_RECT_0_YMAX(x) ((x)<<16) +#define SCISSOR_RECT_0_XMAX(x) (x) + +/* p189 */ +#define _3DSTATE_LOAD_STATE_IMMEDIATE_1 (CMD_3D | (0x1d<<24) | (0x04<<16)) +#define I1_LOAD_S(n) (1<<(4+n)) + +#define S0_VB_OFFSET_MASK 0xffffffc +#define S0_AUTO_CACHE_INV_DISABLE (1<<0) + +#define S1_VERTEX_WIDTH_SHIFT 24 +#define S1_VERTEX_WIDTH_MASK (0x3f<<24) +#define S1_VERTEX_PITCH_SHIFT 16 +#define S1_VERTEX_PITCH_MASK (0x3f<<16) + +#define TEXCOORDFMT_2D 0x0 +#define TEXCOORDFMT_3D 0x1 +#define TEXCOORDFMT_4D 0x2 +#define TEXCOORDFMT_1D 0x3 +#define TEXCOORDFMT_2D_16 0x4 +#define TEXCOORDFMT_4D_16 0x5 +#define TEXCOORDFMT_NOT_PRESENT 0xf +#define S2_TEXCOORD_FMT0_MASK 0xf +#define S2_TEXCOORD_FMT1_SHIFT 4 +#define S2_TEXCOORD_FMT(unit, type) ((type)<<(unit*4)) +#define S2_TEXCOORD_NONE (~0U) + +#define TEXCOORD_WRAP_SHORTEST_TCX 8 +#define TEXCOORD_WRAP_SHORTEST_TCY 4 +#define TEXCOORD_WRAP_SHORTEST_TCZ 2 +#define TEXCOORD_PERSPECTIVE_DISABLE 1 + +#define S3_WRAP_SHORTEST_TCX(unit) (TEXCOORD_WRAP_SHORTEST_TCX << ((unit) * 4)) +#define S3_WRAP_SHORTEST_TCY(unit) (TEXCOORD_WRAP_SHORTEST_TCY << ((unit) * 4)) +#define S3_WRAP_SHORTEST_TCZ(unit) (TEXCOORD_WRAP_SHORTEST_TCZ << ((unit) * 4)) +#define S3_PERSPECTIVE_DISABLE(unit) (TEXCOORD_PERSPECTIVE_DISABLE << ((unit) * 4)) + +/* S3 not interesting */ + +#define S4_POINT_WIDTH_SHIFT 23 +#define S4_POINT_WIDTH_MASK (0x1ff<<23) +#define S4_LINE_WIDTH_SHIFT 19 +#define S4_LINE_WIDTH_ONE (0x2<<19) +#define S4_LINE_WIDTH_MASK (0xf<<19) +#define S4_FLATSHADE_ALPHA (1<<18) +#define S4_FLATSHADE_FOG (1<<17) +#define S4_FLATSHADE_SPECULAR (1<<16) +#define S4_FLATSHADE_COLOR (1<<15) +#define S4_CULLMODE_BOTH (0<<13) +#define S4_CULLMODE_NONE (1<<13) +#define S4_CULLMODE_CW (2<<13) +#define S4_CULLMODE_CCW (3<<13) +#define S4_CULLMODE_MASK (3<<13) +#define S4_VFMT_POINT_WIDTH (1<<12) +#define S4_VFMT_SPEC_FOG (1<<11) +#define S4_VFMT_COLOR (1<<10) +#define S4_VFMT_DEPTH_OFFSET (1<<9) +#define S4_VFMT_XYZ (1<<6) +#define S4_VFMT_XYZW (2<<6) +#define S4_VFMT_XY (3<<6) +#define S4_VFMT_XYW (4<<6) +#define S4_VFMT_XYZW_MASK (7<<6) +#define S4_FORCE_DEFAULT_DIFFUSE (1<<5) +#define S4_FORCE_DEFAULT_SPECULAR (1<<4) +#define S4_LOCAL_DEPTH_OFFSET_ENABLE (1<<3) +#define S4_VFMT_FOG_PARAM (1<<2) +#define S4_SPRITE_POINT_ENABLE (1<<1) +#define S4_LINE_ANTIALIAS_ENABLE (1<<0) + +#define S4_VFMT_MASK (S4_VFMT_POINT_WIDTH | \ + S4_VFMT_SPEC_FOG | \ + S4_VFMT_COLOR | \ + S4_VFMT_DEPTH_OFFSET | \ + S4_VFMT_XYZW_MASK | \ + S4_VFMT_FOG_PARAM) + +#define S5_WRITEDISABLE_ALPHA (1<<31) +#define S5_WRITEDISABLE_RED (1<<30) +#define S5_WRITEDISABLE_GREEN (1<<29) +#define S5_WRITEDISABLE_BLUE (1<<28) +#define S5_WRITEDISABLE_MASK (0xf<<28) +#define S5_FORCE_DEFAULT_POINT_SIZE (1<<27) +#define S5_LAST_PIXEL_ENABLE (1<<26) +#define S5_GLOBAL_DEPTH_OFFSET_ENABLE (1<<25) +#define S5_FOG_ENABLE (1<<24) +#define S5_STENCIL_REF_SHIFT 16 +#define S5_STENCIL_REF_MASK (0xff<<16) +#define S5_STENCIL_TEST_FUNC_SHIFT 13 +#define S5_STENCIL_TEST_FUNC_MASK (0x7<<13) +#define S5_STENCIL_FAIL_SHIFT 10 +#define S5_STENCIL_FAIL_MASK (0x7<<10) +#define S5_STENCIL_PASS_Z_FAIL_SHIFT 7 +#define S5_STENCIL_PASS_Z_FAIL_MASK (0x7<<7) +#define S5_STENCIL_PASS_Z_PASS_SHIFT 4 +#define S5_STENCIL_PASS_Z_PASS_MASK (0x7<<4) +#define S5_STENCIL_WRITE_ENABLE (1<<3) +#define S5_STENCIL_TEST_ENABLE (1<<2) +#define S5_COLOR_DITHER_ENABLE (1<<1) +#define S5_LOGICOP_ENABLE (1<<0) + +#define COMPAREFUNC_ALWAYS 0 +#define COMPAREFUNC_NEVER 0x1 +#define COMPAREFUNC_LESS 0x2 +#define COMPAREFUNC_EQUAL 0x3 +#define COMPAREFUNC_LEQUAL 0x4 +#define COMPAREFUNC_GREATER 0x5 +#define COMPAREFUNC_NOTEQUAL 0x6 +#define COMPAREFUNC_GEQUAL 0x7 + +#define STENCILOP_KEEP 0 +#define STENCILOP_ZERO 0x1 +#define STENCILOP_REPLACE 0x2 +#define STENCILOP_INCRSAT 0x3 +#define STENCILOP_DECRSAT 0x4 +#define STENCILOP_INCR 0x5 +#define STENCILOP_DECR 0x6 +#define STENCILOP_INVERT 0x7 + +#define S6_ALPHA_TEST_ENABLE (1<<31) +#define S6_ALPHA_TEST_FUNC_SHIFT 28 +#define S6_ALPHA_TEST_FUNC_MASK (0x7<<28) +#define S6_ALPHA_REF_SHIFT 20 +#define S6_ALPHA_REF_MASK (0xff<<20) +#define S6_DEPTH_TEST_ENABLE (1<<19) +#define S6_DEPTH_TEST_FUNC_SHIFT 16 +#define S6_DEPTH_TEST_FUNC_MASK (0x7<<16) +#define S6_CBUF_BLEND_ENABLE (1<<15) +#define S6_CBUF_BLEND_FUNC_SHIFT 12 +#define S6_CBUF_BLEND_FUNC_MASK (0x7<<12) +#define S6_CBUF_SRC_BLEND_FACT_SHIFT 8 +#define S6_CBUF_SRC_BLEND_FACT_MASK (0xf<<8) +#define S6_CBUF_DST_BLEND_FACT_SHIFT 4 +#define S6_CBUF_DST_BLEND_FACT_MASK (0xf<<4) +#define S6_DEPTH_WRITE_ENABLE (1<<3) +#define S6_COLOR_WRITE_ENABLE (1<<2) +#define S6_TRISTRIP_PV_SHIFT 0 +#define S6_TRISTRIP_PV_MASK (0x3<<0) + +#define S7_DEPTH_OFFSET_CONST_MASK ~0 + +/* 3DSTATE_MAP_DEINTERLACER_PARAMETERS */ +/* 3DSTATE_MAP_PALETTE_LOAD_32, p206 */ + +/* _3DSTATE_MODES_4, p218 */ +#define _3DSTATE_MODES_4_CMD (CMD_3D|(0x0d<<24)) +#define ENABLE_LOGIC_OP_FUNC (1<<23) +#define LOGIC_OP_FUNC(x) ((x)<<18) +#define LOGICOP_MASK (0xf<<18) +#define LOGICOP_COPY 0xc +#define MODE4_ENABLE_STENCIL_TEST_MASK ((1<<17)|(0xff00)) +#define ENABLE_STENCIL_TEST_MASK (1<<17) +#define STENCIL_TEST_MASK(x) ((x)<<8) +#define MODE4_ENABLE_STENCIL_WRITE_MASK ((1<<16)|(0x00ff)) +#define ENABLE_STENCIL_WRITE_MASK (1<<16) +#define STENCIL_WRITE_MASK(x) ((x)&0xff) + +/* _3DSTATE_MODES_5, p220 */ +#define _3DSTATE_MODES_5_CMD (CMD_3D|(0x0c<<24)) +#define PIPELINE_FLUSH_RENDER_CACHE (1<<18) +#define PIPELINE_FLUSH_TEXTURE_CACHE (1<<16) + +/* p221 */ +#define _3DSTATE_PIXEL_SHADER_CONSTANTS (CMD_3D|(0x1d<<24)|(0x6<<16)) +#define PS1_REG(n) (1<<(n)) +#define PS2_CONST_X(n) (n) +#define PS3_CONST_Y(n) (n) +#define PS4_CONST_Z(n) (n) +#define PS5_CONST_W(n) (n) + +/* p222 */ + +#define I915_MAX_TEX_INDIRECT 4 +#define I915_MAX_TEX_INSN 32 +#define I915_MAX_ALU_INSN 64 +#define I915_MAX_DECL_INSN 27 +#define I915_MAX_TEMPORARY 16 + +/* Each instruction is 3 dwords long, though most don't require all + * this space. Maximum of 123 instructions. Smaller maxes per insn + * type. + */ +#define _3DSTATE_PIXEL_SHADER_PROGRAM (CMD_3D|(0x1d<<24)|(0x5<<16)) + +#define REG_TYPE_R 0 /* temporary regs, no need to + * dcl, must be written before + * read -- Preserved between + * phases. + */ +#define REG_TYPE_T 1 /* Interpolated values, must be + * dcl'ed before use. + * + * 0..7: texture coord, + * 8: diffuse spec, + * 9: specular color, + * 10: fog parameter in w. + */ +#define REG_TYPE_CONST 2 /* Restriction: only one const + * can be referenced per + * instruction, though it may be + * selected for multiple inputs. + * Constants not initialized + * default to zero. + */ +#define REG_TYPE_S 3 /* sampler */ +#define REG_TYPE_OC 4 /* output color (rgba) */ +#define REG_TYPE_OD 5 /* output depth (w), xyz are + * temporaries. If not written, + * interpolated depth is used? + */ +#define REG_TYPE_U 6 /* unpreserved temporaries */ +#define REG_TYPE_MASK 0x7 +#define REG_NR_MASK 0xf + +/* REG_TYPE_T: + */ +#define T_TEX0 0 +#define T_TEX1 1 +#define T_TEX2 2 +#define T_TEX3 3 +#define T_TEX4 4 +#define T_TEX5 5 +#define T_TEX6 6 +#define T_TEX7 7 +#define T_DIFFUSE 8 +#define T_SPECULAR 9 +#define T_FOG_W 10 /* interpolated fog is in W coord */ + +/* Arithmetic instructions */ + +/* .replicate_swizzle == selection and replication of a particular + * scalar channel, ie., .xxxx, .yyyy, .zzzz or .wwww + */ +#define A0_NOP (0x0<<24) /* no operation */ +#define A0_ADD (0x1<<24) /* dst = src0 + src1 */ +#define A0_MOV (0x2<<24) /* dst = src0 */ +#define A0_MUL (0x3<<24) /* dst = src0 * src1 */ +#define A0_MAD (0x4<<24) /* dst = src0 * src1 + src2 */ +#define A0_DP2ADD (0x5<<24) /* dst.xyzw = src0.xy dot src1.xy + src2.replicate_swizzle */ +#define A0_DP3 (0x6<<24) /* dst.xyzw = src0.xyz dot src1.xyz */ +#define A0_DP4 (0x7<<24) /* dst.xyzw = src0.xyzw dot src1.xyzw */ +#define A0_FRC (0x8<<24) /* dst = src0 - floor(src0) */ +#define A0_RCP (0x9<<24) /* dst.xyzw = 1/(src0.replicate_swizzle) */ +#define A0_RSQ (0xa<<24) /* dst.xyzw = 1/(sqrt(abs(src0.replicate_swizzle))) */ +#define A0_EXP (0xb<<24) /* dst.xyzw = exp2(src0.replicate_swizzle) */ +#define A0_LOG (0xc<<24) /* dst.xyzw = log2(abs(src0.replicate_swizzle)) */ +#define A0_CMP (0xd<<24) /* dst = (src0 >= 0.0) ? src1 : src2 */ +#define A0_MIN (0xe<<24) /* dst = (src0 < src1) ? src0 : src1 */ +#define A0_MAX (0xf<<24) /* dst = (src0 >= src1) ? src0 : src1 */ +#define A0_FLR (0x10<<24) /* dst = floor(src0) */ +#define A0_MOD (0x11<<24) /* dst = src0 fmod 1.0 */ +#define A0_TRC (0x12<<24) /* dst = int(src0) */ +#define A0_SGE (0x13<<24) /* dst = src0 >= src1 ? 1.0 : 0.0 */ +#define A0_SLT (0x14<<24) /* dst = src0 < src1 ? 1.0 : 0.0 */ +#define A0_DEST_SATURATE (1<<22) +#define A0_DEST_TYPE_SHIFT 19 +/* Allow: R, OC, OD, U */ +#define A0_DEST_NR_SHIFT 14 +/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ +#define A0_DEST_CHANNEL_X (1<<10) +#define A0_DEST_CHANNEL_Y (2<<10) +#define A0_DEST_CHANNEL_Z (4<<10) +#define A0_DEST_CHANNEL_W (8<<10) +#define A0_DEST_CHANNEL_ALL (0xf<<10) +#define A0_DEST_CHANNEL_SHIFT 10 +#define A0_SRC0_TYPE_SHIFT 7 +#define A0_SRC0_NR_SHIFT 2 + +#define A0_DEST_CHANNEL_XY (A0_DEST_CHANNEL_X|A0_DEST_CHANNEL_Y) +#define A0_DEST_CHANNEL_XYZ (A0_DEST_CHANNEL_XY|A0_DEST_CHANNEL_Z) + +#define SRC_X 0 +#define SRC_Y 1 +#define SRC_Z 2 +#define SRC_W 3 +#define SRC_ZERO 4 +#define SRC_ONE 5 + +#define A1_SRC0_CHANNEL_X_NEGATE (1<<31) +#define A1_SRC0_CHANNEL_X_SHIFT 28 +#define A1_SRC0_CHANNEL_Y_NEGATE (1<<27) +#define A1_SRC0_CHANNEL_Y_SHIFT 24 +#define A1_SRC0_CHANNEL_Z_NEGATE (1<<23) +#define A1_SRC0_CHANNEL_Z_SHIFT 20 +#define A1_SRC0_CHANNEL_W_NEGATE (1<<19) +#define A1_SRC0_CHANNEL_W_SHIFT 16 +#define A1_SRC1_TYPE_SHIFT 13 +#define A1_SRC1_NR_SHIFT 8 +#define A1_SRC1_CHANNEL_X_NEGATE (1<<7) +#define A1_SRC1_CHANNEL_X_SHIFT 4 +#define A1_SRC1_CHANNEL_Y_NEGATE (1<<3) +#define A1_SRC1_CHANNEL_Y_SHIFT 0 + +#define A2_SRC1_CHANNEL_Z_NEGATE (1<<31) +#define A2_SRC1_CHANNEL_Z_SHIFT 28 +#define A2_SRC1_CHANNEL_W_NEGATE (1<<27) +#define A2_SRC1_CHANNEL_W_SHIFT 24 +#define A2_SRC2_TYPE_SHIFT 21 +#define A2_SRC2_NR_SHIFT 16 +#define A2_SRC2_CHANNEL_X_NEGATE (1<<15) +#define A2_SRC2_CHANNEL_X_SHIFT 12 +#define A2_SRC2_CHANNEL_Y_NEGATE (1<<11) +#define A2_SRC2_CHANNEL_Y_SHIFT 8 +#define A2_SRC2_CHANNEL_Z_NEGATE (1<<7) +#define A2_SRC2_CHANNEL_Z_SHIFT 4 +#define A2_SRC2_CHANNEL_W_NEGATE (1<<3) +#define A2_SRC2_CHANNEL_W_SHIFT 0 + +/* Texture instructions */ +#define T0_TEXLD (0x15<<24) /* Sample texture using predeclared + * sampler and address, and output + * filtered texel data to destination + * register */ +#define T0_TEXLDP (0x16<<24) /* Same as texld but performs a + * perspective divide of the texture + * coordinate .xyz values by .w before + * sampling. */ +#define T0_TEXLDB (0x17<<24) /* Same as texld but biases the + * computed LOD by w. Only S4.6 two's + * comp is used. This implies that a + * float to fixed conversion is + * done. */ +#define T0_TEXKILL (0x18<<24) /* Does not perform a sampling + * operation. Simply kills the pixel + * if any channel of the address + * register is < 0.0. */ +#define T0_DEST_TYPE_SHIFT 19 +/* Allow: R, OC, OD, U */ +/* Note: U (unpreserved) regs do not retain their values between + * phases (cannot be used for feedback) + * + * Note: oC and OD registers can only be used as the destination of a + * texture instruction once per phase (this is an implementation + * restriction). + */ +#define T0_DEST_NR_SHIFT 14 +/* Allow R: 0..15, OC,OD: 0..0, U: 0..2 */ +#define T0_SAMPLER_NR_SHIFT 0 /* This field ignored for TEXKILL */ +#define T0_SAMPLER_NR_MASK (0xf<<0) + +#define T1_ADDRESS_REG_TYPE_SHIFT 24 /* Reg to use as texture coord */ +/* Allow R, T, OC, OD -- R, OC, OD are 'dependent' reads, new program phase */ +#define T1_ADDRESS_REG_NR_SHIFT 17 +#define T2_MBZ 0 + +/* Declaration instructions */ +#define D0_DCL (0x19<<24) /* Declare a t (interpolated attrib) + * register or an s (sampler) + * register. */ +#define D0_SAMPLE_TYPE_SHIFT 22 +#define D0_SAMPLE_TYPE_2D (0x0<<22) +#define D0_SAMPLE_TYPE_CUBE (0x1<<22) +#define D0_SAMPLE_TYPE_VOLUME (0x2<<22) +#define D0_SAMPLE_TYPE_MASK (0x3<<22) + +#define D0_TYPE_SHIFT 19 +/* Allow: T, S */ +#define D0_NR_SHIFT 14 +/* Allow T: 0..10, S: 0..15 */ +#define D0_CHANNEL_X (1<<10) +#define D0_CHANNEL_Y (2<<10) +#define D0_CHANNEL_Z (4<<10) +#define D0_CHANNEL_W (8<<10) +#define D0_CHANNEL_ALL (0xf<<10) +#define D0_CHANNEL_NONE (0<<10) + +#define D0_CHANNEL_XY (D0_CHANNEL_X|D0_CHANNEL_Y) +#define D0_CHANNEL_XYZ (D0_CHANNEL_XY|D0_CHANNEL_Z) + +/* I915 Errata: Do not allow (xz), (xw), (xzw) combinations for diffuse + * or specular declarations. + * + * For T dcls, only allow: (x), (xy), (xyz), (w), (xyzw) + * + * Must be zero for S (sampler) dcls + */ +#define D1_MBZ 0 +#define D2_MBZ 0 + +/* p207. + * The DWORD count is 3 times the number of bits set in MS1_MAPMASK_MASK + */ +#define _3DSTATE_MAP_STATE (CMD_3D|(0x1d<<24)|(0x0<<16)) + +#define MS1_MAPMASK_SHIFT 0 +#define MS1_MAPMASK_MASK (0x8fff<<0) + +#define MS2_UNTRUSTED_SURFACE (1<<31) +#define MS2_ADDRESS_MASK 0xfffffffc +#define MS2_VERTICAL_LINE_STRIDE (1<<1) +#define MS2_VERTICAL_OFFSET (1<<1) + +#define MS3_HEIGHT_SHIFT 21 +#define MS3_WIDTH_SHIFT 10 +#define MS3_PALETTE_SELECT (1<<9) +#define MS3_MAPSURF_FORMAT_SHIFT 7 +#define MS3_MAPSURF_FORMAT_MASK (0x7<<7) +#define MAPSURF_8BIT (1<<7) +#define MAPSURF_16BIT (2<<7) +#define MAPSURF_32BIT (3<<7) +#define MAPSURF_422 (5<<7) +#define MAPSURF_COMPRESSED (6<<7) +#define MAPSURF_4BIT_INDEXED (7<<7) +#define MS3_MT_FORMAT_MASK (0x7 << 3) +#define MS3_MT_FORMAT_SHIFT 3 +#define MT_4BIT_IDX_ARGB8888 (7<<3) /* SURFACE_4BIT_INDEXED */ +#define MT_8BIT_I8 (0<<3) /* SURFACE_8BIT */ +#define MT_8BIT_L8 (1<<3) +#define MT_8BIT_A8 (4<<3) +#define MT_8BIT_MONO8 (5<<3) +#define MT_16BIT_RGB565 (0<<3) /* SURFACE_16BIT */ +#define MT_16BIT_ARGB1555 (1<<3) +#define MT_16BIT_ARGB4444 (2<<3) +#define MT_16BIT_AY88 (3<<3) +#define MT_16BIT_88DVDU (5<<3) +#define MT_16BIT_BUMP_655LDVDU (6<<3) +#define MT_16BIT_I16 (7<<3) +#define MT_16BIT_L16 (8<<3) +#define MT_16BIT_A16 (9<<3) +#define MT_32BIT_ARGB8888 (0<<3) /* SURFACE_32BIT */ +#define MT_32BIT_ABGR8888 (1<<3) +#define MT_32BIT_XRGB8888 (2<<3) +#define MT_32BIT_XBGR8888 (3<<3) +#define MT_32BIT_QWVU8888 (4<<3) +#define MT_32BIT_AXVU8888 (5<<3) +#define MT_32BIT_LXVU8888 (6<<3) +#define MT_32BIT_XLVU8888 (7<<3) +#define MT_32BIT_ARGB2101010 (8<<3) +#define MT_32BIT_ABGR2101010 (9<<3) +#define MT_32BIT_AWVU2101010 (0xA<<3) +#define MT_32BIT_GR1616 (0xB<<3) +#define MT_32BIT_VU1616 (0xC<<3) +#define MT_32BIT_xI824 (0xD<<3) +#define MT_32BIT_xA824 (0xE<<3) +#define MT_32BIT_xL824 (0xF<<3) +#define MT_422_YCRCB_SWAPY (0<<3) /* SURFACE_422 */ +#define MT_422_YCRCB_NORMAL (1<<3) +#define MT_422_YCRCB_SWAPUV (2<<3) +#define MT_422_YCRCB_SWAPUVY (3<<3) +#define MT_COMPRESS_DXT1 (0<<3) /* SURFACE_COMPRESSED */ +#define MT_COMPRESS_DXT2_3 (1<<3) +#define MT_COMPRESS_DXT4_5 (2<<3) +#define MT_COMPRESS_FXT1 (3<<3) +#define MT_COMPRESS_DXT1_RGB (4<<3) +#define MS3_USE_FENCE_REGS (1<<2) +#define MS3_TILED_SURFACE (1<<1) +#define MS3_TILE_WALK (1<<0) + +/* The pitch is the pitch measured in DWORDS, minus 1 */ +#define MS4_PITCH_SHIFT 21 +#define MS4_CUBE_FACE_ENA_NEGX (1<<20) +#define MS4_CUBE_FACE_ENA_POSX (1<<19) +#define MS4_CUBE_FACE_ENA_NEGY (1<<18) +#define MS4_CUBE_FACE_ENA_POSY (1<<17) +#define MS4_CUBE_FACE_ENA_NEGZ (1<<16) +#define MS4_CUBE_FACE_ENA_POSZ (1<<15) +#define MS4_CUBE_FACE_ENA_MASK (0x3f<<15) +#define MS4_MAX_LOD_SHIFT 9 +#define MS4_MAX_LOD_MASK (0x3f<<9) +#define MS4_MIP_LAYOUT_LEGACY (0<<8) +#define MS4_MIP_LAYOUT_BELOW_LPT (0<<8) +#define MS4_MIP_LAYOUT_RIGHT_LPT (1<<8) +#define MS4_VOLUME_DEPTH_SHIFT 0 +#define MS4_VOLUME_DEPTH_MASK (0xff<<0) + +/* p244. + * The DWORD count is 3 times the number of bits set in SS1_MAPMASK_MASK. + */ +#define _3DSTATE_SAMPLER_STATE (CMD_3D|(0x1d<<24)|(0x1<<16)) + +#define SS1_MAPMASK_SHIFT 0 +#define SS1_MAPMASK_MASK (0x8fff<<0) + +#define SS2_REVERSE_GAMMA_ENABLE (1<<31) +#define SS2_PACKED_TO_PLANAR_ENABLE (1<<30) +#define SS2_COLORSPACE_CONVERSION (1<<29) +#define SS2_CHROMAKEY_SHIFT 27 +#define SS2_BASE_MIP_LEVEL_SHIFT 22 +#define SS2_BASE_MIP_LEVEL_MASK (0x1f<<22) +#define SS2_MIP_FILTER_SHIFT 20 +#define SS2_MIP_FILTER_MASK (0x3<<20) +#define MIPFILTER_NONE 0 +#define MIPFILTER_NEAREST 1 +#define MIPFILTER_LINEAR 3 +#define SS2_MAG_FILTER_SHIFT 17 +#define SS2_MAG_FILTER_MASK (0x7<<17) +#define FILTER_NEAREST 0 +#define FILTER_LINEAR 1 +#define FILTER_ANISOTROPIC 2 +#define FILTER_4X4_1 3 +#define FILTER_4X4_2 4 +#define FILTER_4X4_FLAT 5 +#define FILTER_6X5_MONO 6 /* XXX - check */ +#define SS2_MIN_FILTER_SHIFT 14 +#define SS2_MIN_FILTER_MASK (0x7<<14) +#define SS2_LOD_BIAS_SHIFT 5 +#define SS2_LOD_BIAS_ONE (0x10<<5) +#define SS2_LOD_BIAS_MASK (0x1ff<<5) +/* Shadow requires: + * MT_X8{I,L,A}24 or MT_{I,L,A}16 texture format + * FILTER_4X4_x MIN and MAG filters + */ +#define SS2_SHADOW_ENABLE (1<<4) +#define SS2_MAX_ANISO_MASK (1<<3) +#define SS2_MAX_ANISO_2 (0<<3) +#define SS2_MAX_ANISO_4 (1<<3) +#define SS2_SHADOW_FUNC_SHIFT 0 +#define SS2_SHADOW_FUNC_MASK (0x7<<0) +/* SS2_SHADOW_FUNC values: see COMPAREFUNC_* */ + +#define SS3_MIN_LOD_SHIFT 24 +#define SS3_MIN_LOD_ONE (0x10<<24) +#define SS3_MIN_LOD_MASK (0xff<<24) +#define SS3_KILL_PIXEL_ENABLE (1<<17) +#define SS3_TCX_ADDR_MODE_SHIFT 12 +#define SS3_TCX_ADDR_MODE_MASK (0x7<<12) +#define TEXCOORDMODE_WRAP 0 +#define TEXCOORDMODE_MIRROR 1 +#define TEXCOORDMODE_CLAMP_EDGE 2 +#define TEXCOORDMODE_CUBE 3 +#define TEXCOORDMODE_CLAMP_BORDER 4 +#define TEXCOORDMODE_MIRROR_ONCE 5 +#define SS3_TCY_ADDR_MODE_SHIFT 9 +#define SS3_TCY_ADDR_MODE_MASK (0x7<<9) +#define SS3_TCZ_ADDR_MODE_SHIFT 6 +#define SS3_TCZ_ADDR_MODE_MASK (0x7<<6) +#define SS3_NORMALIZED_COORDS (1<<5) +#define SS3_TEXTUREMAP_INDEX_SHIFT 1 +#define SS3_TEXTUREMAP_INDEX_MASK (0xf<<1) +#define SS3_DEINTERLACER_ENABLE (1<<0) + +#define SS4_BORDER_COLOR_MASK (~0) + +/* 3DSTATE_SPAN_STIPPLE, p258 + */ +#define _3DSTATE_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define ST1_ENABLE (1<<16) +#define ST1_MASK (0xffff) + +#define FLUSH_MAP_CACHE (1<<0) +#define FLUSH_RENDER_CACHE (1<<1) + +/* BLT commands */ +#define COLOR_BLT_CMD (CMD_2D | (0x40 << 22) | 3) +#define XY_COLOR_BLT_CMD (CMD_2D | (0x50 << 22) | 4) +#define XY_SETUP_CLIP_BLT_CMD (CMD_2D | (0x03 << 22) | 1) +#define XY_SRC_COPY_BLT_CMD (CMD_2D | (0x53 << 22) | 6) +#define SRC_COPY_BLT_CMD (CMD_2D | (0x43 << 22) | 4) + +#define XY_MONO_PAT_BLT_CMD (CMD_2D | (0x52<<22)|0x7) +#define XY_MONO_PAT_VERT_SEED ((1<<10) | (1<<9)|(1<<8)) +#define XY_MONO_PAT_HORT_SEED ((1<<14) | (1<<13)|(1<<12)) +#define XY_MONO_SRC_BLT_CMD (CMD_2D | (0x54<<22)|(0x6)) + +#define XY_SETUP_BLT_CMD (CMD_2D | (0x01 << 22) | 6) +#define XY_TEXT_IMMEDIATE_BLIT_CMD (CMD_2D | (0x31 << 22)) +#define XY_TEXT_BYTE_PACKED (1 << 16) + +/* BR00 */ +#define XY_BLT_WRITE_ALPHA (1 << 21) +#define XY_BLT_WRITE_RGB (1 << 20) +#define XY_SRC_TILED (1 << 15) +#define XY_DST_TILED (1 << 11) + +/* BR13 */ +#define BR13_565 (0x1 << 24) +#define BR13_8888 (0x3 << 24) + +#endif /* CAIRO_DRM_INTEL_COMMAND_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-debug.c b/gfx/cairo/cairo/src/drm/cairo-drm-intel-debug.c new file mode 100644 index 000000000000..bfe513667519 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-debug.c @@ -0,0 +1,1209 @@ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "cairoint.h" +#include "cairo-drm-intel-private.h" + +struct debug_stream { + unsigned offset; /* current gtt offset */ + const char *ptr; /* pointer to gtt offset zero */ + const char *end; /* pointer to gtt offset zero */ +}; + +static cairo_bool_t +debug (struct debug_stream *stream, const char *name, uint32_t len) +{ + uint32_t i; + const uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + + if (len == 0) { + fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]); + ASSERT_NOT_REACHED; + return FALSE; + } + + fprintf (stderr, "%04x: ", stream->offset); + + fprintf (stderr, "%s (%d dwords):\n", name, len); + for (i = 0; i < len; i++) + fprintf (stderr, "\t0x%08x\n", ptr[i]); + fprintf (stderr, "\n"); + + stream->offset += len * sizeof(uint32_t); + return TRUE; +} + + +static const char * +get_prim_name (uint32_t val) +{ + switch (val & PRIM3D_MASK) { + case PRIM3D_TRILIST: return "TRILIST"; + case PRIM3D_TRISTRIP: return "TRISTRIP"; + case PRIM3D_TRISTRIP_RVRSE: return "TRISTRIP_RVRSE"; + case PRIM3D_TRIFAN: return "TRIFAN"; + case PRIM3D_POLY: return "POLY"; + case PRIM3D_LINELIST: return "LINELIST"; + case PRIM3D_LINESTRIP: return "LINESTRIP"; + case PRIM3D_RECTLIST: return "RECTLIST"; + case PRIM3D_POINTLIST: return "POINTLIST"; + case PRIM3D_DIB: return "DIB"; + case PRIM3D_CLEAR_RECT: return "CLEAR_RECT"; + case PRIM3D_ZONE_INIT: return "ZONE_INIT"; + default: return "????"; + } +} + +static cairo_bool_t +debug_prim (struct debug_stream *stream, + const char *name, + cairo_bool_t dump_floats, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + const char *prim = get_prim_name( ptr[0] ); + uint32_t i; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s %s (%d dwords):\n", name, prim, len); + fprintf (stderr, "\t0x%08x\n", ptr[0]); + for (i = 1; i < len; i++) { + if (dump_floats) + fprintf (stderr, "\t0x%08x // %f\n", ptr[i], *(float *)&ptr[i]); + else + fprintf (stderr, "\t0x%08x\n", ptr[i]); + } + + fprintf (stderr, "\n"); + + stream->offset += len * sizeof(uint32_t); + return TRUE; +} + +static const char *opcodes[] = { + "NOP", + "ADD", + "MOV", + "MUL", + "MAD", + "DP2ADD", + "DP3", + "DP4", + "FRC", + "RCP", + "RSQ", + "EXP", + "LOG", + "CMP", + "MIN", + "MAX", + "FLR", + "MOD", + "TRC", + "SGE", + "SLT", + "TEXLD", + "TEXLDP", + "TEXLDB", + "TEXKILL", + "DCL", + "0x1a", + "0x1b", + "0x1c", + "0x1d", + "0x1e", + "0x1f", +}; + +static const int args[] = { + 0, /* 0 nop */ + 2, /* 1 add */ + 1, /* 2 mov */ + 2, /* 3 m ul */ + 3, /* 4 mad */ + 3, /* 5 dp2add */ + 2, /* 6 dp3 */ + 2, /* 7 dp4 */ + 1, /* 8 frc */ + 1, /* 9 rcp */ + 1, /* a rsq */ + 1, /* b exp */ + 1, /* c log */ + 3, /* d cmp */ + 2, /* e min */ + 2, /* f max */ + 1, /* 10 flr */ + 1, /* 11 mod */ + 1, /* 12 trc */ + 2, /* 13 sge */ + 2, /* 14 slt */ + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +static const char *regname[] = { + "R", + "T", + "CONST", + "S", + "OC", + "OD", + "U", + "UNKNOWN", +}; + +static void +print_reg_type_nr(uint32_t type, uint32_t nr) +{ + switch (type) { + case REG_TYPE_T: + switch (nr) { + case T_DIFFUSE: + fprintf (stderr, "T_DIFFUSE"); + return; + case T_SPECULAR: + fprintf (stderr, "T_SPECULAR"); + return; + case T_FOG_W: + fprintf (stderr, "T_FOG_W"); + return; + default: + fprintf (stderr, "T_TEX%d", nr); + return; + } + case REG_TYPE_OC: + if (nr == 0) { + fprintf (stderr, "oC"); + return; + } + break; + case REG_TYPE_OD: + if (nr == 0) { + fprintf (stderr, "oD"); + return; + } + break; + default: + break; + } + + fprintf (stderr, "%s[%d]", regname[type], nr); +} + +#define REG_SWIZZLE_MASK 0x7777 +#define REG_NEGATE_MASK 0x8888 + +#define REG_SWIZZLE_XYZW ((SRC_X << A2_SRC2_CHANNEL_X_SHIFT) | \ + (SRC_Y << A2_SRC2_CHANNEL_Y_SHIFT) | \ + (SRC_Z << A2_SRC2_CHANNEL_Z_SHIFT) | \ + (SRC_W << A2_SRC2_CHANNEL_W_SHIFT)) + +static void +print_reg_neg_swizzle(uint32_t reg) +{ + int i; + + if ((reg & REG_SWIZZLE_MASK) == REG_SWIZZLE_XYZW && + (reg & REG_NEGATE_MASK) == 0) + return; + + fprintf (stderr, "."); + + for (i = 12; i >= 0; i -= 4) { + if (reg & (8 << i)) + fprintf (stderr, "-"); + + switch ((reg >> i) & 0x7) { + case 0: + fprintf (stderr, "x"); + break; + case 1: + fprintf (stderr, "y"); + break; + case 2: + fprintf (stderr, "z"); + break; + case 3: + fprintf (stderr, "w"); + break; + case 4: + fprintf (stderr, "0"); + break; + case 5: + fprintf (stderr, "1"); + break; + default: + fprintf (stderr, "?"); + break; + } + } +} + +static void +print_src_reg(uint32_t dword) +{ + uint32_t nr = (dword >> A2_SRC2_NR_SHIFT) & REG_NR_MASK; + uint32_t type = (dword >> A2_SRC2_TYPE_SHIFT) & REG_TYPE_MASK; + print_reg_type_nr(type, nr); + print_reg_neg_swizzle(dword); +} + +static void +print_dest_reg(uint32_t dword) +{ + uint32_t nr = (dword >> A0_DEST_NR_SHIFT) & REG_NR_MASK; + uint32_t type = (dword >> A0_DEST_TYPE_SHIFT) & REG_TYPE_MASK; + print_reg_type_nr(type, nr); + if ((dword & A0_DEST_CHANNEL_ALL) == A0_DEST_CHANNEL_ALL) + return; + fprintf (stderr, "."); + if (dword & A0_DEST_CHANNEL_X) + fprintf (stderr, "x"); + if (dword & A0_DEST_CHANNEL_Y) + fprintf (stderr, "y"); + if (dword & A0_DEST_CHANNEL_Z) + fprintf (stderr, "z"); + if (dword & A0_DEST_CHANNEL_W) + fprintf (stderr, "w"); +} + +#define GET_SRC0_REG(r0, r1) ((r0<<14)|(r1>>A1_SRC0_CHANNEL_W_SHIFT)) +#define GET_SRC1_REG(r0, r1) ((r0<<8)|(r1>>A2_SRC1_CHANNEL_W_SHIFT)) +#define GET_SRC2_REG(r) (r) + +static void +print_arith_op(uint32_t opcode, const uint32_t * program) +{ + if (opcode != A0_NOP) { + print_dest_reg(program[0]); + if (program[0] & A0_DEST_SATURATE) + fprintf (stderr, " = SATURATE "); + else + fprintf (stderr, " = "); + } + + fprintf (stderr, "%s ", opcodes[opcode]); + + print_src_reg(GET_SRC0_REG(program[0], program[1])); + if (args[opcode] == 1) { + fprintf (stderr, "\n"); + return; + } + + fprintf (stderr, ", "); + print_src_reg(GET_SRC1_REG(program[1], program[2])); + if (args[opcode] == 2) { + fprintf (stderr, "\n"); + return; + } + + fprintf (stderr, ", "); + print_src_reg(GET_SRC2_REG(program[2])); + fprintf (stderr, "\n"); + return; +} + +static void +print_tex_op(uint32_t opcode, const uint32_t * program) +{ + print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL); + fprintf (stderr, " = "); + + fprintf (stderr, "%s ", opcodes[opcode]); + + fprintf (stderr, "S[%d],", program[0] & T0_SAMPLER_NR_MASK); + + print_reg_type_nr((program[1] >> T1_ADDRESS_REG_TYPE_SHIFT) & + REG_TYPE_MASK, + (program[1] >> T1_ADDRESS_REG_NR_SHIFT) & REG_NR_MASK); + fprintf (stderr, "\n"); +} + +static void +print_dcl_op(uint32_t opcode, const uint32_t * program) +{ + fprintf (stderr, "%s ", opcodes[opcode]); + print_dest_reg(program[0] | A0_DEST_CHANNEL_ALL); + fprintf (stderr, "\n"); +} + +static void +i915_disassemble_program (const uint32_t * program, uint32_t sz) +{ + uint32_t size = program[0] & 0x1ff; + uint32_t i; + + fprintf (stderr, "\tPROGRAM\n"); + + assert(size + 2 == sz); + + program++; + for (i = 1; i < sz; i += 3, program += 3) { + uint32_t opcode = program[0] & (0x1f << 24); + + fprintf (stderr, "\t\t"); + + if ((int) opcode >= A0_NOP && opcode <= A0_SLT) + print_arith_op(opcode >> 24, program); + else if (opcode >= T0_TEXLD && opcode <= T0_TEXKILL) + print_tex_op(opcode >> 24, program); + else if (opcode == D0_DCL) + print_dcl_op(opcode >> 24, program); + else + fprintf (stderr, "Unknown opcode 0x%x\n", opcode); + } + + fprintf (stderr, "\tEND-PROGRAM\n\n"); +} + +static cairo_bool_t +debug_program (struct debug_stream *stream, const char *name, uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + + if (len == 0) { + fprintf (stderr, "Error - zero length packet (0x%08x)\n", stream->ptr[0]); + ASSERT_NOT_REACHED; + return FALSE; + } + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + i915_disassemble_program (ptr, len); + + stream->offset += len * sizeof(uint32_t); + return TRUE; +} + +static cairo_bool_t +debug_chain (struct debug_stream *stream, const char *name, uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t old_offset = stream->offset + len * sizeof(uint32_t); + uint32_t i; + + fprintf (stderr, "%s (%d dwords):\n", name, len); + for (i = 0; i < len; i++) + fprintf (stderr, "\t0x%08x\n", ptr[i]); + + stream->offset = ptr[1] & ~0x3; + + if (stream->offset < old_offset) + fprintf (stderr, "\n... skipping backwards from 0x%x --> 0x%x ...\n\n", + old_offset, stream->offset ); + else + fprintf (stderr, "\n... skipping from 0x%x --> 0x%x ...\n\n", + old_offset, stream->offset ); + + return TRUE; +} + +static cairo_bool_t +debug_variable_length_prim (struct debug_stream *stream) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + const char *prim = get_prim_name( ptr[0] ); + uint32_t i, len; + + uint16_t *idx = (uint16_t *)(ptr+1); + for (i = 0; idx[i] != 0xffff; i++) + ; + + len = 1+(i+2)/2; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "3DPRIM, %s variable length %d indices (%d dwords):\n", prim, i, len); + for (i = 0; i < len; i++) + fprintf (stderr, "\t0x%08x\n", ptr[i]); + fprintf (stderr, "\n"); + + stream->offset += len * sizeof(uint32_t); + return TRUE; +} + +#define BITS(dw, hi, lo, ...) \ + do { \ + unsigned himask = 0xffffffffU >> (31 - (hi)); \ + fprintf (stderr, "\t\t "); \ + fprintf (stderr, __VA_ARGS__); \ + fprintf (stderr, ": 0x%x\n", ((dw) & himask) >> (lo)); \ + } while (0) + +#define MBZ(dw, hi, lo) do { \ + unsigned x = (dw) >> (lo); \ + unsigned lomask = (1 << (lo)) - 1; \ + unsigned himask; \ + himask = (1UL << (hi)) - 1; \ + assert ((x & himask & ~lomask) == 0); \ +} while (0) + +#define FLAG(dw, bit, ... ) \ + do { \ + if (((dw) >> (bit)) & 1) { \ + fprintf (stderr, "\t\t "); \ + fprintf (stderr, __VA_ARGS__); \ + fprintf (stderr, "\n"); \ + } \ + } while (0) + +static cairo_bool_t +debug_load_immediate (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t bits = (ptr[0] >> 4) & 0xff; + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords, flags: %x):\n", name, len, bits); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + if (bits & (1<<0)) { + fprintf (stderr, "\t LIS0: 0x%08x\n", ptr[j]); + fprintf (stderr, "\t vb address: 0x%08x\n", (ptr[j] & ~0x3)); + BITS (ptr[j], 0, 0, "vb invalidate disable"); + j++; + } + if (bits & (1<<1)) { + fprintf (stderr, "\t LIS1: 0x%08x\n", ptr[j]); + BITS (ptr[j], 29, 24, "vb dword width"); + BITS (ptr[j], 21, 16, "vb dword pitch"); + BITS (ptr[j], 15, 0, "vb max index"); + j++; + } + if (bits & (1<<2)) { + int i; + fprintf (stderr, "\t LIS2: 0x%08x\n", ptr[j]); + for (i = 0; i < 8; i++) { + unsigned tc = (ptr[j] >> (i * 4)) & 0xf; + if (tc != 0xf) + BITS (tc, 3, 0, "tex coord %d", i); + } + j++; + } + if (bits & (1<<3)) { + fprintf (stderr, "\t LIS3: 0x%08x\n", ptr[j]); + j++; + } + if (bits & (1<<4)) { + fprintf (stderr, "\t LIS4: 0x%08x\n", ptr[j]); + BITS (ptr[j], 31, 23, "point width"); + BITS (ptr[j], 22, 19, "line width"); + FLAG (ptr[j], 18, "alpha flatshade"); + FLAG (ptr[j], 17, "fog flatshade"); + FLAG (ptr[j], 16, "spec flatshade"); + FLAG (ptr[j], 15, "rgb flatshade"); + BITS (ptr[j], 14, 13, "cull mode"); + FLAG (ptr[j], 12, "vfmt: point width"); + FLAG (ptr[j], 11, "vfmt: specular/fog"); + FLAG (ptr[j], 10, "vfmt: rgba"); + FLAG (ptr[j], 9, "vfmt: depth offset"); + BITS (ptr[j], 8, 6, "vfmt: position (2==xyzw)"); + FLAG (ptr[j], 5, "force dflt diffuse"); + FLAG (ptr[j], 4, "force dflt specular"); + FLAG (ptr[j], 3, "local depth offset enable"); + FLAG (ptr[j], 2, "vfmt: fp32 fog coord"); + FLAG (ptr[j], 1, "sprite point"); + FLAG (ptr[j], 0, "antialiasing"); + j++; + } + if (bits & (1<<5)) { + fprintf (stderr, "\t LIS5: 0x%08x\n", ptr[j]); + BITS (ptr[j], 31, 28, "rgba write disables"); + FLAG (ptr[j], 27, "force dflt point width"); + FLAG (ptr[j], 26, "last pixel enable"); + FLAG (ptr[j], 25, "global z offset enable"); + FLAG (ptr[j], 24, "fog enable"); + BITS (ptr[j], 23, 16, "stencil ref"); + BITS (ptr[j], 15, 13, "stencil test"); + BITS (ptr[j], 12, 10, "stencil fail op"); + BITS (ptr[j], 9, 7, "stencil pass z fail op"); + BITS (ptr[j], 6, 4, "stencil pass z pass op"); + FLAG (ptr[j], 3, "stencil write enable"); + FLAG (ptr[j], 2, "stencil test enable"); + FLAG (ptr[j], 1, "color dither enable"); + FLAG (ptr[j], 0, "logiop enable"); + j++; + } + if (bits & (1<<6)) { + fprintf (stderr, "\t LIS6: 0x%08x\n", ptr[j]); + FLAG (ptr[j], 31, "alpha test enable"); + BITS (ptr[j], 30, 28, "alpha func"); + BITS (ptr[j], 27, 20, "alpha ref"); + FLAG (ptr[j], 19, "depth test enable"); + BITS (ptr[j], 18, 16, "depth func"); + FLAG (ptr[j], 15, "blend enable"); + BITS (ptr[j], 14, 12, "blend func"); + BITS (ptr[j], 11, 8, "blend src factor"); + BITS (ptr[j], 7, 4, "blend dst factor"); + FLAG (ptr[j], 3, "depth write enable"); + FLAG (ptr[j], 2, "color write enable"); + BITS (ptr[j], 1, 0, "provoking vertex"); + j++; + } + + fprintf (stderr, "\n"); + + assert(j == len); + + stream->offset += len * sizeof(uint32_t); + return TRUE; +} + +static cairo_bool_t +debug_load_indirect (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t bits = (ptr[0] >> 8) & 0x3f; + uint32_t i, j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + for (i = 0; i < 6; i++) { + if (bits & (1<offset += len * sizeof(uint32_t); + return TRUE; +} + +static void +BR13 (struct debug_stream *stream, + uint32_t val) +{ + fprintf (stderr, "\t0x%08x\n", val); + FLAG (val, 30, "clipping enable"); + BITS (val, 25, 24, "color depth (3==32bpp)"); + BITS (val, 23, 16, "raster op"); + BITS (val, 15, 0, "dest pitch"); +} + +static void +BR2223 (struct debug_stream *stream, + uint32_t val22, uint32_t val23) +{ + union { uint32_t val; short field[2]; } BR22, BR23; + + BR22.val = val22; + BR23.val = val23; + + fprintf (stderr, "\t0x%08x\n", val22); + BITS (val22, 31, 16, "dest y1"); + BITS (val22, 15, 0, "dest x1"); + + fprintf (stderr, "\t0x%08x\n", val23); + BITS (val23, 31, 16, "dest y2"); + BITS (val23, 15, 0, "dest x2"); + + /* The blit engine may produce unexpected results when these aren't met */ + assert(BR22.field[0] < BR23.field[0]); + assert(BR22.field[1] < BR23.field[1]); +} + +static void +BR09 (struct debug_stream *stream, + uint32_t val) +{ + fprintf (stderr, "\t0x%08x -- dest address\n", val); +} + +static void +BR26 (struct debug_stream *stream, + uint32_t val) +{ + fprintf (stderr, "\t0x%08x\n", val); + BITS (val, 31, 16, "src y1"); + BITS (val, 15, 0, "src x1"); +} + +static void +BR11 (struct debug_stream *stream, + uint32_t val) +{ + fprintf (stderr, "\t0x%08x\n", val); + BITS (val, 15, 0, "src pitch"); +} + +static void +BR12 (struct debug_stream *stream, + uint32_t val) +{ + fprintf (stderr, "\t0x%08x -- src address\n", val); +} + +static void +BR16 (struct debug_stream *stream, + uint32_t val) +{ + fprintf (stderr, "\t0x%08x -- color\n", val); +} + +static cairo_bool_t +debug_copy_blit (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + BR13(stream, ptr[j++]); + BR2223(stream, ptr[j], ptr[j+1]); + j += 2; + BR09(stream, ptr[j++]); + BR26(stream, ptr[j++]); + BR11(stream, ptr[j++]); + BR12(stream, ptr[j++]); + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t +debug_color_blit (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + BR13(stream, ptr[j++]); + BR2223(stream, ptr[j], ptr[j+1]); + j += 2; + BR09(stream, ptr[j++]); + BR16(stream, ptr[j++]); + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t +debug_modes4 (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j]); + BITS (ptr[j], 21, 18, "logicop func"); + FLAG (ptr[j], 17, "stencil test mask modify-enable"); + FLAG (ptr[j], 16, "stencil write mask modify-enable"); + BITS (ptr[j], 15, 8, "stencil test mask"); + BITS (ptr[j], 7, 0, "stencil write mask"); + fprintf (stderr, "\n"); + j++; + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t +debug_map_state (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + { + fprintf (stderr, "\t0x%08x\n", ptr[j]); + BITS (ptr[j], 15, 0, "map mask"); + j++; + } + + while (j < len) { + { + fprintf (stderr, "\t TMn.0: 0x%08x\n", ptr[j]); + fprintf (stderr, "\t map address: 0x%08x\n", (ptr[j] & ~0x3)); + FLAG (ptr[j], 1, "vertical line stride"); + FLAG (ptr[j], 0, "vertical line stride offset"); + j++; + } + + { + fprintf (stderr, "\t TMn.1: 0x%08x\n", ptr[j]); + BITS (ptr[j], 31, 21, "height"); + BITS (ptr[j], 20, 10, "width"); + BITS (ptr[j], 9, 7, "surface format"); + BITS (ptr[j], 6, 3, "texel format"); + FLAG (ptr[j], 2, "use fence regs"); + FLAG (ptr[j], 1, "tiled surface"); + FLAG (ptr[j], 0, "tile walk ymajor"); + j++; + } + { + fprintf (stderr, "\t TMn.2: 0x%08x\n", ptr[j]); + BITS (ptr[j], 31, 21, "dword pitch"); + BITS (ptr[j], 20, 15, "cube face enables"); + BITS (ptr[j], 14, 9, "max lod"); + FLAG (ptr[j], 8, "mip layout right"); + BITS (ptr[j], 7, 0, "depth"); + j++; + } + } + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t +debug_sampler_state (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + { + fprintf (stderr, "\t0x%08x\n", ptr[j]); + BITS (ptr[j], 15, 0, "sampler mask"); + j++; + } + + while (j < len) { + { + fprintf (stderr, "\t TSn.0: 0x%08x\n", ptr[j]); + FLAG (ptr[j], 31, "reverse gamma"); + FLAG (ptr[j], 30, "planar to packed"); + FLAG (ptr[j], 29, "yuv->rgb"); + BITS (ptr[j], 28, 27, "chromakey index"); + BITS (ptr[j], 26, 22, "base mip level"); + BITS (ptr[j], 21, 20, "mip mode filter"); + BITS (ptr[j], 19, 17, "mag mode filter"); + BITS (ptr[j], 16, 14, "min mode filter"); + BITS (ptr[j], 13, 5, "lod bias (s4.4)"); + FLAG (ptr[j], 4, "shadow enable"); + FLAG (ptr[j], 3, "max-aniso-4"); + BITS (ptr[j], 2, 0, "shadow func"); + j++; + } + + { + fprintf (stderr, "\t TSn.1: 0x%08x\n", ptr[j]); + BITS (ptr[j], 31, 24, "min lod"); + MBZ( ptr[j], 23, 18 ); + FLAG (ptr[j], 17, "kill pixel enable"); + FLAG (ptr[j], 16, "keyed tex filter mode"); + FLAG (ptr[j], 15, "chromakey enable"); + BITS (ptr[j], 14, 12, "tcx wrap mode"); + BITS (ptr[j], 11, 9, "tcy wrap mode"); + BITS (ptr[j], 8, 6, "tcz wrap mode"); + FLAG (ptr[j], 5, "normalized coords"); + BITS (ptr[j], 4, 1, "map (surface) index"); + FLAG (ptr[j], 0, "EAST deinterlacer enable"); + j++; + } + { + fprintf (stderr, "\t TSn.2: 0x%08x (default color)\n", ptr[j]); + j++; + } + } + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t +debug_dest_vars (struct debug_stream *stream, + const char *name, + uint32_t len) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + { + fprintf (stderr, "\t0x%08x\n", ptr[j]); + FLAG (ptr[j], 31, "early classic ztest"); + FLAG (ptr[j], 30, "opengl tex default color"); + FLAG (ptr[j], 29, "bypass iz"); + FLAG (ptr[j], 28, "lod preclamp"); + BITS (ptr[j], 27, 26, "dither pattern"); + FLAG (ptr[j], 25, "linear gamma blend"); + FLAG (ptr[j], 24, "debug dither"); + BITS (ptr[j], 23, 20, "dstorg x"); + BITS (ptr[j], 19, 16, "dstorg y"); + MBZ (ptr[j], 15, 15 ); + BITS (ptr[j], 14, 12, "422 write select"); + BITS (ptr[j], 11, 8, "cbuf format"); + BITS (ptr[j], 3, 2, "zbuf format"); + FLAG (ptr[j], 1, "vert line stride"); + FLAG (ptr[j], 1, "vert line stride offset"); + j++; + } + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t debug_buf_info( struct debug_stream *stream, + const char *name, + uint32_t len ) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t j = 0; + + fprintf (stderr, "%04x: ", stream->offset); + fprintf (stderr, "%s (%d dwords):\n", name, len); + fprintf (stderr, "\t0x%08x\n", ptr[j++]); + + { + fprintf (stderr, "\t0x%08x\n", ptr[j]); + BITS (ptr[j], 28, 28, "aux buffer id"); + BITS (ptr[j], 27, 24, "buffer id (7=depth, 3=back)"); + FLAG (ptr[j], 23, "use fence regs"); + FLAG (ptr[j], 22, "tiled surface"); + FLAG (ptr[j], 21, "tile walk ymajor"); + MBZ (ptr[j], 20, 14); + BITS (ptr[j], 13, 2, "dword pitch"); + MBZ (ptr[j], 2, 0); + j++; + } + + fprintf (stderr, "\t0x%08x -- buffer base address\n", ptr[j++]); + + stream->offset += len * sizeof(uint32_t); + assert(j == len); + return TRUE; +} + +static cairo_bool_t +decode_3d_i915 (struct debug_stream *stream) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t cmd = *ptr; + + switch ((cmd >> 24) & 0x1f) { + case 0x6: + return debug (stream, "3DSTATE_ANTI_ALIASING", 1); + case 0x7: + return debug (stream, "3DSTATE_RASTERIZATION_RULES", 1); + case 0x8: + return debug (stream, "3DSTATE_BACKFACE_STENCIL_OPS", 1); + case 0x9: + return debug (stream, "3DSTATE_BACKFACE_STENCIL_MASKS", 1); + case 0xb: + return debug (stream, "3DSTATE_INDEPENDENT_ALPHA_BLEND", 1); + case 0xc: + return debug (stream, "3DSTATE_MODES5", 1); + case 0xd: + return debug_modes4(stream, "3DSTATE_MODES4", 1); + case 0x15: + return debug (stream, "3DSTATE_FOG_COLOR", 1); + case 0x16: + return debug (stream, "3DSTATE_COORD_SET_BINDINGS", 1); + case 0x1c: + /* 3DState16NP */ + switch((cmd >> 19) & 0x1f) { + case 0x10: + return debug (stream, "3DSTATE_SCISSOR_ENABLE", 1); + case 0x11: + return debug (stream, "3DSTATE_DEPTH_SUBRECTANGLE_DISABLE", 1); + default: + break; + } + break; + case 0x1d: + /* 3DStateMW */ + switch ((cmd >> 16) & 0xff) { + case 0x0: + return debug_map_state(stream, "3DSTATE_MAP_STATE", (cmd & 0x1f) + 2); + case 0x1: + return debug_sampler_state(stream, "3DSTATE_SAMPLER_STATE", (cmd & 0x1f) + 2); + case 0x4: + return debug_load_immediate(stream, "3DSTATE_LOAD_STATE_IMMEDIATE", (cmd & 0xf) + 2); + case 0x5: + return debug_program(stream, "3DSTATE_PIXEL_SHADER_PROGRAM", (cmd & 0x1ff) + 2); + case 0x6: + return debug (stream, "3DSTATE_PIXEL_SHADER_CONSTANTS", (cmd & 0xff) + 2); + case 0x7: + return debug_load_indirect(stream, "3DSTATE_LOAD_INDIRECT", (cmd & 0xff) + 2); + case 0x80: + return debug (stream, "3DSTATE_DRAWING_RECTANGLE", (cmd & 0xffff) + 2); + case 0x81: + return debug (stream, "3DSTATE_SCISSOR_RECTANGLE", (cmd & 0xffff) + 2); + case 0x83: + return debug (stream, "3DSTATE_SPAN_STIPPLE", (cmd & 0xffff) + 2); + case 0x85: + return debug_dest_vars(stream, "3DSTATE_DEST_BUFFER_VARS", (cmd & 0xffff) + 2); + case 0x88: + return debug (stream, "3DSTATE_CONSTANT_BLEND_COLOR", (cmd & 0xffff) + 2); + case 0x89: + return debug (stream, "3DSTATE_FOG_MODE", (cmd & 0xffff) + 2); + case 0x8e: + return debug_buf_info(stream, "3DSTATE_BUFFER_INFO", (cmd & 0xffff) + 2); + case 0x97: + return debug (stream, "3DSTATE_DEPTH_OFFSET_SCALE", (cmd & 0xffff) + 2); + case 0x98: + return debug (stream, "3DSTATE_DEFAULT_Z", (cmd & 0xffff) + 2); + case 0x99: + return debug (stream, "3DSTATE_DEFAULT_DIFFUSE", (cmd & 0xffff) + 2); + case 0x9a: + return debug (stream, "3DSTATE_DEFAULT_SPECULAR", (cmd & 0xffff) + 2); + case 0x9c: + return debug (stream, "3DSTATE_CLEAR_PARAMETERS", (cmd & 0xffff) + 2); + default: + ASSERT_NOT_REACHED; + return 0; + } + break; + case 0x1e: + if (cmd & (1 << 23)) + return debug (stream, "???", (cmd & 0xffff) + 1); + else + return debug (stream, "", 1); + break; + case 0x1f: + if ((cmd & (1 << 23)) == 0) { + return debug_prim (stream, "3DPRIM (inline)", 1, (cmd & 0x1ffff) + 2); + } else if (cmd & (1 << 17)) { + if ((cmd & 0xffff) == 0) + return debug_variable_length_prim (stream); + else + return debug_prim (stream, "3DPRIM (indexed)", 0, (((cmd & 0xffff) + 1) / 2) + 1); + } else + return debug_prim (stream, "3DPRIM (indirect sequential)", 0, 2); + break; + default: + return debug (stream, "", 0); + } + + return FALSE; +} + +static cairo_bool_t +decode_3d_i965 (struct debug_stream *stream) +{ + const uint32_t *data = (uint32_t *) (stream->ptr + stream->offset); + const uint32_t opcode = (data[0] & 0xffff0000) >> 16; + unsigned int idx; + const struct { + uint32_t opcode; + int min_len; + int max_len; + const char *name; + } opcodes_3d[] = { + { 0x6000, 3, 3, "URB_FENCE" }, + { 0x6001, 2, 2, "CS_URB_STATE" }, + { 0x6002, 2, 2, "CONSTANT_BUFFER" }, + { 0x6101, 6, 6, "STATE_BASE_ADDRESS" }, + { 0x6102, 2, 2 , "STATE_SIP" }, + { 0x6104, 1, 1, "3DSTATE_PIPELINE_SELECT" }, + { 0x680b, 1, 1, "3DSTATE_VF_STATISTICS" }, + { 0x6904, 1, 1, "3DSTATE_PIPELINE_SELECT" }, + { 0x7800, 7, 7, "3DSTATE_PIPELINED_POINTERS" }, + { 0x7801, 6, 6, "3DSTATE_BINDING_TABLE_POINTERS" }, + { 0x780b, 1, 1, "3DSTATE_VF_STATISTICS" }, + { 0x7808, 5, 257, "3DSTATE_VERTEX_BUFFERS" }, + { 0x7809, 3, 256, "3DSTATE_VERTEX_ELEMENTS" }, + { 0x780a, 3, 3, "3DSTATE_INDEX_BUFFER" }, + { 0x7900, 4, 4, "3DSTATE_DRAWING_RECTANGLE" }, + { 0x7901, 5, 5, "3DSTATE_CONSTANT_COLOR" }, + { 0x7905, 5, 7, "3DSTATE_DEPTH_BUFFER" }, + { 0x7906, 2, 2, "3DSTATE_POLY_STIPPLE_OFFSET" }, + { 0x7907, 33, 33, "3DSTATE_POLY_STIPPLE_PATTERN" }, + { 0x7908, 3, 3, "3DSTATE_LINE_STIPPLE" }, + { 0x7909, 2, 2, "3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP" }, + { 0x790a, 3, 3, "3DSTATE_AA_LINE_PARAMETERS" }, + { 0x7b00, 6, 6, "3DPRIMITIVE" }, + }, *opcode_3d; + + for (idx = 0; idx < ARRAY_LENGTH (opcodes_3d); idx++) { + opcode_3d = &opcodes_3d[idx]; + if (opcode == opcode_3d->opcode) { + unsigned int len = 1; + if (opcode_3d->max_len != 1) + len = (data[0] & 0x000000ff) + 2; + return debug (stream, opcode_3d->name, len); + } + } + + return FALSE; +} + +static cairo_bool_t +decode_3d_i830 (struct debug_stream *stream) +{ + ASSERT_NOT_REACHED; + return FALSE; +} + +static cairo_bool_t +i915_debug_packet (struct debug_stream *stream, + int devid) +{ + uint32_t *ptr = (uint32_t *)(stream->ptr + stream->offset); + uint32_t cmd = *ptr; + + switch (((cmd >> 29) & 0x7)) { + case 0x0: + switch ((cmd >> 23) & 0x3f) { + case 0x0: + return debug (stream, "MI_NOOP", 1); + case 0x3: + return debug (stream, "MI_WAIT_FOR_EVENT", 1); + case 0x4: + return debug (stream, "MI_FLUSH", 1); + case 0xA: + debug (stream, "MI_BATCH_BUFFER_END", 1); + return FALSE; + case 0x22: + return debug (stream, "MI_LOAD_REGISTER_IMM", 3); + case 0x31: + return debug_chain(stream, "MI_BATCH_BUFFER_START", 2); + default: + break; + } + break; + case 0x1: + break; + case 0x2: + switch ((cmd >> 22) & 0xff) { + case 0x50: + return debug_color_blit(stream, "XY_COLOR_BLT", (cmd & 0xff) + 2); + case 0x53: + return debug_copy_blit(stream, "XY_SRC_COPY_BLT", (cmd & 0xff) + 2); + default: + return debug (stream, "blit command", (cmd & 0xff) + 2); + } + break; + case 0x3: + if (IS_965(devid)) + return decode_3d_i965 (stream); + else if (IS_9XX(devid)) + return decode_3d_i915 (stream); + else + return decode_3d_i830 (stream); + default: + break; + } + + fprintf (stderr, "Bogus cmd: %x [%x]\n", (cmd >> 29) & 7, cmd); + ASSERT_NOT_REACHED; + return 0; +} + +void +intel_dump_batchbuffer (const void *batch, + uint32_t length, + int devid) +{ + struct debug_stream stream; + cairo_bool_t done = FALSE; + + fprintf (stderr, "\nBATCH: (%d dwords)\n", length / 4); + + stream.offset = 0; + stream.ptr = batch; + + while (! done && stream.offset < length) { + if (! i915_debug_packet (&stream, devid)) + break; + + assert (stream.offset <= length); + } + + fprintf (stderr, "END-BATCH\n\n"); + fflush (stderr); +} diff --git a/gfx/cairo/cairo/src/cairo-glitz-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-ioctl-private.h similarity index 69% rename from gfx/cairo/cairo/src/cairo-glitz-private.h rename to gfx/cairo/cairo/src/drm/cairo-drm-intel-ioctl-private.h index 8a876eeab858..4a766d7e1303 100644 --- a/gfx/cairo/cairo/src/cairo-glitz-private.h +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-ioctl-private.h @@ -1,6 +1,6 @@ /* Cairo - a vector graphics library with display and print output * - * Copyright © 2005 Red Hat, Inc. + * Copyright © 2009 Chris Wilson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -12,7 +12,7 @@ * * 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 + * 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 * @@ -25,17 +25,21 @@ * 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. */ -#ifndef CAIRO_GLITZ_PRIVATE_H -#define CAIRO_GLITZ_PRIVATE_H +#ifndef CAIRO_DRM_INTEL_IOCTL_PRIVATE_H +#define CAIRO_DRM_INTEL_IOCTL_PRIVATE_H -#include "cairoint.h" -#include "cairo-glitz.h" +#include "cairo-drm-intel-command-private.h" -slim_hidden_proto (cairo_glitz_surface_create); +#include -#endif /* CAIRO_GLITZ_PRIVATE_H */ +struct drm_i915_gem_real_size { + uint32_t handle; + uint64_t size; +}; + +#define DRM_I915_GEM_REAL_SIZE 0x2a +#define DRM_IOCTL_I915_GEM_REAL_SIZE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_REAL_SIZE, struct drm_i915_gem_real_size) + +#endif /* CAIRO_DRM_INTEL_IOCTL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-private.h new file mode 100644 index 000000000000..0cfded1bd8e8 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-private.h @@ -0,0 +1,515 @@ +/* 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. + * + */ + +#ifndef CAIRO_DRM_INTEL_PRIVATE_H +#define CAIRO_DRM_INTEL_PRIVATE_H + +#include "cairoint.h" +#include "cairo-cache-private.h" +#include "cairo-compiler-private.h" +#include "cairo-drm-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" +#include "cairo-mutex-private.h" +#include "cairo-rtree-private.h" +#include "cairo-types-private.h" +#include "cairo-pattern-private.h" + +#include "cairo-drm-intel-ioctl-private.h" + +#define INTEL_TILING_DEFAULT I915_TILING_Y + +#define INTEL_BO_CACHE_BUCKETS 12 /* cache surfaces up to 16 MiB */ + +#define INTEL_GLYPH_CACHE_WIDTH 1024 +#define INTEL_GLYPH_CACHE_HEIGHT 1024 +#define INTEL_GLYPH_CACHE_MIN_SIZE 1 +#define INTEL_GLYPH_CACHE_MAX_SIZE 128 + +typedef struct _intel_bo { + cairo_drm_bo_t base; + + cairo_list_t link; + cairo_list_t cache_list; + + uint32_t offset; + uint32_t batch_read_domains; + uint32_t batch_write_domain; + + uint32_t opaque0; + uint32_t opaque1; + + uint32_t full_size; + uint16_t stride; + uint16_t _stride; + uint32_t tiling :4; + uint32_t _tiling :4; + uint32_t purgeable :1; + uint32_t busy :1; + uint32_t cpu :1; + + struct drm_i915_gem_exec_object2 *exec; + void *virtual; +} intel_bo_t; + +#define INTEL_BATCH_SIZE (64*1024) +#define INTEL_VERTEX_BUFFER_SIZE (512*1024) +#define INTEL_MAX_RELOCS 2048 + +static inline void +intel_bo_mark_purgeable (intel_bo_t *bo) +{ + if (bo->base.name == 0) + bo->purgeable = 1; +} + +typedef struct _intel_vertex_buffer intel_vertex_buffer_t; + +typedef void (*intel_vertex_buffer_new_func_t) (intel_vertex_buffer_t *vertex_buffer); +typedef void (*intel_vertex_buffer_start_rectangles_func_t) (intel_vertex_buffer_t *vertex_buffer, + uint32_t floats_per_vertex); +typedef void (*intel_vertex_buffer_flush_func_t) (intel_vertex_buffer_t *vertex_buffer); +typedef void (*intel_vertex_buffer_finish_func_t) (intel_vertex_buffer_t *vertex_buffer); + +struct _intel_vertex_buffer { + uint32_t vbo_batch; /* reloc position in batch, 0 -> not yet allocated */ + uint32_t vbo_offset; + uint32_t vbo_used; + + uint32_t vertex_index; + uint32_t vertex_count; + + uint32_t floats_per_vertex; + uint32_t rectangle_size; + + intel_bo_t *last_vbo; + uint32_t last_vbo_offset; + uint32_t last_vbo_space; + + intel_vertex_buffer_new_func_t new; + intel_vertex_buffer_start_rectangles_func_t start_rectangles; + intel_vertex_buffer_flush_func_t flush; + intel_vertex_buffer_finish_func_t finish; + + uint32_t base[INTEL_VERTEX_BUFFER_SIZE / sizeof (uint32_t)]; +}; + +typedef struct _intel_batch intel_batch_t; + +typedef void (*intel_batch_commit_func_t) (intel_batch_t *batch); +typedef void (*intel_batch_reset_func_t) (intel_batch_t *batch); + +struct _intel_batch { + size_t gtt_size; + size_t gtt_avail_size; + + intel_batch_commit_func_t commit; + intel_batch_reset_func_t reset; + + uint16_t exec_count; + uint16_t reloc_count; + uint16_t used; + uint16_t header; + + intel_bo_t *target_bo[INTEL_MAX_RELOCS]; + struct drm_i915_gem_exec_object2 exec[INTEL_MAX_RELOCS]; + struct drm_i915_gem_relocation_entry reloc[INTEL_MAX_RELOCS]; + + uint32_t base[INTEL_BATCH_SIZE / sizeof (uint32_t)]; + + intel_vertex_buffer_t vertex_buffer; +}; + +typedef struct _intel_buffer { + intel_bo_t *bo; + uint32_t offset; + cairo_format_t format; + uint32_t map0, map1; + uint32_t width; + uint32_t height; + uint32_t stride; +} intel_buffer_t; + +typedef struct _intel_buffer_cache { + int ref_count; + intel_buffer_t buffer; + cairo_rtree_t rtree; + cairo_list_t link; +} intel_buffer_cache_t; + +typedef struct _intel_glyph { + cairo_rtree_node_t node; + intel_buffer_cache_t *cache; + void **owner; + float texcoord[3]; + int width, height; +} intel_glyph_t; + +typedef struct _intel_gradient_cache { + cairo_pattern_union_t pattern; + intel_buffer_t buffer; +} intel_gradient_cache_t; +#define GRADIENT_CACHE_SIZE 16 + +typedef struct _intel_surface { + cairo_drm_surface_t drm; + + cairo_cache_entry_t snapshot_cache_entry; +} intel_surface_t; + +typedef void (*intel_reset_context_func_t) (void *device); + +typedef struct _intel_device { + cairo_drm_device_t base; + + size_t gtt_max_size; + size_t gtt_avail_size; + + cairo_freepool_t bo_pool; + cairo_list_t bo_in_flight; + + cairo_mutex_t mutex; + intel_batch_t batch; + + intel_buffer_cache_t glyph_cache[2]; + cairo_list_t fonts; + + struct { + intel_gradient_cache_t cache[GRADIENT_CACHE_SIZE]; + unsigned int size; + } gradient_cache; + + cairo_cache_t snapshot_cache; + size_t snapshot_cache_max_size; + + intel_reset_context_func_t reset_context; + + cairo_status_t (*flush) (struct _intel_device *); +} intel_device_t; + +static inline intel_device_t * +to_intel_device (cairo_device_t *base) +{ + return (intel_device_t *) base; +} + +static inline intel_bo_t * +to_intel_bo (cairo_drm_bo_t *base) +{ + return (intel_bo_t *) base; +} + +static inline intel_bo_t * +intel_bo_reference (intel_bo_t *bo) +{ + return to_intel_bo (cairo_drm_bo_reference (&bo->base)); +} + +cairo_private cairo_bool_t +intel_bo_madvise (intel_device_t *device, intel_bo_t *bo, int madv); + +static cairo_always_inline void +intel_bo_destroy (intel_device_t *device, intel_bo_t *bo) +{ + cairo_drm_bo_destroy (&device->base.base, &bo->base); +} + +static inline void +intel_bo_in_flight_add (intel_device_t *device, + intel_bo_t *bo) +{ + if (bo->base.name == 0 && bo->exec != NULL && cairo_list_is_empty (&bo->cache_list)) + cairo_list_add (&bo->cache_list, &device->bo_in_flight); +} + +cairo_private int +intel_get (int fd, int param); + +cairo_private cairo_bool_t +intel_info (int fd, uint64_t *gtt_size); + +cairo_private cairo_status_t +intel_device_init (intel_device_t *device, int fd); + +cairo_private void +intel_device_fini (intel_device_t *dev); + +cairo_private intel_bo_t * +intel_bo_create (intel_device_t *dev, + uint32_t max_size, + uint32_t real_size, + cairo_bool_t gpu_target, + uint32_t tiling, + uint32_t stride); + +cairo_private intel_bo_t * +intel_bo_create_for_name (intel_device_t *dev, uint32_t name); + +cairo_private void +intel_bo_set_tiling (const intel_device_t *dev, + intel_bo_t *bo); + +cairo_private cairo_bool_t +intel_bo_is_inactive (const intel_device_t *device, + intel_bo_t *bo); + +cairo_private cairo_bool_t +intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo); + +cairo_private void +intel_bo_write (const intel_device_t *dev, + intel_bo_t *bo, + unsigned long offset, + unsigned long size, + const void *data); + +cairo_private void +intel_bo_read (const intel_device_t *dev, + intel_bo_t *bo, + unsigned long offset, + unsigned long size, + void *data); + +cairo_private void * +intel_bo_map (const intel_device_t *dev, intel_bo_t *bo); + +cairo_private void +intel_bo_unmap (intel_bo_t *bo); + +cairo_private cairo_status_t +intel_bo_init (const intel_device_t *dev, + intel_bo_t *bo, + uint32_t size, + uint32_t initial_domain); + +cairo_private cairo_status_t +intel_bo_init_for_name (const intel_device_t *dev, + intel_bo_t *bo, + uint32_t size, + uint32_t name); + +cairo_private cairo_status_t +intel_bo_put_image (intel_device_t *dev, + intel_bo_t *bo, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y); + +cairo_private void +intel_surface_init (intel_surface_t *surface, + const cairo_surface_backend_t *backend, + cairo_drm_device_t *device, + cairo_format_t format, + int width, int height); + +cairo_private cairo_status_t +intel_buffer_cache_init (intel_buffer_cache_t *cache, + intel_device_t *device, + cairo_format_t format, + int width, int height); + +cairo_private cairo_status_t +intel_gradient_render (intel_device_t *device, + const cairo_gradient_pattern_t *pattern, + intel_buffer_t *buffer); + +cairo_private cairo_int_status_t +intel_get_glyph (intel_device_t *device, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +cairo_private void +intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font); + +cairo_private void +intel_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private void +intel_glyph_cache_unpin (intel_device_t *device); + +static inline intel_glyph_t * +intel_glyph_pin (intel_glyph_t *glyph) +{ + cairo_rtree_node_t *node = &glyph->node; + if (unlikely (node->pinned == 0)) + return _cairo_rtree_pin (&glyph->cache->rtree, node); + return glyph; +} + +cairo_private cairo_status_t +intel_snapshot_cache_insert (intel_device_t *device, + intel_surface_t *surface); + +cairo_private void +intel_surface_detach_snapshot (cairo_surface_t *abstract_surface); + +cairo_private void +intel_snapshot_cache_thaw (intel_device_t *device); + +cairo_private void +intel_throttle (intel_device_t *device); + +cairo_private cairo_status_t +intel_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +intel_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra); +cairo_private cairo_surface_t * +intel_surface_map_to_image (void *abstract_surface); + +cairo_private cairo_status_t +intel_surface_flush (void *abstract_surface, + unsigned flags); + +cairo_private cairo_status_t +intel_surface_finish (void *abstract_surface); + +cairo_private void +intel_dump_batchbuffer (const void *batch, + uint32_t length, + int devid); + +static inline uint32_t cairo_const +MS3_tiling (uint32_t tiling) +{ + switch (tiling) { + default: + case I915_TILING_NONE: return 0; + case I915_TILING_X: return MS3_TILED_SURFACE; + case I915_TILING_Y: return MS3_TILED_SURFACE | MS3_TILE_WALK; + } +} + +static inline float cairo_const +texcoord_2d_16 (double x, double y) +{ + union { + uint32_t ui; + float f; + } u; + u.ui = (_cairo_half_from_float (y) << 16) | _cairo_half_from_float (x); + return u.f; +} + +#define PCI_CHIP_I810 0x7121 +#define PCI_CHIP_I810_DC100 0x7123 +#define PCI_CHIP_I810_E 0x7125 +#define PCI_CHIP_I815 0x1132 + +#define PCI_CHIP_I830_M 0x3577 +#define PCI_CHIP_845_G 0x2562 +#define PCI_CHIP_I855_GM 0x3582 +#define PCI_CHIP_I865_G 0x2572 + +#define PCI_CHIP_I915_G 0x2582 +#define PCI_CHIP_E7221_G 0x258A +#define PCI_CHIP_I915_GM 0x2592 +#define PCI_CHIP_I945_G 0x2772 +#define PCI_CHIP_I945_GM 0x27A2 +#define PCI_CHIP_I945_GME 0x27AE + +#define PCI_CHIP_Q35_G 0x29B2 +#define PCI_CHIP_G33_G 0x29C2 +#define PCI_CHIP_Q33_G 0x29D2 + +#define PCI_CHIP_IGD_GM 0xA011 +#define PCI_CHIP_IGD_G 0xA001 + +#define IS_IGDGM(devid) (devid == PCI_CHIP_IGD_GM) +#define IS_IGDG(devid) (devid == PCI_CHIP_IGD_G) +#define IS_IGD(devid) (IS_IGDG(devid) || IS_IGDGM(devid)) + +#define PCI_CHIP_I965_G 0x29A2 +#define PCI_CHIP_I965_Q 0x2992 +#define PCI_CHIP_I965_G_1 0x2982 +#define PCI_CHIP_I946_GZ 0x2972 +#define PCI_CHIP_I965_GM 0x2A02 +#define PCI_CHIP_I965_GME 0x2A12 + +#define PCI_CHIP_GM45_GM 0x2A42 + +#define PCI_CHIP_IGD_E_G 0x2E02 +#define PCI_CHIP_Q45_G 0x2E12 +#define PCI_CHIP_G45_G 0x2E22 +#define PCI_CHIP_G41_G 0x2E32 + +#define PCI_CHIP_ILD_G 0x0042 +#define PCI_CHIP_ILM_G 0x0046 + +#define IS_MOBILE(devid) (devid == PCI_CHIP_I855_GM || \ + devid == PCI_CHIP_I915_GM || \ + devid == PCI_CHIP_I945_GM || \ + devid == PCI_CHIP_I945_GME || \ + devid == PCI_CHIP_I965_GM || \ + devid == PCI_CHIP_I965_GME || \ + devid == PCI_CHIP_GM45_GM || IS_IGD(devid)) + +#define IS_G45(devid) (devid == PCI_CHIP_IGD_E_G || \ + devid == PCI_CHIP_Q45_G || \ + devid == PCI_CHIP_G45_G || \ + devid == PCI_CHIP_G41_G) +#define IS_GM45(devid) (devid == PCI_CHIP_GM45_GM) +#define IS_G4X(devid) (IS_G45(devid) || IS_GM45(devid)) + +#define IS_ILD(devid) (devid == PCI_CHIP_ILD_G) +#define IS_ILM(devid) (devid == PCI_CHIP_ILM_G) +#define IS_IRONLAKE(devid) (IS_ILD(devid) || IS_ILM(devid)) + +#define IS_915(devid) (devid == PCI_CHIP_I915_G || \ + devid == PCI_CHIP_E7221_G || \ + devid == PCI_CHIP_I915_GM) + +#define IS_945(devid) (devid == PCI_CHIP_I945_G || \ + devid == PCI_CHIP_I945_GM || \ + devid == PCI_CHIP_I945_GME || \ + devid == PCI_CHIP_G33_G || \ + devid == PCI_CHIP_Q33_G || \ + devid == PCI_CHIP_Q35_G || IS_IGD(devid)) + +#define IS_965(devid) (devid == PCI_CHIP_I965_G || \ + devid == PCI_CHIP_I965_Q || \ + devid == PCI_CHIP_I965_G_1 || \ + devid == PCI_CHIP_I965_GM || \ + devid == PCI_CHIP_I965_GME || \ + devid == PCI_CHIP_I946_GZ || \ + IS_G4X(devid) || \ + IS_IRONLAKE(devid)) + +#define IS_9XX(devid) (IS_915(devid) || \ + IS_945(devid) || \ + IS_965(devid)) + + +#endif /* CAIRO_DRM_INTEL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel-surface.c b/gfx/cairo/cairo/src/drm/cairo-drm-intel-surface.c new file mode 100644 index 000000000000..8fe2c3ae3b6e --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-surface.c @@ -0,0 +1,454 @@ +/* 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. + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-intel-private.h" + +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" + +/* Basic generic/stub surface for intel chipsets */ + +#define MAX_SIZE 2048 + +static cairo_surface_t * +intel_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); +} + +cairo_status_t +intel_surface_finish (void *abstract_surface) +{ + intel_surface_t *surface = abstract_surface; + + intel_bo_in_flight_add (to_intel_device (surface->drm.base.device), + to_intel_bo (surface->drm.bo)); + return _cairo_drm_surface_finish (&surface->drm); +} + +static void +surface_finish_and_destroy (cairo_surface_t *surface) +{ + cairo_surface_finish (surface); + cairo_surface_destroy (surface); +} + +cairo_status_t +intel_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + intel_surface_t *surface = abstract_surface; + cairo_surface_t *image; + cairo_status_t status; + void *ptr; + + if (surface->drm.fallback != NULL) { + image = surface->drm.fallback; + goto DONE; + } + + image = _cairo_surface_has_snapshot (&surface->drm.base, + &_cairo_image_surface_backend); + if (image != NULL) + goto DONE; + + if (surface->drm.base.backend->flush != NULL) { + status = surface->drm.base.backend->flush (surface); + if (unlikely (status)) + return status; + } + + ptr = intel_bo_map (to_intel_device (surface->drm.base.device), + to_intel_bo (surface->drm.bo)); + if (unlikely (ptr == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + image = cairo_image_surface_create_for_data (ptr, + surface->drm.format, + surface->drm.width, + surface->drm.height, + surface->drm.stride); + if (unlikely (image->status)) + return image->status; + + _cairo_surface_attach_snapshot (&surface->drm.base, image, surface_finish_and_destroy); + +DONE: + *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +void +intel_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +cairo_surface_t * +intel_surface_map_to_image (void *abstract_surface) +{ + intel_surface_t *surface = abstract_surface; + + if (surface->drm.fallback == NULL) { + cairo_surface_t *image; + cairo_status_t status; + void *ptr; + + if (surface->drm.base.backend->flush != NULL) { + status = surface->drm.base.backend->flush (surface); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + } + + ptr = intel_bo_map (to_intel_device (surface->drm.base.device), + to_intel_bo (surface->drm.bo)); + if (unlikely (ptr == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + image = cairo_image_surface_create_for_data (ptr, + surface->drm.format, + surface->drm.width, + surface->drm.height, + surface->drm.stride); + if (unlikely (image->status)) + return image; + + surface->drm.fallback = image; + } + + return surface->drm.fallback; +} + +cairo_status_t +intel_surface_flush (void *abstract_surface, unsigned flags) +{ + intel_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->drm.fallback == NULL) + return CAIRO_STATUS_SUCCESS; + + /* kill any outstanding maps */ + cairo_surface_finish (surface->drm.fallback); + + status = cairo_surface_status (surface->drm.fallback); + cairo_surface_destroy (surface->drm.fallback); + surface->drm.fallback = NULL; + + return status; +} + +static cairo_int_status_t +intel_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + return _cairo_surface_paint (intel_surface_map_to_image (abstract_surface), + op, source, clip); +} + +static cairo_int_status_t +intel_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + return _cairo_surface_mask (intel_surface_map_to_image (abstract_surface), + op, source, mask, clip); +} + +static cairo_int_status_t +intel_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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, + cairo_clip_t *clip) +{ + return _cairo_surface_stroke (intel_surface_map_to_image (abstract_surface), + op, source, path, stroke_style, ctm, ctm_inverse, + tolerance, antialias, clip); +} + +static cairo_int_status_t +intel_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + return _cairo_surface_fill (intel_surface_map_to_image (abstract_surface), + op, source, path, fill_rule, + tolerance, antialias, clip); +} + +static cairo_int_status_t +intel_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, + cairo_clip_t *clip, + int *num_remaining) +{ + *num_remaining = 0; + return _cairo_surface_show_text_glyphs (intel_surface_map_to_image (abstract_surface), + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, clip); +} + +static const cairo_surface_backend_t intel_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + intel_surface_create_similar, + intel_surface_finish, + + NULL, + intel_surface_acquire_source_image, + intel_surface_release_source_image, + + NULL, NULL, NULL, + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_drm_surface_get_font_options, + + intel_surface_flush, + NULL, /* mark dirty */ + NULL, NULL, /* font/glyph fini */ + + intel_surface_paint, + intel_surface_mask, + intel_surface_stroke, + intel_surface_fill, + intel_surface_glyphs, +}; + +void +intel_surface_init (intel_surface_t *surface, + const cairo_surface_backend_t *backend, + cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + _cairo_surface_init (&surface->drm.base, + backend, + &device->base, + _cairo_content_from_format (format), + FALSE); + + _cairo_drm_surface_init (&surface->drm, format, width, height); + + surface->snapshot_cache_entry.hash = 0; +} + +static cairo_surface_t * +intel_surface_create (cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + intel_surface_t *surface; + cairo_status_t status; + + surface = _cairo_malloc (sizeof (intel_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + intel_surface_init (surface, &intel_surface_backend, device, + format, width, height); + + if (width && height) { + /* Vol I, p134: size restrictions for textures */ + width = (width + 3) & -4; + height = (height + 1) & -2; + surface->drm.stride = + cairo_format_stride_for_width (surface->drm.format, width); + surface->drm.bo = &intel_bo_create (to_intel_device (&device->base), + surface->drm.stride * height, + surface->drm.stride * height, + TRUE, I915_TILING_NONE, surface->drm.stride)->base; + if (surface->drm.bo == NULL) { + status = _cairo_drm_surface_finish (&surface->drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + return &surface->drm.base; +} + +static cairo_surface_t * +intel_surface_create_for_name (cairo_drm_device_t *device, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + intel_surface_t *surface; + cairo_status_t status; + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + } + + if (stride < cairo_format_stride_for_width (format, width)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + + surface = _cairo_malloc (sizeof (intel_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + intel_surface_init (surface, &intel_surface_backend, + device, format, width, height); + + if (width && height) { + surface->drm.stride = stride; + + surface->drm.bo = &intel_bo_create_for_name (to_intel_device (&device->base), + name)->base; + if (unlikely (surface->drm.bo == NULL)) { + status = _cairo_drm_surface_finish (&surface->drm); + free (surface); + return _cairo_surface_create_in_error (_cairo_error + (CAIRO_STATUS_NO_MEMORY)); + } + } + + return &surface->drm.base; +} + +static cairo_status_t +intel_surface_enable_scan_out (void *abstract_surface) +{ + intel_surface_t *surface = abstract_surface; + + if (unlikely (surface->drm.bo == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + + to_intel_bo (surface->drm.bo)->tiling = I915_TILING_X; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +intel_device_throttle (cairo_drm_device_t *device) +{ + intel_throttle (to_intel_device (&device->base)); + return CAIRO_STATUS_SUCCESS; +} + +static void +intel_device_destroy (void *data) +{ + intel_device_t *device = data; + + intel_device_fini (device); + + free (data); +} + +cairo_drm_device_t * +_cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id) +{ + intel_device_t *device; + cairo_status_t status; + + if (! intel_info (fd, NULL)) + return NULL; + + device = _cairo_malloc (sizeof (intel_device_t)); + if (unlikely (device == NULL)) + return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + status = intel_device_init (device, fd); + if (unlikely (status)) { + free (device); + return (cairo_drm_device_t *) _cairo_device_create_in_error (status); + } + + device->base.surface.create = intel_surface_create; + device->base.surface.create_for_name = intel_surface_create_for_name; + device->base.surface.create_from_cacheable_image = NULL; + device->base.surface.flink = _cairo_drm_surface_flink; + device->base.surface.enable_scan_out = intel_surface_enable_scan_out; + + device->base.surface.map_to_image = intel_surface_map_to_image; + + device->base.device.flush = NULL; + device->base.device.throttle = intel_device_throttle; + device->base.device.destroy = intel_device_destroy; + + return _cairo_drm_device_init (&device->base, + fd, dev, + vendor_id, chip_id, + MAX_SIZE); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-intel.c b/gfx/cairo/cairo/src/drm/cairo-drm-intel.c new file mode 100644 index 000000000000..e6fb83dd51ce --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel.c @@ -0,0 +1,1347 @@ +/* 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. + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-intel-private.h" +#include "cairo-drm-intel-ioctl-private.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-pattern-private.h" +#include "cairo-image-surface-private.h" + +#include +#include +#include +#include + +#define GLYPH_CACHE_WIDTH 1024 +#define GLYPH_CACHE_HEIGHT 1024 +#define GLYPH_CACHE_MIN_SIZE 1 +#define GLYPH_CACHE_MAX_SIZE 128 + +#define IMAGE_CACHE_WIDTH 1024 +#define IMAGE_CACHE_HEIGHT 1024 + +int +intel_get (int fd, int param) +{ + struct drm_i915_getparam gp; + int value; + + gp.param = param; + gp.value = &value; + if (ioctl (fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0) + return 0; + + VG (VALGRIND_MAKE_MEM_DEFINED (&value, sizeof (value))); + + return value; +} + +cairo_bool_t +intel_info (int fd, uint64_t *gtt_size) +{ + struct drm_i915_gem_get_aperture info; + + if (! intel_get (fd, I915_PARAM_HAS_GEM)) + return FALSE; + + if (! intel_get (fd, I915_PARAM_HAS_EXECBUF2)) + return FALSE; + + if (ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &info) < 0) + return FALSE; + + VG (VALGRIND_MAKE_MEM_DEFINED (&info, sizeof (info))); + + if (gtt_size != NULL) + *gtt_size = info.aper_size; + + return TRUE; +} + +void +intel_bo_write (const intel_device_t *device, + intel_bo_t *bo, + unsigned long offset, + unsigned long size, + const void *data) +{ + struct drm_i915_gem_pwrite pwrite; + int ret; + + assert (bo->tiling == I915_TILING_NONE); + assert (size); + assert (offset < bo->base.size); + assert (size+offset <= bo->base.size); + + intel_bo_set_tiling (device, bo); + + assert (bo->_tiling == I915_TILING_NONE); + + memset (&pwrite, 0, sizeof (pwrite)); + pwrite.handle = bo->base.handle; + pwrite.offset = offset; + pwrite.size = size; + pwrite.data_ptr = (uint64_t) (uintptr_t) data; + do { + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite); + } while (ret == -1 && errno == EINTR); + assert (ret == 0); + + bo->busy = FALSE; +} + +void +intel_bo_read (const intel_device_t *device, + intel_bo_t *bo, + unsigned long offset, + unsigned long size, + void *data) +{ + struct drm_i915_gem_pread pread; + int ret; + + assert (bo->tiling == I915_TILING_NONE); + assert (size); + assert (offset < bo->base.size); + assert (size+offset <= bo->base.size); + + intel_bo_set_tiling (device, bo); + + assert (bo->_tiling == I915_TILING_NONE); + + memset (&pread, 0, sizeof (pread)); + pread.handle = bo->base.handle; + pread.offset = offset; + pread.size = size; + pread.data_ptr = (uint64_t) (uintptr_t) data; + do { + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_PREAD, &pread); + } while (ret == -1 && errno == EINTR); + assert (ret == 0); + + bo->cpu = TRUE; + bo->busy = FALSE; +} + +void * +intel_bo_map (const intel_device_t *device, intel_bo_t *bo) +{ + struct drm_i915_gem_set_domain set_domain; + uint32_t domain; + int ret; + + intel_bo_set_tiling (device, bo); + + if (bo->virtual != NULL) + return bo->virtual; + + if (bo->cpu && bo->tiling == I915_TILING_NONE) { + struct drm_i915_gem_mmap mmap_arg; + + mmap_arg.handle = bo->base.handle; + mmap_arg.offset = 0; + mmap_arg.size = bo->base.size; + mmap_arg.addr_ptr = 0; + + do { + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg); + } while (ret == -1 && errno == EINTR); + if (unlikely (ret != 0)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + bo->virtual = (void *) (uintptr_t) mmap_arg.addr_ptr; + domain = I915_GEM_DOMAIN_CPU; + } else { + struct drm_i915_gem_mmap_gtt mmap_arg; + void *ptr; + + /* Get the fake offset back... */ + mmap_arg.handle = bo->base.handle; + do { + ret = ioctl (device->base.fd, + DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg); + } while (ret == -1 && errno == EINTR); + if (unlikely (ret != 0)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + /* and mmap it */ + ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE, + MAP_SHARED, device->base.fd, + mmap_arg.offset); + if (unlikely (ptr == MAP_FAILED)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + bo->virtual = ptr; + domain = I915_GEM_DOMAIN_GTT; + } + + VG (VALGRIND_MAKE_MEM_DEFINED (bo->virtual, bo->base.size)); + + set_domain.handle = bo->base.handle; + set_domain.read_domains = domain; + set_domain.write_domain = domain; + + do { + ret = ioctl (device->base.fd, + DRM_IOCTL_I915_GEM_SET_DOMAIN, + &set_domain); + } while (ret == -1 && errno == EINTR); + + if (ret != 0) { + intel_bo_unmap (bo); + _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR); + return NULL; + } + + bo->busy = FALSE; + return bo->virtual; +} + +void +intel_bo_unmap (intel_bo_t *bo) +{ + munmap (bo->virtual, bo->base.size); + bo->virtual = NULL; +} + +cairo_bool_t +intel_bo_is_inactive (const intel_device_t *device, intel_bo_t *bo) +{ + struct drm_i915_gem_busy busy; + + if (! bo->busy) + return TRUE; + + /* Is this buffer busy for our intended usage pattern? */ + busy.handle = bo->base.handle; + busy.busy = 1; + ioctl (device->base.fd, DRM_IOCTL_I915_GEM_BUSY, &busy); + + bo->busy = busy.busy; + return ! busy.busy; +} + +cairo_bool_t +intel_bo_wait (const intel_device_t *device, const intel_bo_t *bo) +{ + struct drm_i915_gem_set_domain set_domain; + int ret; + + set_domain.handle = bo->base.handle; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; + set_domain.write_domain = 0; + + do { + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); + } while (ret == -1 && errno == EINTR); + + return ret == 0; +} + +static inline int +pot (int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +cairo_bool_t +intel_bo_madvise (intel_device_t *device, + intel_bo_t *bo, + int advice) +{ + struct drm_i915_gem_madvise madv; + + madv.handle = bo->base.handle; + madv.madv = advice; + madv.retained = TRUE; + ioctl (device->base.fd, DRM_IOCTL_I915_GEM_MADVISE, &madv); + return madv.retained; +} + +static void +intel_bo_set_real_size (intel_device_t *device, + intel_bo_t *bo, + size_t size) +{ + struct drm_i915_gem_real_size arg; + int ret; + + return; + + if (size == bo->base.size) + return; + + arg.handle = bo->base.handle; + arg.size = size; + do { + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_REAL_SIZE, &arg); + } while (ret == -1 && errno == EINTR); + + if (ret == 0) { + if (size > bo->base.size) { + assert (bo->exec == NULL); + bo->cpu = TRUE; + bo->busy = FALSE; + } + + bo->base.size = size; + } +} + +intel_bo_t * +intel_bo_create (intel_device_t *device, + uint32_t max_size, + uint32_t real_size, + cairo_bool_t gpu_target, + uint32_t tiling, + uint32_t stride) +{ + intel_bo_t *bo; + uint32_t cache_size; + struct drm_i915_gem_create create; + int bucket; + int ret; + + max_size = (max_size + 4095) & -4096; + real_size = (real_size + 4095) & -4096; + cache_size = pot (max_size); + bucket = ffs (cache_size / 4096) - 1; + if (bucket >= INTEL_BO_CACHE_BUCKETS) + cache_size = max_size; + + if (gpu_target) { + intel_bo_t *first = NULL; + + cairo_list_foreach_entry (bo, intel_bo_t, + &device->bo_in_flight, + cache_list) + { + assert (bo->exec != NULL); + if (tiling && bo->_tiling && + (bo->_tiling != tiling || bo->_stride != stride)) + { + continue; + } + + if (real_size <= bo->base.size) { + if (real_size >= bo->base.size/2) { + cairo_list_del (&bo->cache_list); + bo = intel_bo_reference (bo); + goto DONE; + } + + if (first == NULL) + first = bo; + } + } + + if (first != NULL) { + cairo_list_del (&first->cache_list); + bo = intel_bo_reference (first); + goto DONE; + } + } + + /* no cached buffer available, allocate fresh */ + bo = _cairo_freepool_alloc (&device->bo_pool); + if (unlikely (bo == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return bo; + } + + cairo_list_init (&bo->cache_list); + + bo->base.name = 0; + + bo->offset = 0; + bo->virtual = NULL; + bo->cpu = TRUE; + + bo->_tiling = I915_TILING_NONE; + bo->_stride = 0; + bo->purgeable = 0; + bo->busy = FALSE; + + bo->opaque0 = 0; + bo->opaque1 = 0; + + bo->exec = NULL; + bo->batch_read_domains = 0; + bo->batch_write_domain = 0; + cairo_list_init (&bo->link); + + create.size = cache_size; + create.handle = 0; + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_CREATE, &create); + if (unlikely (ret != 0)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + _cairo_freepool_free (&device->bo_pool, bo); + return NULL; + } + + bo->base.handle = create.handle; + bo->full_size = bo->base.size = create.size; + + intel_bo_set_real_size (device, bo, real_size); + CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); +DONE: + bo->tiling = tiling; + bo->stride = stride; + return bo; +} + +intel_bo_t * +intel_bo_create_for_name (intel_device_t *device, uint32_t name) +{ + struct drm_i915_gem_get_tiling get_tiling; + cairo_status_t status; + intel_bo_t *bo; + int ret; + + bo = _cairo_freepool_alloc (&device->bo_pool); + if (unlikely (bo == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name); + if (unlikely (status)) + goto FAIL; + + CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); + cairo_list_init (&bo->cache_list); + + bo->full_size = bo->base.size; + bo->offset = 0; + bo->virtual = NULL; + bo->purgeable = 0; + bo->busy = TRUE; + bo->cpu = FALSE; + + bo->opaque0 = 0; + bo->opaque1 = 0; + + bo->exec = NULL; + bo->batch_read_domains = 0; + bo->batch_write_domain = 0; + cairo_list_init (&bo->link); + + memset (&get_tiling, 0, sizeof (get_tiling)); + get_tiling.handle = bo->base.handle; + + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_GET_TILING, &get_tiling); + if (unlikely (ret != 0)) { + _cairo_error_throw (CAIRO_STATUS_DEVICE_ERROR); + _cairo_drm_bo_close (&device->base, &bo->base); + goto FAIL; + } + + bo->_tiling = bo->tiling = get_tiling.tiling_mode; + // bo->stride = get_tiling.stride; /* XXX not available from get_tiling */ + + return bo; + +FAIL: + _cairo_freepool_free (&device->bo_pool, bo); + return NULL; +} + +static void +intel_bo_release (void *_dev, void *_bo) +{ + intel_device_t *device = _dev; + intel_bo_t *bo = _bo; + + if (bo->virtual != NULL) + intel_bo_unmap (bo); + + assert (bo->exec == NULL); + assert (cairo_list_is_empty (&bo->cache_list)); + + _cairo_drm_bo_close (&device->base, &bo->base); + _cairo_freepool_free (&device->bo_pool, bo); +} + +void +intel_bo_set_tiling (const intel_device_t *device, + intel_bo_t *bo) +{ + struct drm_i915_gem_set_tiling set_tiling; + int ret; + + if (bo->tiling == bo->_tiling && + (bo->tiling == I915_TILING_NONE || bo->stride == bo->_stride)) + return; + + do { + set_tiling.handle = bo->base.handle; + set_tiling.tiling_mode = bo->tiling; + set_tiling.stride = bo->stride; + + ret = ioctl (device->base.fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling); + } while (ret == -1 && errno == EINTR); + + assert (ret == 0); + bo->_tiling = bo->tiling; + bo->_stride = bo->stride; +} + +static cairo_status_t +_intel_bo_put_a1_image (intel_device_t *device, + intel_bo_t *bo, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *a8 = buf; + uint8_t *data; + int x; + + data = src->data + src_y * src->stride; + + if (bo->tiling == I915_TILING_NONE && width == bo->stride) { + uint8_t *p; + int size; + + size = bo->stride * height; + if (size > (int) sizeof (buf)) { + a8 = _cairo_malloc_ab (bo->stride, height); + if (a8 == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + p = a8; + while (height--) { + for (x = 0; x < width; x++) { + int i = src_x + x; + int byte = i / 8; + int bit = i % 8; + p[x] = data[byte] & (1 << bit) ? 0xff : 0x00; + } + + data += src->stride; + p += bo->stride; + } + + intel_bo_write (device, bo, + dst_y * bo->stride + dst_x, /* XXX bo_offset */ + size, a8); + } else { + uint8_t *dst; + + if (width > (int) sizeof (buf)) { + a8 = _cairo_malloc (width); + if (a8 == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + dst = intel_bo_map (device, bo); + if (dst == NULL) { + if (a8 != buf) + free (a8); + return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + } + + dst += dst_y * bo->stride + dst_x; /* XXX bo_offset */ + while (height--) { + for (x = 0; x < width; x++) { + int i = src_x + x; + int byte = i / 8; + int bit = i % 8; + a8[x] = data[byte] & (1 << bit) ? 0xff : 0x00; + } + + memcpy (dst, a8, width); + dst += bo->stride; + data += src->stride; + } + } + + if (a8 != buf) + free (a8); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +intel_bo_put_image (intel_device_t *device, + intel_bo_t *bo, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + uint8_t *data; + int size; + int offset; + + intel_bo_set_tiling (device, bo); + + offset = dst_y * bo->stride; + data = src->data + src_y * src->stride; + switch (src->format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + offset += 4 * dst_x; + data += 4 * src_x; + size = 4 * width; + break; + case CAIRO_FORMAT_RGB16_565: + offset += 2 * dst_x; + data += 2 * src_x; + size = 2 * width; + break; + case CAIRO_FORMAT_A8: + offset += dst_x; + data += src_x; + size = width; + break; + case CAIRO_FORMAT_A1: + return _intel_bo_put_a1_image (device, bo, src, + src_x, src_y, + width, height, + dst_x, dst_y); + default: + case CAIRO_FORMAT_INVALID: + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + } + + if (bo->tiling == I915_TILING_NONE && src->stride == bo->stride) { + intel_bo_write (device, bo, offset, bo->stride * height, data); + } else { + uint8_t *dst; + + dst = intel_bo_map (device, bo); + if (unlikely (dst == NULL)) + return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + + dst += offset; + while (height--) { + memcpy (dst, data, size); + dst += bo->stride; + data += src->stride; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_intel_snapshot_cache_entry_can_remove (const void *closure) +{ + return TRUE; +} + +static void +_intel_snapshot_cache_entry_destroy (void *closure) +{ + intel_surface_t *surface = cairo_container_of (closure, + intel_surface_t, + snapshot_cache_entry); + + surface->snapshot_cache_entry.hash = 0; +} + +cairo_status_t +intel_device_init (intel_device_t *device, int fd) +{ + struct drm_i915_gem_get_aperture aperture; + cairo_status_t status; + size_t size; + int ret; + int n; + + ret = ioctl (fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); + if (ret != 0) + return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + + CAIRO_MUTEX_INIT (device->mutex); + + device->gtt_max_size = aperture.aper_size; + device->gtt_avail_size = aperture.aper_available_size; + device->gtt_avail_size -= device->gtt_avail_size >> 5; + + size = aperture.aper_size / 8; + device->snapshot_cache_max_size = size / 4; + status = _cairo_cache_init (&device->snapshot_cache, + NULL, + _intel_snapshot_cache_entry_can_remove, + _intel_snapshot_cache_entry_destroy, + size); + if (unlikely (status)) + return status; + + for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) { + device->glyph_cache[n].buffer.bo = NULL; + cairo_list_init (&device->glyph_cache[n].rtree.pinned); + } + cairo_list_init (&device->fonts); + + device->gradient_cache.size = 0; + + device->base.bo.release = intel_bo_release; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_intel_gradient_cache_fini (intel_device_t *device) +{ + unsigned int n; + + for (n = 0; n < device->gradient_cache.size; n++) { + _cairo_pattern_fini (&device->gradient_cache.cache[n].pattern.base); + if (device->gradient_cache.cache[n].buffer.bo != NULL) + cairo_drm_bo_destroy (&device->base.base, + &device->gradient_cache.cache[n].buffer.bo->base); + } +} + +static void +_intel_glyph_cache_fini (intel_device_t *device, intel_buffer_cache_t *cache) +{ + if (cache->buffer.bo == NULL) + return; + + intel_bo_destroy (device, cache->buffer.bo); + _cairo_rtree_fini (&cache->rtree); +} + +void +intel_device_fini (intel_device_t *device) +{ + cairo_scaled_font_t *scaled_font, *next_scaled_font; + int n; + + cairo_list_foreach_entry_safe (scaled_font, + next_scaled_font, + cairo_scaled_font_t, + &device->fonts, + link) + { + _cairo_scaled_font_revoke_ownership (scaled_font); + } + + for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) + _intel_glyph_cache_fini (device, &device->glyph_cache[n]); + + _cairo_cache_fini (&device->snapshot_cache); + + _intel_gradient_cache_fini (device); + _cairo_freepool_fini (&device->bo_pool); + + _cairo_drm_device_fini (&device->base); +} + +void +intel_throttle (intel_device_t *device) +{ + ioctl (device->base.fd, DRM_IOCTL_I915_GEM_THROTTLE); +} + +void +intel_glyph_cache_unpin (intel_device_t *device) +{ + int n; + + for (n = 0; n < ARRAY_LENGTH (device->glyph_cache); n++) + _cairo_rtree_unpin (&device->glyph_cache[n].rtree); +} + +static cairo_status_t +intel_glyph_cache_add_glyph (intel_device_t *device, + intel_buffer_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + intel_glyph_t *glyph; + cairo_rtree_node_t *node = NULL; + double sf_x, sf_y; + cairo_status_t status; + uint8_t *dst, *src; + 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 unpinned slot */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_rtree_evict_random (&cache->rtree, width, height, &node); + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_rtree_node_insert (&cache->rtree, node, width, height, &node); + } + if (unlikely (status)) + return status; + + /* XXX streaming upload? */ + + height = glyph_surface->height; + src = glyph_surface->data; + dst = cache->buffer.bo->virtual; + if (dst == NULL) { + dst = intel_bo_map (device, cache->buffer.bo); + if (unlikely (dst == NULL)) + return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + } + + dst += node->y * cache->buffer.stride; + switch (glyph_surface->format) { + case CAIRO_FORMAT_A1: { + uint8_t buf[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *a8 = buf; + int x; + + if (width > (int) sizeof (buf)) { + a8 = _cairo_malloc (width); + if (unlikely (a8 == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + dst += node->x; + width = glyph_surface->width; + while (height--) { + for (x = 0; x < width; x++) + a8[x] = src[x>>3] & (1 << (x&7)) ? 0xff : 0x00; + + memcpy (dst, a8, width); + dst += cache->buffer.stride; + src += glyph_surface->stride; + } + + if (a8 != buf) + free (a8); + break; + } + + case CAIRO_FORMAT_A8: + dst += node->x; + width = glyph_surface->width; + while (height--) { + memcpy (dst, src, width); + dst += cache->buffer.stride; + src += glyph_surface->stride; + } + break; + + case CAIRO_FORMAT_ARGB32: + dst += 4*node->x; + width = 4*glyph_surface->width; + while (height--) { + memcpy (dst, src, width); + dst += cache->buffer.stride; + src += glyph_surface->stride; + } + break; + default: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + } + + scaled_glyph->surface_private = node; + + glyph= (intel_glyph_t *) node; + glyph->node.owner = &scaled_glyph->surface_private; + glyph->cache = cache; + + /* compute tex coords: bottom-right, bottom-left, top-left */ + sf_x = 1. / cache->buffer.width; + sf_y = 1. / cache->buffer.height; + glyph->texcoord[0] = + texcoord_2d_16 (sf_x * (node->x + glyph_surface->width), + sf_y * (node->y + glyph_surface->height)); + glyph->texcoord[1] = + texcoord_2d_16 (sf_x * node->x, + sf_y * (node->y + glyph_surface->height)); + glyph->texcoord[2] = + texcoord_2d_16 (sf_x * node->x, + sf_y * node->y); + + glyph->width = glyph_surface->width; + glyph->height = glyph_surface->height; + + return CAIRO_STATUS_SUCCESS; +} + +void +intel_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + intel_glyph_t *glyph; + + glyph = scaled_glyph->surface_private; + if (glyph != NULL) { + /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ + glyph->node.owner = NULL; + if (! glyph->node.pinned) + _cairo_rtree_node_remove (&glyph->cache->rtree, &glyph->node); + } +} + +void +intel_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_list_del (&scaled_font->link); +} + +static cairo_status_t +intel_get_glyph_cache (intel_device_t *device, + cairo_format_t format, + intel_buffer_cache_t **out) +{ + intel_buffer_cache_t *cache; + cairo_status_t status; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + cache = &device->glyph_cache[0]; + format = CAIRO_FORMAT_ARGB32; + break; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + cache = &device->glyph_cache[1]; + format = CAIRO_FORMAT_A8; + break; + default: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + } + + if (unlikely (cache->buffer.bo == NULL)) { + status = intel_buffer_cache_init (cache, device, format, + INTEL_GLYPH_CACHE_WIDTH, + INTEL_GLYPH_CACHE_HEIGHT); + if (unlikely (status)) + return status; + + _cairo_rtree_init (&cache->rtree, + INTEL_GLYPH_CACHE_WIDTH, + INTEL_GLYPH_CACHE_HEIGHT, + 0, sizeof (intel_glyph_t)); + } + + *out = cache; + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +intel_get_glyph (intel_device_t *device, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_bool_t own_surface = FALSE; + intel_buffer_cache_t *cache; + cairo_status_t status; + + if (scaled_glyph->surface == NULL) { + status = + scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + CAIRO_SCALED_GLYPH_INFO_SURFACE); + if (unlikely (status)) + return status; + + if (unlikely (scaled_glyph->surface == NULL)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + own_surface = TRUE; + } + + if (unlikely (scaled_glyph->surface->width == 0 || + scaled_glyph->surface->height == 0)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + if (unlikely (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || + scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = intel_get_glyph_cache (device, + scaled_glyph->surface->format, + &cache); + if (unlikely (status)) + return status; + + status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph); + if (unlikely (_cairo_status_is_error (status))) + return status; + + if (unlikely (status == CAIRO_INT_STATUS_UNSUPPORTED)) { + /* no room, replace entire cache */ + + assert (cache->buffer.bo->exec != NULL); + + _cairo_rtree_reset (&cache->rtree); + intel_bo_destroy (device, cache->buffer.bo); + cache->buffer.bo = NULL; + + status = intel_buffer_cache_init (cache, device, + scaled_glyph->surface->format, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT); + if (unlikely (status)) + return status; + + status = intel_glyph_cache_add_glyph (device, cache, scaled_glyph); + if (unlikely (status)) + return status; + } + + if (own_surface) { + /* and release the copy of the image from system memory */ + cairo_surface_destroy (&scaled_glyph->surface->base); + scaled_glyph->surface = NULL; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +intel_buffer_cache_init (intel_buffer_cache_t *cache, + intel_device_t *device, + cairo_format_t format, + int width, int height) +{ + const uint32_t tiling = I915_TILING_Y; + uint32_t stride, size; + + assert ((width & 3) == 0); + assert ((height & 1) == 0); + cache->buffer.format = format; + cache->buffer.width = width; + cache->buffer.height = height; + + switch (format) { + default: + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + case CAIRO_FORMAT_ARGB32: + cache->buffer.map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; + stride = width * 4; + break; + case CAIRO_FORMAT_A8: + cache->buffer.map0 = MAPSURF_8BIT | MT_8BIT_I8; + stride = width; + break; + } + + size = height * stride; + cache->buffer.bo = intel_bo_create (device, + size, size, + FALSE, tiling, stride); + if (unlikely (cache->buffer.bo == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cache->buffer.stride = stride; + + cache->buffer.offset = 0; + cache->buffer.map0 |= MS3_tiling (tiling); + cache->buffer.map0 |= ((height - 1) << MS3_HEIGHT_SHIFT) | + ((width - 1) << MS3_WIDTH_SHIFT); + cache->buffer.map1 = ((stride / 4) - 1) << MS4_PITCH_SHIFT; + + cache->ref_count = 0; + cairo_list_init (&cache->link); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +intel_snapshot_cache_insert (intel_device_t *device, + intel_surface_t *surface) +{ + cairo_status_t status; + + surface->snapshot_cache_entry.size = surface->drm.bo->size; + if (surface->snapshot_cache_entry.size > + device->snapshot_cache_max_size) + { + return CAIRO_STATUS_SUCCESS; + } + + if (device->snapshot_cache.freeze_count == 0) + _cairo_cache_freeze (&device->snapshot_cache); + + surface->snapshot_cache_entry.hash = (unsigned long) surface; + status = _cairo_cache_insert (&device->snapshot_cache, + &surface->snapshot_cache_entry); + if (unlikely (status)) { + surface->snapshot_cache_entry.hash = 0; + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +void +intel_surface_detach_snapshot (cairo_surface_t *abstract_surface) +{ + intel_surface_t *surface = (intel_surface_t *) abstract_surface; + + if (surface->snapshot_cache_entry.hash) { + intel_device_t *device; + + device = (intel_device_t *) surface->drm.base.device; + _cairo_cache_remove (&device->snapshot_cache, + &surface->snapshot_cache_entry); + assert (surface->snapshot_cache_entry.hash == 0); + } +} + +void +intel_snapshot_cache_thaw (intel_device_t *device) +{ + if (device->snapshot_cache.freeze_count) + _cairo_cache_thaw (&device->snapshot_cache); +} + +static cairo_bool_t +_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, + const cairo_gradient_pattern_t *b) +{ + unsigned int n; + + if (a->n_stops != b->n_stops) + return FALSE; + + for (n = 0; n < a->n_stops; n++) { + if (_cairo_fixed_from_double (a->stops[n].offset) != + _cairo_fixed_from_double (b->stops[n].offset)) + { + return FALSE; + } + + if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) + return FALSE; + } + + return TRUE; +} + +static uint32_t +hars_petruska_f54_1_random (void) +{ +#define rol(x,k) ((x << k) | (x >> (32-k))) + static uint32_t x; + return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; +#undef rol +} + +static int +intel_gradient_sample_width (const cairo_gradient_pattern_t *gradient) +{ + unsigned int n; + int width; + + width = 8; + for (n = 1; n < gradient->n_stops; n++) { + double dx = gradient->stops[n].offset - gradient->stops[n-1].offset; + double delta, max; + int ramp; + + if (dx == 0) + continue; + + max = gradient->stops[n].color.red - + gradient->stops[n-1].color.red; + + delta = gradient->stops[n].color.green - + gradient->stops[n-1].color.green; + if (delta > max) + max = delta; + + delta = gradient->stops[n].color.blue - + gradient->stops[n-1].color.blue; + if (delta > max) + max = delta; + + delta = gradient->stops[n].color.alpha - + gradient->stops[n-1].color.alpha; + if (delta > max) + max = delta; + + ramp = 128 * max / dx; + if (ramp > width) + width = ramp; + } + + width = (width + 7) & -8; + return MIN (width, 1024); +} + +cairo_status_t +intel_gradient_render (intel_device_t *device, + const cairo_gradient_pattern_t *pattern, + intel_buffer_t *buffer) +{ + 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; + int width; + unsigned int i; + cairo_status_t status; + + for (i = 0; i < device->gradient_cache.size; i++) { + if (_gradient_color_stops_equal (pattern, + &device->gradient_cache.cache[i].pattern.gradient.base)) { + *buffer = device->gradient_cache.cache[i].buffer; + return CAIRO_STATUS_SUCCESS; + } + } + + pixman_stops = pixman_stops_stack; + if (unlikely (pattern->n_stops > ARRAY_LENGTH (pixman_stops_stack))) { + pixman_stops = _cairo_malloc_ab (pattern->n_stops, + sizeof (pixman_gradient_stop_t)); + if (unlikely (pixman_stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < pattern->n_stops; i++) { + pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); + pixman_stops[i].color.red = pattern->stops[i].color.red_short; + pixman_stops[i].color.green = pattern->stops[i].color.green_short; + pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; + pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; + } + + width = intel_gradient_sample_width (pattern); + + p1.x = 0; + p1.y = 0; + p2.x = width << 16; + p2.y = 0; + + gradient = pixman_image_create_linear_gradient (&p1, &p2, + pixman_stops, + pattern->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 (PIXMAN_a8r8g8b8, width, 1, NULL, 0); + 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); + + buffer->bo = intel_bo_create (device, + 4*width, 4*width, + FALSE, I915_TILING_NONE, 4*width); + if (unlikely (buffer->bo == NULL)) { + pixman_image_unref (image); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + intel_bo_write (device, buffer->bo, 0, 4*width, pixman_image_get_data (image)); + pixman_image_unref (image); + + buffer->offset = 0; + buffer->width = width; + buffer->height = 1; + buffer->stride = 4*width; + buffer->format = CAIRO_FORMAT_ARGB32; + buffer->map0 = MAPSURF_32BIT | MT_32BIT_ARGB8888; + buffer->map0 |= ((width - 1) << MS3_WIDTH_SHIFT); + buffer->map1 = (width - 1) << MS4_PITCH_SHIFT; + + if (device->gradient_cache.size < GRADIENT_CACHE_SIZE) { + i = device->gradient_cache.size++; + } else { + i = hars_petruska_f54_1_random () % GRADIENT_CACHE_SIZE; + _cairo_pattern_fini (&device->gradient_cache.cache[i].pattern.base); + intel_bo_destroy (device, device->gradient_cache.cache[i].buffer.bo); + } + + status = _cairo_pattern_init_copy (&device->gradient_cache.cache[i].pattern.base, + &pattern->base); + if (unlikely (status)) { + intel_bo_destroy (device, buffer->bo); + /* Ensure the cache is correctly initialised for i965_device_destroy */ + _cairo_pattern_init_solid (&device->gradient_cache.cache[i].pattern.solid, + CAIRO_COLOR_TRANSPARENT); + return status; + } + + device->gradient_cache.cache[i].buffer = *buffer; + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-private.h new file mode 100644 index 000000000000..2db7f38d6d53 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-private.h @@ -0,0 +1,238 @@ +/* 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. + * + * Contributors(s): + * Chris Wilson + */ + +#ifndef CAIRO_DRM_PRIVATE_H +#define CAIRO_DRM_PRIVATE_H + +#include "cairo-drm.h" + +#include "cairo-device-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-surface-private.h" + +#include /* dev_t */ + +typedef struct _cairo_drm_device cairo_drm_device_t; + +typedef cairo_drm_device_t * +(*cairo_drm_device_create_func_t) (int fd, + dev_t dev, + int vendor_id, + int chip_id); + +typedef cairo_int_status_t +(*cairo_drm_device_flush_func_t) (cairo_drm_device_t *device); + +typedef cairo_int_status_t +(*cairo_drm_device_throttle_func_t) (cairo_drm_device_t *device); + +typedef void +(*cairo_drm_device_destroy_func_t) (void *data); + +typedef cairo_surface_t * +(*cairo_drm_surface_create_func_t) (cairo_drm_device_t *device, + cairo_format_t format, + int width, int height); + +typedef cairo_surface_t * +(*cairo_drm_surface_create_for_name_func_t) (cairo_drm_device_t *device, + unsigned int name, + cairo_format_t format, + int width, int height, int stride); + +typedef cairo_surface_t * +(*cairo_drm_surface_create_from_cacheable_image_func_t) + (cairo_drm_device_t *device, cairo_surface_t *image); + +typedef cairo_int_status_t +(*cairo_drm_surface_flink_func_t) (void *surface); + +typedef cairo_status_t +(*cairo_drm_surface_enable_scan_out_func_t) (void *surface); + +typedef cairo_surface_t * +(*cairo_drm_surface_map_to_image_func_t) (void *surface); + +typedef struct _cairo_drm_bo_backend { + void (*release) (void *device, void *bo); +} cairo_drm_bo_backend_t; + +typedef struct _cairo_drm_device_backend { + cairo_drm_device_flush_func_t flush; + cairo_drm_device_throttle_func_t throttle; + cairo_drm_device_destroy_func_t destroy; +} cairo_drm_device_backend_t; + +typedef struct _cairo_drm_surface_backend { + cairo_drm_surface_create_func_t create; + cairo_drm_surface_create_for_name_func_t create_for_name; + cairo_drm_surface_create_from_cacheable_image_func_t create_from_cacheable_image; + cairo_drm_surface_flink_func_t flink; + cairo_drm_surface_enable_scan_out_func_t enable_scan_out; + cairo_drm_surface_map_to_image_func_t map_to_image; +} cairo_drm_surface_backend_t; + +typedef struct _cairo_drm_bo { + cairo_reference_count_t ref_count; + uint32_t name; + uint32_t handle; + uint32_t size; +} cairo_drm_bo_t; + +struct _cairo_drm_device { + cairo_device_t base; + + int vendor_id; + int chip_id; + dev_t id; + int fd; + + int max_surface_size; + + cairo_drm_bo_backend_t bo; + cairo_drm_surface_backend_t surface; + cairo_drm_device_backend_t device; + + cairo_drm_device_t *next, *prev; +}; + +typedef struct _cairo_drm_surface { + cairo_surface_t base; + + cairo_drm_bo_t *bo; + + cairo_format_t format; + int width, height, stride; + + cairo_surface_t *fallback; + uint32_t map_count; +} cairo_drm_surface_t; + +static inline cairo_drm_bo_t * +cairo_drm_bo_reference (cairo_drm_bo_t *bo) +{ + _cairo_reference_count_inc (&bo->ref_count); + return bo; +} + +static cairo_always_inline void +cairo_drm_bo_destroy (cairo_device_t *abstract_device, + cairo_drm_bo_t *bo) +{ + if (_cairo_reference_count_dec_and_test (&bo->ref_count)) { + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + device->bo.release (device, bo); + } +} + +cairo_private cairo_status_t +_cairo_drm_bo_open_for_name (const cairo_drm_device_t *dev, + cairo_drm_bo_t *bo, + uint32_t name); + +cairo_private cairo_status_t +_cairo_drm_bo_flink (const cairo_drm_device_t *dev, + cairo_drm_bo_t *bo); + +cairo_private void +_cairo_drm_bo_close (const cairo_drm_device_t *dev, + cairo_drm_bo_t *bo); + +cairo_private void +_cairo_drm_surface_init (cairo_drm_surface_t *surface, + cairo_format_t format, + int width, int height); + +cairo_private cairo_status_t +_cairo_drm_surface_finish (cairo_drm_surface_t *surface); + +cairo_private void +_cairo_drm_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options); + +cairo_private cairo_bool_t +_cairo_drm_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + +cairo_private cairo_int_status_t +_cairo_drm_surface_flink (void *abstract_surface); + +static inline cairo_drm_device_t * +_cairo_drm_device_create_in_error (cairo_status_t status) +{ + return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); +} + +cairo_private cairo_drm_device_t * +_cairo_drm_device_init (cairo_drm_device_t *device, + int fd, + dev_t devid, + int vendor_id, + int chip_id, + int max_surface_size); + +cairo_private void +_cairo_drm_device_fini (cairo_drm_device_t *device); + +/* h/w specific backends */ + +cairo_private cairo_drm_device_t * +_cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id); + +cairo_private cairo_drm_device_t * +_cairo_drm_i915_device_create (int fd, dev_t dev, int vendor_id, int chip_id); + +cairo_private cairo_drm_device_t * +_cairo_drm_i965_device_create (int fd, dev_t dev, int vendor_id, int chip_id); + +cairo_private cairo_drm_device_t * +_cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id); + +#if CAIRO_HAS_GALLIUM_SURFACE +cairo_private cairo_drm_device_t * +_cairo_drm_gallium_device_create (int fd, dev_t dev, int vendor_id, int chip_id); +#endif + +slim_hidden_proto (cairo_drm_device_default); +slim_hidden_proto (cairo_drm_device_get); +slim_hidden_proto (cairo_drm_device_get_for_fd); + +slim_hidden_proto (cairo_drm_surface_create_for_name); + +cairo_private cairo_bool_t +_cairo_drm_size_is_valid (cairo_device_t *abstract_device, + int width, int height); + +#endif /* CAIRO_DRM_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-radeon-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-radeon-private.h new file mode 100644 index 000000000000..076852835934 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-radeon-private.h @@ -0,0 +1,103 @@ +/* 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. + * + */ + +#ifndef CAIRO_DRM_RADEON_PRIVATE_H +#define CAIRO_DRM_RADEON_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" +#include "cairo-drm-private.h" +#include "cairo-freelist-private.h" + +typedef struct _radeon_bo { + cairo_drm_bo_t base; + + void *virtual; + + cairo_bool_t in_batch; + uint32_t read_domains; + uint32_t write_domain; +} radeon_bo_t; + +typedef struct _radeon_device { + cairo_drm_device_t base; + cairo_freepool_t bo_pool; + + uint64_t vram_limit; + uint64_t gart_limit; +} radeon_device_t; + +cairo_private cairo_status_t +radeon_device_init (radeon_device_t *device, int fd); + +cairo_private void +radeon_device_fini (radeon_device_t *device); + +cairo_private cairo_bool_t +radeon_info (int fd, + uint64_t *gart_size, + uint64_t *vram_size); + +cairo_private void +radeon_bo_write (const radeon_device_t *dev, + radeon_bo_t *bo, + unsigned long offset, + unsigned long size, + const void *data); + +cairo_private void +radeon_bo_read (const radeon_device_t *dev, + radeon_bo_t *bo, + unsigned long offset, + unsigned long size, + void *data); + +cairo_private void +radeon_bo_wait (const radeon_device_t *dev, radeon_bo_t *bo); + +cairo_private void * +radeon_bo_map (const radeon_device_t *dev, radeon_bo_t *bo); + +cairo_private void +radeon_bo_unmap (radeon_bo_t *bo); + +cairo_private cairo_drm_bo_t * +radeon_bo_create (radeon_device_t *dev, + uint32_t size, + uint32_t initial_domain); + +cairo_private cairo_drm_bo_t * +radeon_bo_create_for_name (radeon_device_t *dev, uint32_t name); + +cairo_private cairo_surface_t * +radeon_bo_get_image (const radeon_device_t *device, + radeon_bo_t *bo, + const cairo_drm_surface_t *surface); + +#endif /* CAIRO_DRM_RADEON_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-radeon-surface.c b/gfx/cairo/cairo/src/drm/cairo-drm-radeon-surface.c new file mode 100644 index 000000000000..9c9f8526c699 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-radeon-surface.c @@ -0,0 +1,454 @@ +/* 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. + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-radeon-private.h" + +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" + +#include +#include /* workaround for broken */ +#include + +/* Basic stub surface for radeon chipsets */ + +#define MAX_SIZE 2048 + +typedef struct _radeon_surface { + cairo_drm_surface_t base; +} radeon_surface_t; + +static inline radeon_device_t * +to_radeon_device (cairo_device_t *device) +{ + return (radeon_device_t *) device; +} + +static inline radeon_bo_t * +to_radeon_bo (cairo_drm_bo_t *bo) +{ + return (radeon_bo_t *) bo; +} + +static cairo_surface_t * +radeon_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); +} + +static cairo_status_t +radeon_surface_finish (void *abstract_surface) +{ + radeon_surface_t *surface = abstract_surface; + + return _cairo_drm_surface_finish (&surface->base); +} + +static cairo_status_t +radeon_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + radeon_surface_t *surface = abstract_surface; + cairo_surface_t *image; + cairo_status_t status; + + /* XXX batch flush */ + + if (surface->base.fallback != NULL) { + image = surface->base.fallback; + goto DONE; + } + + image = _cairo_surface_has_snapshot (&surface->base.base, + &_cairo_image_surface_backend); + if (image != NULL) + goto DONE; + + if (surface->base.base.backend->flush != NULL) { + status = surface->base.base.backend->flush (surface); + if (unlikely (status)) + return status; + } + + image = radeon_bo_get_image (to_radeon_device (surface->base.base.device), + to_radeon_bo (surface->base.bo), + &surface->base); + status = image->status; + if (unlikely (status)) + return status; + + _cairo_surface_attach_snapshot (&surface->base.base, image, cairo_surface_destroy); + +DONE: + *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +radeon_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_surface_t * +radeon_surface_map_to_image (radeon_surface_t *surface) +{ + if (surface->base.fallback == NULL) { + cairo_surface_t *image; + cairo_status_t status; + void *ptr; + + if (surface->base.base.backend->flush != NULL) { + status = surface->base.base.backend->flush (surface); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + } + + ptr = radeon_bo_map (to_radeon_device (surface->base.base.device), + to_radeon_bo (surface->base.bo)); + if (unlikely (ptr == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + image = cairo_image_surface_create_for_data (ptr, + surface->base.format, + surface->base.width, + surface->base.height, + surface->base.stride); + if (unlikely (image->status)) { + radeon_bo_unmap (to_radeon_bo (surface->base.bo)); + return image; + } + + surface->base.fallback = image; + } + + return surface->base.fallback; +} + +static cairo_status_t +radeon_surface_flush (void *abstract_surface, + unsigned flags) +{ + radeon_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + if (surface->base.fallback == NULL) + return CAIRO_STATUS_SUCCESS; + + /* kill any outstanding maps */ + cairo_surface_finish (surface->base.fallback); + + status = cairo_surface_status (surface->base.fallback); + cairo_surface_destroy (surface->base.fallback); + surface->base.fallback = NULL; + + radeon_bo_unmap (to_radeon_bo (surface->base.bo)); + + return status; +} + +static cairo_int_status_t +radeon_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + return _cairo_surface_paint (radeon_surface_map_to_image (abstract_surface), + op, source, clip); +} + +static cairo_int_status_t +radeon_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_clip_t *clip) +{ + return _cairo_surface_mask (radeon_surface_map_to_image (abstract_surface), + op, source, mask, clip); +} + +static cairo_int_status_t +radeon_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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, + cairo_clip_t *clip) +{ + return _cairo_surface_stroke (radeon_surface_map_to_image (abstract_surface), + op, source, path, stroke_style, ctm, ctm_inverse, + tolerance, antialias, clip); +} + +static cairo_int_status_t +radeon_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + return _cairo_surface_fill (radeon_surface_map_to_image (abstract_surface), + op, source, path, fill_rule, + tolerance, antialias, clip); +} + +static cairo_int_status_t +radeon_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, + cairo_clip_t *clip, + int *num_remaining) +{ + *num_remaining = 0; + return _cairo_surface_show_text_glyphs (radeon_surface_map_to_image (abstract_surface), + op, source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, clip); +} + +static const cairo_surface_backend_t radeon_surface_backend = { + CAIRO_SURFACE_TYPE_DRM, + _cairo_default_context_create, + + radeon_surface_create_similar, + radeon_surface_finish, + + NULL, + radeon_surface_acquire_source_image, + radeon_surface_release_source_image, + + NULL, NULL, NULL, + NULL, /* composite */ + NULL, /* fill */ + NULL, /* trapezoids */ + NULL, /* span */ + NULL, /* check-span */ + + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_drm_surface_get_extents, + NULL, /* old-glyphs */ + _cairo_drm_surface_get_font_options, + + radeon_surface_flush, + NULL, /* mark dirty */ + NULL, NULL, /* font/glyph fini */ + + radeon_surface_paint, + radeon_surface_mask, + radeon_surface_stroke, + radeon_surface_fill, + radeon_surface_glyphs, +}; + +static void +radeon_surface_init (radeon_surface_t *surface, + cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + _cairo_surface_init (&surface->base.base, + &radeon_surface_backend, + &device->base, + _cairo_content_from_format (format), + FALSE); + _cairo_drm_surface_init (&surface->base, format, width, height); +} + +static cairo_surface_t * +radeon_surface_create_internal (cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + radeon_surface_t *surface; + cairo_status_t status; + + surface = _cairo_malloc (sizeof (radeon_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + radeon_surface_init (surface, device, format, width, height); + + if (width && height) { + surface->base.stride = + cairo_format_stride_for_width (surface->base.format, width); + + surface->base.bo = radeon_bo_create (to_radeon_device (&device->base), + surface->base.stride * height, + RADEON_GEM_DOMAIN_GTT); + + if (unlikely (surface->base.bo == NULL)) { + status = _cairo_drm_surface_finish (&surface->base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + return &surface->base.base; +} + +static cairo_surface_t * +radeon_surface_create (cairo_drm_device_t *device, + cairo_format_t format, + int width, int height) +{ + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB16_565: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + } + + return radeon_surface_create_internal (device, format, width, height); +} + +static cairo_surface_t * +radeon_surface_create_for_name (cairo_drm_device_t *device, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + radeon_surface_t *surface; + cairo_status_t status; + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: + case CAIRO_FORMAT_RGB16_565: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_A8: + break; + } + + if (stride < cairo_format_stride_for_width (format, width)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + + surface = _cairo_malloc (sizeof (radeon_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + radeon_surface_init (surface, device, format, width, height); + + if (width && height) { + surface->base.stride = stride; + + surface->base.bo = radeon_bo_create_for_name (to_radeon_device (&device->base), + name); + + if (unlikely (surface->base.bo == NULL)) { + status = _cairo_drm_surface_finish (&surface->base); + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + return &surface->base.base; +} + +static void +radeon_device_destroy (void *data) +{ + radeon_device_t *device = data; + + radeon_device_fini (device); + + free (data); +} + +cairo_drm_device_t * +_cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id) +{ + radeon_device_t *device; + uint64_t gart_size, vram_size; + cairo_status_t status; + + if (! radeon_info (fd, &gart_size, &vram_size)) + return NULL; + + device = _cairo_malloc (sizeof (radeon_device_t)); + if (device == NULL) + return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + status = radeon_device_init (device, fd); + if (unlikely (status)) { + free (device); + return _cairo_drm_device_create_in_error (status); + } + + device->base.surface.create = radeon_surface_create; + device->base.surface.create_for_name = radeon_surface_create_for_name; + device->base.surface.create_from_cacheable_image = NULL; + device->base.surface.flink = _cairo_drm_surface_flink; + device->base.surface.enable_scan_out = NULL; + + device->base.device.flush = NULL; + device->base.device.throttle = NULL; + device->base.device.destroy = radeon_device_destroy; + + device->vram_limit = vram_size; + device->gart_limit = gart_size; + + return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-radeon.c b/gfx/cairo/cairo/src/drm/cairo-drm-radeon.c new file mode 100644 index 000000000000..8bc91bfe0812 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-radeon.c @@ -0,0 +1,331 @@ +/* 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. + * + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" +#include "cairo-drm-radeon-private.h" + +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" + +#include +#include +#include +#include + +cairo_bool_t +radeon_info (int fd, + uint64_t *gart_size, + uint64_t *vram_size) +{ + struct drm_radeon_gem_info info; + int ret; + + ret = ioctl (fd, DRM_IOCTL_RADEON_GEM_INFO, &info); + if (ret == -1) + return FALSE; + + if (gart_size != NULL) + *gart_size = info.gart_size; + + if (vram_size != NULL) + *vram_size = info.vram_size; + + return TRUE; +} + +void +radeon_bo_write (const radeon_device_t *device, + radeon_bo_t *bo, + unsigned long offset, + unsigned long size, + const void *data) +{ + struct drm_radeon_gem_pwrite pwrite; + int ret; + + memset (&pwrite, 0, sizeof (pwrite)); + pwrite.handle = bo->base.handle; + pwrite.offset = offset; + pwrite.size = size; + pwrite.data_ptr = (uint64_t) (uintptr_t) data; + do { + ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PWRITE, &pwrite); + } while (ret == -1 && errno == EINTR); + + /* XXX temporary workaround */ + if (ret == -1 && errno == ENOSYS) { + uint8_t *ptr; + + ptr = radeon_bo_map (device, bo); + if (ptr != NULL) { + memcpy (ptr + offset, data, size); + radeon_bo_unmap (bo); + } + } +} + +void +radeon_bo_read (const radeon_device_t *device, + radeon_bo_t *bo, + unsigned long offset, + unsigned long size, + void *data) +{ + struct drm_radeon_gem_pread pread; + int ret; + + memset (&pread, 0, sizeof (pread)); + pread.handle = bo->base.handle; + pread.offset = offset; + pread.size = size; + pread.data_ptr = (uint64_t) (uintptr_t) data; + do { + ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_PREAD, &pread); + } while (ret == -1 && errno == EINTR); + + /* XXX temporary workaround */ + if (ret == -1 && errno == ENOSYS) { + uint8_t *ptr; + + ptr = radeon_bo_map (device, bo); + if (ptr != NULL) { + memcpy (data, ptr + offset, size); + radeon_bo_unmap (bo); + } + } + + VG (VALGRIND_MAKE_MEM_DEFINED (data, size)); +} + +void +radeon_bo_wait (const radeon_device_t *device, radeon_bo_t *bo) +{ + struct drm_radeon_gem_wait_idle wait; + int ret; + + wait.handle = bo->base.handle; + do { + ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_WAIT_IDLE, &wait); + } while (ret == -1 && (errno == EINTR || errno == EBUSY)); +} + +void * +radeon_bo_map (const radeon_device_t *device, radeon_bo_t *bo) +{ + struct drm_radeon_gem_mmap mmap_arg; + void *ptr; + int ret; + + assert (bo->virtual == NULL); + + memset (&mmap_arg, 0, sizeof (mmap_arg)); + mmap_arg.handle = bo->base.handle; + mmap_arg.offset = 0; + mmap_arg.size = bo->base.size; + + do { + ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_MMAP, &mmap_arg); + } while (ret == -1 && errno == EINTR); + if (unlikely (ret != 0)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + VG (VALGRIND_MAKE_MEM_DEFINED (&mmap_arg, sizeof (mmap_arg))); + + /* and mmap it */ + ptr = mmap (0, bo->base.size, PROT_READ | PROT_WRITE, + MAP_SHARED, device->base.fd, + mmap_arg.addr_ptr); + if (unlikely (ptr == MAP_FAILED)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + bo->virtual = ptr; + + /* XXX set_domain? */ + return bo->virtual; +} + +void +radeon_bo_unmap (radeon_bo_t *bo) +{ + assert (bo->virtual != NULL); + + munmap (bo->virtual, bo->base.size); + bo->virtual = NULL; +} + +cairo_drm_bo_t * +radeon_bo_create (radeon_device_t *device, + uint32_t size, + uint32_t initial_domain) +{ + struct drm_radeon_gem_create create; + radeon_bo_t *bo; + int ret; + + bo = _cairo_freepool_alloc (&device->bo_pool); + if (unlikely (bo == NULL)) + return NULL; + + create.size = size; + create.alignment = 0; + create.initial_domain = initial_domain; + create.flags = 0; + create.handle = 0; + + do { + ret = ioctl (device->base.fd, DRM_IOCTL_RADEON_GEM_CREATE, &create); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + _cairo_freepool_free (&device->bo_pool, bo); + return NULL; + } + + bo->base.handle = create.handle; + bo->base.size = size; + + bo->virtual = NULL; + + bo->in_batch = FALSE; + bo->read_domains = 0; + bo->write_domain = 0; + + CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); + return &bo->base; +} + +cairo_drm_bo_t * +radeon_bo_create_for_name (radeon_device_t *device, + uint32_t name) +{ + radeon_bo_t *bo; + cairo_status_t status; + + bo = _cairo_freepool_alloc (&device->bo_pool); + if (unlikely (bo == NULL)) + return NULL; + + status = _cairo_drm_bo_open_for_name (&device->base, &bo->base, name); + if (unlikely (status)) { + _cairo_freepool_free (&device->bo_pool, bo); + return NULL; + } + + bo->virtual = NULL; + + bo->in_batch = FALSE; + bo->read_domains = 0; + bo->write_domain = 0; + + CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1); + return &bo->base; +} + +static void +radeon_bo_release (void *_dev, void *_bo) +{ + radeon_device_t *device = _dev; + radeon_bo_t *bo = _bo; + + _cairo_drm_bo_close (&device->base, &bo->base); + _cairo_freepool_free (&device->bo_pool, bo); +} + +cairo_surface_t * +radeon_bo_get_image (const radeon_device_t *device, + radeon_bo_t *bo, + const cairo_drm_surface_t *surface) +{ + cairo_image_surface_t *image; + uint8_t *dst; + int size, row; + + image = (cairo_image_surface_t *) + cairo_image_surface_create (surface->format, + surface->width, + surface->height); + if (unlikely (image->base.status)) + return &image->base; + + if (image->stride == surface->stride) { + size = surface->stride * surface->height; + radeon_bo_read (device, bo, 0, size, image->data); + } else { + int offset; + + size = surface->width; + if (surface->format != CAIRO_FORMAT_A8) + size *= 4; + + offset = 0; + row = surface->height; + dst = image->data; + while (row--) { + radeon_bo_read (device, bo, offset, size, dst); + offset += surface->stride; + dst += image->stride; + } + } + + return &image->base; +} + +static void +_radeon_device_init_bo_cache (radeon_device_t *device) +{ + _cairo_freepool_init (&device->bo_pool, sizeof (radeon_bo_t)); +} + +cairo_status_t +radeon_device_init (radeon_device_t *device, int fd) +{ + _radeon_device_init_bo_cache (device); + + device->base.bo.release = radeon_bo_release; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_radeon_bo_cache_fini (radeon_device_t *device) +{ + _cairo_freepool_fini (&device->bo_pool); +} + +void +radeon_device_fini (radeon_device_t *device) +{ + _radeon_bo_cache_fini (device); + _cairo_drm_device_fini (&device->base); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm-surface.c b/gfx/cairo/cairo/src/drm/cairo-drm-surface.c new file mode 100644 index 000000000000..8c4dd0ee81d3 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-surface.c @@ -0,0 +1,369 @@ +/* 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. + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" + +#include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" + +void +_cairo_drm_surface_init (cairo_drm_surface_t *surface, + cairo_format_t format, + int width, int height) +{ + surface->bo = NULL; + surface->format = format; + surface->width = width; + surface->height = height; + surface->stride = 0; + + surface->fallback = NULL; + surface->map_count = 0; +} + +cairo_status_t +_cairo_drm_surface_finish (cairo_drm_surface_t *surface) +{ + assert (surface->fallback == NULL); + + if (surface->bo != NULL) + cairo_drm_bo_destroy (surface->base.device, surface->bo); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_drm_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); +} + +cairo_bool_t +_cairo_drm_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_drm_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +cairo_surface_t * +cairo_drm_surface_create (cairo_device_t *abstract_device, + cairo_format_t format, + int width, int height) +{ + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + cairo_surface_t *surface; + + if (device != NULL && device->base.status) + { + surface = _cairo_surface_create_in_error (device->base.status); + } + else if (device == NULL || + device->surface.create == NULL || + width == 0 || width > device->max_surface_size || + height == 0 || height > device->max_surface_size) + { + surface = cairo_image_surface_create (format, width, height); + } + else if (device->base.finished) + { + surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + } + else + { + surface = device->surface.create (device, format, width, height); + if (surface->status == CAIRO_STATUS_INVALID_SIZE) + surface = cairo_image_surface_create (format, width, height); + } + + return surface; +} + +cairo_surface_t * +cairo_drm_surface_create_for_name (cairo_device_t *abstract_device, + unsigned int name, + cairo_format_t format, + int width, int height, int stride) +{ + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + cairo_surface_t *surface; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + if (device != NULL && device->base.status) + { + surface = _cairo_surface_create_in_error (device->base.status); + } + else if (device == NULL || device->surface.create_for_name == NULL) + { + /* XXX invalid device! */ + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + else if (width == 0 || width > device->max_surface_size || + height == 0 || height > device->max_surface_size) + { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + else if (device->base.finished) + { + surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + } + else + { + surface = device->surface.create_for_name (device, + name, format, + width, height, stride); + } + + return surface; +} +slim_hidden_def (cairo_drm_surface_create_for_name); + +cairo_surface_t * +cairo_drm_surface_create_from_cacheable_image (cairo_device_t *abstract_device, + cairo_surface_t *surface) +{ + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + + if (surface->status) { + surface = _cairo_surface_create_in_error (surface->status); + } else if (device != NULL && device->base.status) { + surface = _cairo_surface_create_in_error (device->base.status); + } else if (device == NULL || device->surface.create_from_cacheable_image == NULL) { + /* XXX invalid device! */ + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } else if (device->base.finished) { + surface = _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED); + } else { + surface = device->surface.create_from_cacheable_image (device, surface); + } + + return surface; +} + +static cairo_drm_surface_t * +_cairo_surface_as_drm (cairo_surface_t *abstract_surface) +{ + if (unlikely (abstract_surface->status)) + return NULL; + + if (abstract_surface->type != CAIRO_SURFACE_TYPE_DRM) + return NULL; + + return (cairo_drm_surface_t *) abstract_surface; +} + +cairo_status_t +cairo_drm_surface_enable_scan_out (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + cairo_drm_device_t *device; + + surface = _cairo_surface_as_drm (abstract_surface); + if (unlikely (surface == NULL)) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + if (unlikely (surface->base.finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + device = (cairo_drm_device_t *) surface->base.device; + if (device->surface.enable_scan_out == NULL) + return CAIRO_STATUS_SUCCESS; + + if (unlikely (device->base.finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + return device->surface.enable_scan_out (abstract_surface); +} + +unsigned int +cairo_drm_surface_get_handle (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->bo->handle; +} + +cairo_int_status_t +_cairo_drm_surface_flink (void *abstract_surface) +{ + cairo_drm_surface_t *surface = abstract_surface; + + return _cairo_drm_bo_flink ((cairo_drm_device_t *) surface->base.device, + surface->bo); +} + +unsigned int +cairo_drm_surface_get_name (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + cairo_drm_device_t *device; + cairo_status_t status; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + if (surface->bo->name) + return surface->bo->name; + + device = (cairo_drm_device_t *) surface->base.device; + if (device->surface.flink == NULL) + return 0; + + status = device->surface.flink (abstract_surface); + if (status) { + if (_cairo_status_is_error (status)) + status = _cairo_surface_set_error (abstract_surface, status); + + return 0; + } + + return surface->bo->name; +} + +cairo_format_t +cairo_drm_surface_get_format (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) + return cairo_image_surface_get_format (abstract_surface); + + return surface->format; +} + +int +cairo_drm_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) + return cairo_image_surface_get_width (abstract_surface); + + return surface->width; +} + +int +cairo_drm_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) + return cairo_image_surface_get_height (abstract_surface); + + return surface->height; +} + +int +cairo_drm_surface_get_stride (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) + return cairo_image_surface_get_stride (abstract_surface); + + return surface->stride; +} + +/* XXX drm or general surface layer? naming? */ +cairo_surface_t * +cairo_drm_surface_map_to_image (cairo_surface_t *abstract_surface) +{ + cairo_drm_surface_t *surface; + cairo_drm_device_t *device; + cairo_status_t status; + + if (unlikely (abstract_surface->status)) + return _cairo_surface_create_in_error (abstract_surface->status); + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) { + if (_cairo_surface_is_image (abstract_surface)) + return cairo_surface_reference (abstract_surface); + + status = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return _cairo_surface_create_in_error (status); + } + + surface->map_count++; + device = (cairo_drm_device_t *) surface->base.device; + return cairo_surface_reference (device->surface.map_to_image (surface)); +} + +void +cairo_drm_surface_unmap (cairo_surface_t *abstract_surface, + cairo_surface_t *image) +{ + cairo_drm_surface_t *surface; + + surface = _cairo_surface_as_drm (abstract_surface); + if (surface == NULL) { + if (_cairo_surface_is_image (abstract_surface)) + cairo_surface_destroy (image); + else + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + /* XXX assert image belongs to drm */ + //assert (image == drm->fallback); + cairo_surface_destroy (image); + + assert (surface->map_count > 0); + if (--surface->map_count == 0) + cairo_surface_flush (&surface->base); +} diff --git a/gfx/cairo/cairo/src/drm/cairo-drm.c b/gfx/cairo/cairo/src/drm/cairo-drm.c new file mode 100644 index 000000000000..661e181b6287 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm.c @@ -0,0 +1,390 @@ +/* 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. + */ + +#include "cairoint.h" + +#include "cairo-drm-private.h" + +#include "cairo-device-private.h" +#include "cairo-error-private.h" + +#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE +#include +#include +#include /* open(), close() */ + +static cairo_drm_device_t *_cairo_drm_known_devices; +static cairo_drm_device_t *_cairo_drm_default_device; + +static const char * +get_udev_property(struct udev_device *device, const char *name) +{ + struct udev_list_entry *entry; + + udev_list_entry_foreach (entry, + udev_device_get_properties_list_entry (device)) + { + if (strcmp (udev_list_entry_get_name (entry), name) == 0) + return udev_list_entry_get_value (entry); + } + + return NULL; +} + +static void +_device_flush (void *abstract_device) +{ + cairo_drm_device_t *device = abstract_device; + + device->device.flush (device); +} + +static void +_device_finish (void *abstract_device) +{ + cairo_drm_device_t *device = abstract_device; + + CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex); + if (device->prev != NULL) + device->prev->next = device->next; + else + _cairo_drm_known_devices = device->next; + if (device->next != NULL) + device->next->prev = device->prev; + + CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex); + + if (_cairo_atomic_ptr_cmpxchg (&_cairo_drm_default_device, + device, NULL)) + { + cairo_device_destroy (&device->base); + } +} + +static void +_device_destroy (void *abstract_device) +{ + cairo_drm_device_t *device = abstract_device; + + device->device.destroy (device); +} + +static const cairo_device_backend_t _cairo_drm_device_backend = { + CAIRO_DEVICE_TYPE_DRM, + + NULL, NULL, /* lock, unlock */ + + _device_flush, + _device_finish, + _device_destroy, +}; + +cairo_drm_device_t * +_cairo_drm_device_init (cairo_drm_device_t *dev, + int fd, + dev_t devid, + int vendor_id, + int chip_id, + int max_surface_size) +{ + assert (CAIRO_MUTEX_IS_LOCKED (_cairo_drm_device_mutex)); + + _cairo_device_init (&dev->base, &_cairo_drm_device_backend); + + dev->id = devid; + dev->vendor_id = vendor_id; + dev->chip_id = chip_id; + dev->fd = fd; + + dev->max_surface_size = max_surface_size; + + dev->prev = NULL; + dev->next = _cairo_drm_known_devices; + if (_cairo_drm_known_devices != NULL) + _cairo_drm_known_devices->prev = dev; + _cairo_drm_known_devices = dev; + + if (_cairo_drm_default_device == NULL) + _cairo_drm_default_device = (cairo_drm_device_t *) cairo_device_reference (&dev->base); + + return dev; +} + +cairo_device_t * +cairo_drm_device_get (struct udev_device *device) +{ + static const struct dri_driver_entry { + uint32_t vendor_id; + uint32_t chip_id; + cairo_drm_device_create_func_t create_func; + } driver_map[] = { + { 0x8086, 0x29a2, _cairo_drm_i965_device_create }, /* I965_G */ + { 0x8086, 0x2982, _cairo_drm_i965_device_create }, /* G35_G */ + { 0x8086, 0x2992, _cairo_drm_i965_device_create }, /* I965_Q */ + { 0x8086, 0x2972, _cairo_drm_i965_device_create }, /* I946_GZ */ + { 0x8086, 0x2a02, _cairo_drm_i965_device_create }, /* I965_GM */ + { 0x8086, 0x2a12, _cairo_drm_i965_device_create }, /* I965_GME */ + { 0x8086, 0x2e02, _cairo_drm_i965_device_create }, /* IGD_E_G */ + { 0x8086, 0x2e22, _cairo_drm_i965_device_create }, /* G45_G */ + { 0x8086, 0x2e12, _cairo_drm_i965_device_create }, /* Q45_G */ + { 0x8086, 0x2e32, _cairo_drm_i965_device_create }, /* G41_G */ + { 0x8086, 0x2a42, _cairo_drm_i965_device_create }, /* GM45_GM */ + + { 0x8086, 0x2582, _cairo_drm_i915_device_create }, /* I915_G */ + { 0x8086, 0x2592, _cairo_drm_i915_device_create }, /* I915_GM */ + { 0x8086, 0x258a, _cairo_drm_i915_device_create }, /* E7221_G */ + { 0x8086, 0x2772, _cairo_drm_i915_device_create }, /* I945_G */ + { 0x8086, 0x27a2, _cairo_drm_i915_device_create }, /* I945_GM */ + { 0x8086, 0x27ae, _cairo_drm_i915_device_create }, /* I945_GME */ + { 0x8086, 0x29c2, _cairo_drm_i915_device_create }, /* G33_G */ + { 0x8086, 0x29b2, _cairo_drm_i915_device_create }, /* Q35_G */ + { 0x8086, 0x29d2, _cairo_drm_i915_device_create }, /* Q33_G */ + { 0x8086, 0xa011, _cairo_drm_i915_device_create }, /* IGD_GM */ + { 0x8086, 0xa001, _cairo_drm_i915_device_create }, /* IGD_G */ + + /* XXX i830 */ + + { 0x8086, ~0, _cairo_drm_intel_device_create }, + + { 0x1002, ~0, _cairo_drm_radeon_device_create }, +#if CAIRO_HAS_GALLIUM_SURFACE + { ~0, ~0, _cairo_drm_gallium_device_create }, +#endif + }; + + cairo_drm_device_t *dev; + dev_t devid; + struct udev_device *parent; + const char *pci_id; + uint32_t vendor_id, chip_id; + const char *path; + int i, fd; + + devid = udev_device_get_devnum (device); + + CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex); + for (dev = _cairo_drm_known_devices; dev != NULL; dev = dev->next) { + if (dev->id == devid) { + dev = (cairo_drm_device_t *) cairo_device_reference (&dev->base); + goto DONE; + } + } + + parent = udev_device_get_parent (device); + pci_id = get_udev_property (parent, "PCI_ID"); + if (pci_id == NULL || sscanf (pci_id, "%x:%x", &vendor_id, &chip_id) != 2) { + dev = NULL; + goto DONE; + } + +#if CAIRO_HAS_GALLIUM_SURFACE + if (getenv ("CAIRO_GALLIUM_FORCE")) + { + i = ARRAY_LENGTH (driver_map) - 1; + } + else +#endif + { + for (i = 0; i < ARRAY_LENGTH (driver_map); i++) { + if (driver_map[i].vendor_id == ~0U) + break; + + if (driver_map[i].vendor_id == vendor_id && + (driver_map[i].chip_id == ~0U || driver_map[i].chip_id == chip_id)) + break; + } + + if (i == ARRAY_LENGTH (driver_map)) { + dev = (cairo_drm_device_t *) + _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR); + goto DONE; + } + } + + path = udev_device_get_devnode (device); + if (path == NULL) + path = "/dev/dri/card0"; /* XXX buggy udev? */ + + fd = open (path, O_RDWR); + if (fd == -1) { + /* XXX more likely to be a permissions issue... */ + _cairo_error_throw (CAIRO_STATUS_FILE_NOT_FOUND); + dev = NULL; + goto DONE; + } + + dev = driver_map[i].create_func (fd, devid, vendor_id, chip_id); + if (dev == NULL) + close (fd); + + DONE: + CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex); + + if (dev == NULL) + return _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR); + else + return &dev->base; +} +slim_hidden_def (cairo_drm_device_get); + +cairo_device_t * +cairo_drm_device_get_for_fd (int fd) +{ + struct stat st; + struct udev *udev; + struct udev_device *device; + cairo_device_t *dev = NULL; + + if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode)) { + //_cairo_error_throw (CAIRO_STATUS_INVALID_DEVICE); + return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + udev = udev_new (); + + device = udev_device_new_from_devnum (udev, 'c', st.st_rdev); + if (device != NULL) { + dev = cairo_drm_device_get (device); + udev_device_unref (device); + } + + udev_unref (udev); + + return dev; +} +slim_hidden_def (cairo_drm_device_get_for_fd); + +cairo_device_t * +cairo_drm_device_default (void) +{ + struct udev *udev; + struct udev_enumerate *e; + struct udev_list_entry *entry; + cairo_device_t *dev; + + /* optimistic atomic pointer read */ + dev = &_cairo_drm_default_device->base; + if (dev != NULL) + return dev; + + udev = udev_new(); + if (udev == NULL) + return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + e = udev_enumerate_new (udev); + udev_enumerate_add_match_subsystem (e, "drm"); + udev_enumerate_scan_devices (e); + udev_list_entry_foreach (entry, udev_enumerate_get_list_entry (e)) { + struct udev_device *device; + + device = + udev_device_new_from_syspath (udev, + udev_list_entry_get_name (entry)); + + dev = cairo_drm_device_get (device); + + udev_device_unref (device); + + if (dev != NULL) { + if (((cairo_drm_device_t *) dev)->fd == -1) { + /* try again, we may find a usable card */ + cairo_device_destroy (dev); + dev = NULL; + } else + break; + } + } + udev_enumerate_unref (e); + udev_unref (udev); + + cairo_device_destroy (dev); /* owned by _cairo_drm_default_device */ + return dev; +} +slim_hidden_def (cairo_drm_device_default); + +void +_cairo_drm_device_reset_static_data (void) +{ + if (_cairo_drm_default_device != NULL) { + cairo_device_t *device = &_cairo_drm_default_device->base; + _cairo_drm_default_device = NULL; + cairo_device_destroy (device); + } +} + +int +cairo_drm_device_get_fd (cairo_device_t *abstract_device) +{ + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + + if (device->base.status) + return -1; + + return device->fd; +} + +void +_cairo_drm_device_fini (cairo_drm_device_t *device) +{ + if (device->fd != -1) + close (device->fd); +} + +void +cairo_drm_device_throttle (cairo_device_t *abstract_device) +{ + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + cairo_status_t status; + + if (unlikely (device->base.status)) + return; + + if (device->device.throttle == NULL) + return; + + status = device->device.throttle (device); + if (unlikely (status)) + _cairo_status_set_error (&device->base.status, status); +} + +cairo_bool_t +_cairo_drm_size_is_valid (cairo_device_t *abstract_device, + int width, int height) +{ + cairo_drm_device_t *device = (cairo_drm_device_t *) abstract_device; + + if (unlikely (device->base.status)) + return FALSE; + + return width <= device->max_surface_size && + height <= device->max_surface_size; +} diff --git a/gfx/cairo/cairo/src/filterpublic.awk b/gfx/cairo/cairo/src/filterpublic.awk deleted file mode 100644 index 98846102b262..000000000000 --- a/gfx/cairo/cairo/src/filterpublic.awk +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/awk -f -# Reads cairo header files on stdin, and outputs a file with defines for -# renaming all public functions to Mozilla-specific names. -# Usage: -# cat *.h | awk -f ./filterpublic.awk | sort > cairo-rename.h -# -# pixman: -# grep '(' ../../libpixman/src/pixman.h | grep '^[a-z]' | sed 's, *(.*$,,' | sed 's,^.* ,,' - -BEGIN { state = "public"; } - -/^cairo_public/ { state = "function"; next; } -/[a-zA-Z_]+/ { - if (state == "function") { - print "#define " $1 " _moz_" $1; - state = "public"; - } - } - -# catch some one-off things -END { -} diff --git a/gfx/cairo/cairo/src/meson.build b/gfx/cairo/cairo/src/meson.build new file mode 100644 index 000000000000..f5d741eaeeee --- /dev/null +++ b/gfx/cairo/cairo/src/meson.build @@ -0,0 +1,321 @@ +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', + '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', + 'cairo-pdf-operators.c', + 'cairo-pdf-shading.c', + 'cairo-tag-attributes.c', + 'cairo-deflate-stream.c', +] + +cairo_headers = [ + 'cairo.h', + 'cairo-version.h', + 'cairo-deprecated.h', +] + +cairo_feature_sources = { + 'cairo-png': [ + 'cairo-png.c', + ], + 'cairo-ft': [ + 'cairo-ft-font.c', + ], + + 'cairo-xlib': [ + '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', + ], + 'cairo-xcb': [ + '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', + ], + 'cairo-qt': [ + 'cairo-qt-surface.cpp', + ], + 'cairo-quartz': [ + 'cairo-quartz-surface.c', + ], + 'cairo-quartz-image': [ + 'cairo-quartz-image-surface.c', + ], + 'cairo-quartz-font': [ + 'cairo-quartz-font.c', + ], + 'cairo-win32': [ + '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', + ], + 'cairo-win32-font': [ + 'win32/cairo-win32-font.c', + ], + 'cairo-drm': [ + 'drm/cairo-drm.c', + 'drm/cairo-drm-bo.c', + 'drm/cairo-drm-surface.c', + 'drm/cairo-drm-intel.c', + 'drm/cairo-drm-intel-debug.c', + 'drm/cairo-drm-intel-surface.c', + 'drm/cairo-drm-i915-surface.c', + 'drm/cairo-drm-i915-glyphs.c', + 'drm/cairo-drm-i915-shader.c', + 'drm/cairo-drm-i915-spans.c', + 'drm/cairo-drm-i965-surface.c', + 'drm/cairo-drm-i965-glyphs.c', + 'drm/cairo-drm-i965-shader.c', + 'drm/cairo-drm-i965-spans.c', + 'drm/cairo-drm-intel-brw-eu.c', + 'drm/cairo-drm-intel-brw-eu-emit.c', + 'drm/cairo-drm-intel-brw-eu-util.c', + 'drm/cairo-drm-radeon.c', + 'drm/cairo-drm-radeon-surface.c', + ], + '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-cogl': [ + 'cairo-cogl-surface.c', + 'cairo-cogl-gradient.c', + ], + 'cairo-directfb': [ + 'cairo-directfb-surface.c', + ], + 'cairo-vg': [ + 'cairo-vg-surface.c', + ], + 'cairo-script': [ + 'cairo-script-surface.c', + ], + 'cairo-ps': [ + 'cairo-ps-surface.c', + ], + 'cairo-pdf': [ + 'cairo-pdf-surface.c', + 'cairo-pdf-interchange.c', + 'cairo-tag-stack.c', + ], + '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', + ], +} + +cairo_feature_headers = { + 'cairo-ps': ['cairo-ps.h'], + 'cairo-pdf': ['cairo-pdf.h'], + 'cairo-svg': ['cairo-svg.h'], + 'cairo-ft': ['cairo-ft.h'], + 'cairo-xlib': ['cairo-xlib.h'], + 'cairo-xlib-xrender': ['cairo-xlib-xrender.h'], + 'cairo-xcb': ['cairo-xcb.h'], + 'cairo-qt': ['cairo-qt.h'], + 'cairo-quartz': ['cairo-quartz.h'], + 'cairo-quartz-image': ['cairo-quartz-image.h'], + 'cairo-win32': ['cairo-win32.h'], + 'cairo-gl': ['cairo-gl.h'], + 'cairo-directfb': ['cairo-directfb.h'], + 'cairo-drm': ['cairo-drm.h'], + 'cairo-script': ['cairo-script.h'], + 'cairo-tee': ['cairo-tee.h'], + 'cairo-xml': ['cairo-xml.h'], + 'cairo-vg': ['cairo-vg.h'], + 'cairo-cogl': ['cairo-cogl.h'], +} + +cairo_no_warn_c_args = cc.get_supported_arguments([ + '-Wno-attributes', + '-Wno-unused-but-set-variable', + '-Wno-missing-field-initializers', + '-Wno-unused-parameter', + '-Wno-long-long', +]) + +foreach feature: built_features + source_key = feature.get('source-key', feature.get('name')) + cairo_sources += cairo_feature_sources.get(source_key, []) + cairo_headers += cairo_feature_headers.get(source_key, []) +endforeach + +incsrc = include_directories('.') + +libcairo = library('cairo', cairo_sources, + dependencies: deps, + c_args: cairo_no_warn_c_args + pthread_c_args + ['-DHAVE_CONFIG_H'], + cpp_args: cairo_no_warn_c_args + pthread_c_args + ['-DHAVE_CONFIG_H'], + link_args: extra_link_args, + soversion: cairo_version_sonum, + version: cairo_libversion, + install: true, + include_directories: incbase, +) + +cairo_headers += [configure_file(output: 'cairo-features.h', configuration: feature_conf)] + +libcairo_dep = declare_dependency(link_with: libcairo, + dependencies: deps, + include_directories: incsrc) + +pkgmod.generate(libcairo, + description: 'Multi-platform 2D graphics library', + subdirs: [meson.project_name()], +) + +meson.override_dependency('cairo', libcairo_dep) + +install_headers(cairo_headers, subdir: 'cairo') diff --git a/gfx/cairo/cairo/src/test-base-compositor-surface.c b/gfx/cairo/cairo/src/test-base-compositor-surface.c new file mode 100644 index 000000000000..ff84b10aff71 --- /dev/null +++ b/gfx/cairo/cairo/src/test-base-compositor-surface.c @@ -0,0 +1,824 @@ +/* -*- 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 + * + * 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): + * Carl D. Worth + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "test-compositor-surface-private.h" + +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-compositor-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-region-private.h" +#include "cairo-traps-private.h" + +/* The intention is that this is a surface that just works, and most + * important of all does not try to be clever! + */ + +typedef cairo_int_status_t +(*draw_func_t) (cairo_image_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents); + +static pixman_op_t +_pixman_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_CLEAR: + return PIXMAN_OP_CLEAR; + + case CAIRO_OPERATOR_SOURCE: + return PIXMAN_OP_SRC; + case CAIRO_OPERATOR_OVER: + return PIXMAN_OP_OVER; + case CAIRO_OPERATOR_IN: + return PIXMAN_OP_IN; + case CAIRO_OPERATOR_OUT: + return PIXMAN_OP_OUT; + case CAIRO_OPERATOR_ATOP: + return PIXMAN_OP_ATOP; + + case CAIRO_OPERATOR_DEST: + return PIXMAN_OP_DST; + case CAIRO_OPERATOR_DEST_OVER: + return PIXMAN_OP_OVER_REVERSE; + case CAIRO_OPERATOR_DEST_IN: + return PIXMAN_OP_IN_REVERSE; + case CAIRO_OPERATOR_DEST_OUT: + return PIXMAN_OP_OUT_REVERSE; + case CAIRO_OPERATOR_DEST_ATOP: + return PIXMAN_OP_ATOP_REVERSE; + + case CAIRO_OPERATOR_XOR: + return PIXMAN_OP_XOR; + case CAIRO_OPERATOR_ADD: + return PIXMAN_OP_ADD; + case CAIRO_OPERATOR_SATURATE: + return PIXMAN_OP_SATURATE; + + case CAIRO_OPERATOR_MULTIPLY: + return PIXMAN_OP_MULTIPLY; + case CAIRO_OPERATOR_SCREEN: + return PIXMAN_OP_SCREEN; + case CAIRO_OPERATOR_OVERLAY: + return PIXMAN_OP_OVERLAY; + case CAIRO_OPERATOR_DARKEN: + return PIXMAN_OP_DARKEN; + case CAIRO_OPERATOR_LIGHTEN: + return PIXMAN_OP_LIGHTEN; + case CAIRO_OPERATOR_COLOR_DODGE: + return PIXMAN_OP_COLOR_DODGE; + case CAIRO_OPERATOR_COLOR_BURN: + return PIXMAN_OP_COLOR_BURN; + case CAIRO_OPERATOR_HARD_LIGHT: + return PIXMAN_OP_HARD_LIGHT; + case CAIRO_OPERATOR_SOFT_LIGHT: + return PIXMAN_OP_SOFT_LIGHT; + case CAIRO_OPERATOR_DIFFERENCE: + return PIXMAN_OP_DIFFERENCE; + case CAIRO_OPERATOR_EXCLUSION: + return PIXMAN_OP_EXCLUSION; + case CAIRO_OPERATOR_HSL_HUE: + return PIXMAN_OP_HSL_HUE; + case CAIRO_OPERATOR_HSL_SATURATION: + return PIXMAN_OP_HSL_SATURATION; + case CAIRO_OPERATOR_HSL_COLOR: + return PIXMAN_OP_HSL_COLOR; + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return PIXMAN_OP_HSL_LUMINOSITY; + + default: + ASSERT_NOT_REACHED; + return PIXMAN_OP_OVER; + } +} + +static cairo_image_surface_t * +create_composite_mask (cairo_image_surface_t *dst, + void *draw_closure, + draw_func_t draw_func, + const cairo_composite_rectangles_t *extents) +{ + cairo_image_surface_t *surface; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + surface = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, PIXMAN_a8, + extents->bounded.width, + extents->bounded.height, + 0); + if (unlikely (surface->base.status)) + return surface; + + status = draw_func (surface, draw_closure, + CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base, + extents->bounded.x, extents->bounded.y, + &extents->bounded); + if (unlikely (status)) + goto error; + + status = _cairo_clip_combine_with_surface (extents->clip, + &surface->base, + extents->bounded.x, + extents->bounded.y); + if (unlikely (status)) + goto error; + + return surface; + +error: + cairo_surface_destroy (&surface->base); + return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); +} + +/* Handles compositing with a clip surface when the operator allows + * us to combine the clip with the mask + */ +static cairo_status_t +clip_and_composite_with_mask (const cairo_composite_rectangles_t*extents, + draw_func_t draw_func, + void *draw_closure) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface; + cairo_image_surface_t *mask; + pixman_image_t *src; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int src_x, src_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + mask = create_composite_mask (dst, draw_closure, draw_func, extents); + if (unlikely (mask->base.status)) + return mask->base.status; + + src = _pixman_image_for_pattern (dst, + &extents->source_pattern.base, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error; + } + + pixman_image_composite32 (_pixman_operator (extents->op), + src, mask->pixman_image, dst->pixman_image, + extents->bounded.x + src_x, + extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + pixman_image_unref (src); +error: + cairo_surface_destroy (&mask->base); + return status; +} + +/* Handles compositing with a clip surface when we have to do the operation + * in two pieces and combine them together. + */ +static cairo_status_t +clip_and_composite_combine (const cairo_composite_rectangles_t*extents, + draw_func_t draw_func, + void *draw_closure) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface; + cairo_image_surface_t *tmp, *clip; + int clip_x, clip_y; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + tmp = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + dst->pixman_format, + extents->bounded.width, + extents->bounded.height, + 0); + if (unlikely (tmp->base.status)) + return tmp->base.status; + + pixman_image_composite32 (PIXMAN_OP_SRC, + dst->pixman_image, NULL, tmp->pixman_image, + extents->bounded.x, extents->bounded.y, + 0, 0, + 0, 0, + extents->bounded.width, extents->bounded.height); + + status = draw_func (tmp, draw_closure, + extents->op, &extents->source_pattern.base, + extents->bounded.x, extents->bounded.y, + &extents->bounded); + if (unlikely (status)) + goto error; + + clip = (cairo_image_surface_t *) + _cairo_clip_get_surface (extents->clip, &dst->base, &clip_x, &clip_y); + if (unlikely (clip->base.status)) + goto error; + + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + clip->pixman_image, NULL, dst->pixman_image, + extents->bounded.x - clip_x, extents->bounded.y - clip_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + pixman_image_composite32 (PIXMAN_OP_ADD, + tmp->pixman_image, clip->pixman_image, dst->pixman_image, + 0, 0, + extents->bounded.x - clip_x, extents->bounded.y - clip_y, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + cairo_surface_destroy (&clip->base); + + error: + cairo_surface_destroy (&tmp->base); + + return status; +} + +/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's + * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) + */ +static cairo_status_t +clip_and_composite_source (const cairo_composite_rectangles_t *extents, + draw_func_t draw_func, + void *draw_closure) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface; + cairo_image_surface_t *mask; + pixman_image_t *src; + int src_x, src_y; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + mask = create_composite_mask (dst, draw_closure, draw_func, extents); + if (unlikely (mask->base.status)) + return mask->base.status; + + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask->pixman_image, NULL, dst->pixman_image, + 0, 0, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + src = _pixman_image_for_pattern (dst, + &extents->source_pattern.base, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (unlikely (src == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error; + } + + pixman_image_composite32 (PIXMAN_OP_ADD, + src, mask->pixman_image, dst->pixman_image, + extents->bounded.x + src_x, extents->bounded.y + src_y, + 0, 0, + extents->bounded.x, extents->bounded.y, + extents->bounded.width, extents->bounded.height); + + pixman_image_unref (src); + +error: + cairo_surface_destroy (&mask->base); + return status; +} + +static cairo_status_t +fixup_unbounded (const cairo_composite_rectangles_t *extents) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface; + pixman_image_t *mask; + int mask_x, mask_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if (! _cairo_clip_is_region (extents->clip)) { + cairo_image_surface_t *clip; + + clip = (cairo_image_surface_t *) + _cairo_clip_get_surface (extents->clip, &dst->base, + &mask_x, &mask_y); + if (unlikely (clip->base.status)) + return clip->base.status; + + mask = pixman_image_ref (clip->pixman_image); + cairo_surface_destroy (&clip->base); + } else { + mask_x = mask_y = 0; + mask = _pixman_image_for_color (CAIRO_COLOR_WHITE); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* top */ + if (extents->bounded.y != extents->unbounded.y) { + int x = extents->unbounded.x; + int y = extents->unbounded.y; + int width = extents->unbounded.width; + int height = extents->bounded.y - y; + + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + /* left */ + if (extents->bounded.x != extents->unbounded.x) { + int x = extents->unbounded.x; + int y = extents->bounded.y; + int width = extents->bounded.x - x; + int height = extents->bounded.height; + + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + /* right */ + if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) { + int x = extents->bounded.x + extents->bounded.width; + int y = extents->bounded.y; + int width = extents->unbounded.x + extents->unbounded.width - x; + int height = extents->bounded.height; + + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + /* bottom */ + if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) { + int x = extents->unbounded.x; + int y = extents->bounded.y + extents->bounded.height; + int width = extents->unbounded.width; + int height = extents->unbounded.y + extents->unbounded.height - y; + + pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, + mask, NULL, dst->pixman_image, + x - mask_x, y - mask_y, + 0, 0, + x, y, + width, height); + } + + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +set_clip_region (cairo_composite_rectangles_t *extents) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *) extents->surface; + cairo_region_t *region = _cairo_clip_get_region (extents->clip); + pixman_region32_t *rgn = region ? ®ion->rgn : NULL; + if (! pixman_image_set_clip_region32 (dst->pixman_image, rgn)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +clip_and_composite (cairo_composite_rectangles_t *extents, + draw_func_t draw_func, + void *draw_closure) +{ + cairo_status_t status; + + status = set_clip_region (extents); + if (unlikely (status)) + return status; + + if (extents->op == CAIRO_OPERATOR_SOURCE) { + status = clip_and_composite_source (extents, draw_func, draw_closure); + } else { + if (extents->op == CAIRO_OPERATOR_CLEAR) { + extents->source_pattern.solid = _cairo_pattern_white; + extents->op = CAIRO_OPERATOR_DEST_OUT; + } + if (! _cairo_clip_is_region (extents->clip)) { + if (extents->is_bounded) + status = clip_and_composite_with_mask (extents, draw_func, draw_closure); + else + status = clip_and_composite_combine (extents, draw_func, draw_closure); + } else { + status = draw_func ((cairo_image_surface_t *) extents->surface, + draw_closure, + extents->op, + &extents->source_pattern.base, + 0, 0, + &extents->bounded); + } + } + + if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) + status = fixup_unbounded (extents); + + return status; +} + +/* high-level compositor interface */ + +static cairo_int_status_t +composite_paint (cairo_image_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents) +{ + cairo_rectangle_int_t sample; + pixman_image_t *src; + int src_x, src_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + _cairo_pattern_sampled_area (pattern, extents, &sample); + src = _pixman_image_for_pattern (dst, + pattern, FALSE, + extents, &sample, + &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + TRACE ((stderr, "%s: src=(%d, %d), dst=(%d, %d) size=%dx%d\n", __FUNCTION__, + extents->x + src_x, extents->y + src_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height)); + + pixman_image_composite32 (_pixman_operator (op), + src, NULL, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (src); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +base_compositor_paint (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + TRACE ((stderr, "%s\n", __FUNCTION__)); + return clip_and_composite (extents, composite_paint, NULL); +} + +static cairo_int_status_t +composite_mask (cairo_image_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents) +{ + cairo_rectangle_int_t sample; + pixman_image_t *src, *mask; + int src_x, src_y; + int mask_x, mask_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + _cairo_pattern_sampled_area (pattern, extents, &sample); + src = _pixman_image_for_pattern (dst, pattern, FALSE, + extents, &sample, + &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_pattern_sampled_area (closure, extents, &sample); + mask = _pixman_image_for_pattern (dst, closure, TRUE, + extents, &sample, + &mask_x, &mask_y); + if (unlikely (mask == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + extents->x + src_x, extents->y + src_y, + extents->x + mask_x, extents->y + mask_y, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (mask); + pixman_image_unref (src); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +base_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + TRACE ((stderr, "%s\n", __FUNCTION__)); + return clip_and_composite (extents, composite_mask, &extents->mask_pattern.base); +} + +typedef struct { + cairo_traps_t traps; + cairo_antialias_t antialias; +} composite_traps_info_t; + +static cairo_int_status_t +composite_traps (cairo_image_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents) +{ + composite_traps_info_t *info = closure; + cairo_rectangle_int_t sample; + pixman_image_t *src, *mask; + int src_x, src_y; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + _cairo_pattern_sampled_area (pattern, extents, &sample); + src = _pixman_image_for_pattern (dst, pattern, FALSE, + extents, &sample, + &src_x, &src_y); + if (unlikely (src == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + mask = pixman_image_create_bits (info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) { + pixman_image_unref (src); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _pixman_image_add_traps (mask, extents->x, extents->y, &info->traps); + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + extents->x + src_x - dst_x, extents->y + src_y - dst_y, + 0, 0, + extents->x - dst_x, extents->y - dst_y, + extents->width, extents->height); + + pixman_image_unref (mask); + pixman_image_unref (src); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +trim_extents_to_traps (cairo_composite_rectangles_t *extents, + cairo_traps_t *traps) +{ + cairo_box_t box; + + /* X trims the affected area to the extents of the trapezoids, so + * we need to compensate when fixing up the unbounded area. + */ + _cairo_traps_extents (traps, &box); + return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); +} + +static cairo_int_status_t +base_compositor_stroke (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + 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) +{ + composite_traps_info_t info; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + info.antialias = antialias; + _cairo_traps_init_with_clip (&info.traps, extents->clip); + status = _cairo_path_fixed_stroke_polygon_to_traps (path, style, + ctm, ctm_inverse, + tolerance, + &info.traps); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = trim_extents_to_traps (extents, &info.traps); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite (extents, composite_traps, &info); + _cairo_traps_fini (&info.traps); + + return status; +} + +static cairo_int_status_t +base_compositor_fill (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + composite_traps_info_t info; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + info.antialias = antialias; + _cairo_traps_init_with_clip (&info.traps, extents->clip); + status = _cairo_path_fixed_fill_to_traps (path, + fill_rule, tolerance, + &info.traps); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = trim_extents_to_traps (extents, &info.traps); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite (extents, composite_traps, &info); + _cairo_traps_fini (&info.traps); + + return status; +} + +static cairo_int_status_t +composite_glyphs (cairo_image_surface_t *dst, + void *closure, + cairo_operator_t op, + const cairo_pattern_t *pattern, + int dst_x, + int dst_y, + const cairo_rectangle_int_t *extents) +{ + cairo_composite_glyphs_info_t *info = closure; + pixman_image_t *mask; + cairo_status_t status; + int i; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + mask = pixman_image_create_bits (PIXMAN_a8, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = CAIRO_STATUS_SUCCESS; + _cairo_scaled_font_freeze_cache (info->font); + for (i = 0; i < info->num_glyphs; i++) { + cairo_image_surface_t *glyph_surface; + cairo_scaled_glyph_t *scaled_glyph; + unsigned long glyph_index = info->glyphs[i].index; + int x, y; + + status = _cairo_scaled_glyph_lookup (info->font, glyph_index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + break; + + glyph_surface = scaled_glyph->surface; + if (glyph_surface->width && glyph_surface->height) { + /* round glyph locations to the nearest pixel */ + /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */ + x = _cairo_lround (info->glyphs[i].x - + glyph_surface->base.device_transform.x0); + y = _cairo_lround (info->glyphs[i].y - + glyph_surface->base.device_transform.y0); + + pixman_image_composite32 (PIXMAN_OP_ADD, + glyph_surface->pixman_image, NULL, mask, + 0, 0, + 0, 0, + x - extents->x, y - extents->y, + glyph_surface->width, + glyph_surface->height); + } + } + _cairo_scaled_font_thaw_cache (info->font); + + if (status == CAIRO_STATUS_SUCCESS) { + cairo_rectangle_int_t sample; + pixman_image_t *src; + int src_x, src_y; + + _cairo_pattern_sampled_area (pattern, extents, &sample); + src = _pixman_image_for_pattern (dst, pattern, FALSE, + extents, &sample, + &src_x, &src_y); + if (src != NULL) { + dst_x = extents->x - dst_x; + dst_y = extents->y - dst_y; + pixman_image_composite32 (_pixman_operator (op), + src, mask, dst->pixman_image, + src_x + dst_x, src_y + dst_y, + 0, 0, + dst_x, dst_y, + extents->width, extents->height); + pixman_image_unref (src); + } else + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + pixman_image_unref (mask); + + return status; +} + +static cairo_int_status_t +base_compositor_glyphs (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_bool_t overlap) +{ + cairo_composite_glyphs_info_t info; + + info.font = scaled_font; + info.glyphs = glyphs; + info.num_glyphs = num_glyphs; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + return clip_and_composite (extents, composite_glyphs, &info); +} + +static const cairo_compositor_t base_compositor = { + &__cairo_no_compositor, + + base_compositor_paint, + base_compositor_mask, + base_compositor_stroke, + base_compositor_fill, + base_compositor_glyphs, +}; + +cairo_surface_t * +_cairo_test_base_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (&base_compositor, + content, width, height); +} diff --git a/gfx/cairo/cairo/src/test-meta-surface.h b/gfx/cairo/cairo/src/test-compositor-surface-private.h similarity index 67% rename from gfx/cairo/cairo/src/test-meta-surface.h rename to gfx/cairo/cairo/src/test-compositor-surface-private.h index c036bb9c89c1..491f241bacc3 100644 --- a/gfx/cairo/cairo/src/test-meta-surface.h +++ b/gfx/cairo/cairo/src/test-compositor-surface-private.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2005 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 @@ -12,7 +12,7 @@ * * 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 + * 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 * @@ -27,24 +27,30 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Red Hat, Inc. + * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): - * Carl Worth + * Chris Wilson */ -#ifndef TEST_META_SURFACE_H -#define TEST_META_SURFACE_H +#ifndef TEST_COMPOSITOR_SURFACE_PRIVATE_H +#define TEST_COMPOSITOR_SURFACE_PRIVATE_H #include "cairo.h" +#include "test-compositor-surface.h" + +#include "cairo-compiler-private.h" +#include "cairo-compositor-private.h" + CAIRO_BEGIN_DECLS -cairo_surface_t * -_cairo_test_meta_surface_create (cairo_content_t content, - int width, - int height); +cairo_private cairo_surface_t * +test_compositor_surface_create (const cairo_compositor_t *compositor, + cairo_content_t content, + int width, + int height); CAIRO_END_DECLS -#endif /* TEST_META_SURFACE_H */ +#endif /* TEST_COMPOSITOR_SURFACE_PRIVATE H */ diff --git a/gfx/cairo/cairo/src/test-compositor-surface.c b/gfx/cairo/cairo/src/test-compositor-surface.c new file mode 100644 index 000000000000..d6e04a122c25 --- /dev/null +++ b/gfx/cairo/cairo/src/test-compositor-surface.c @@ -0,0 +1,265 @@ +/* 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "test-compositor-surface-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" + +typedef struct _test_compositor_surface { + cairo_image_surface_t base; +} test_compositor_surface_t; + +static const cairo_surface_backend_t test_compositor_surface_backend; + +cairo_surface_t * +test_compositor_surface_create (const cairo_compositor_t *compositor, + cairo_content_t content, + int width, + int height) +{ + test_compositor_surface_t *surface; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + + switch (content) { + case CAIRO_CONTENT_ALPHA: + pixman_format = PIXMAN_a8; + break; + case CAIRO_CONTENT_COLOR: + pixman_format = PIXMAN_x8r8g8b8; + break; + case CAIRO_CONTENT_COLOR_ALPHA: + pixman_format = PIXMAN_a8r8g8b8; + break; + default: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + } + + pixman_image = pixman_image_create_bits (pixman_format, width, height, + NULL, 0); + if (unlikely (pixman_image == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _cairo_malloc (sizeof (test_compositor_surface_t)); + if (unlikely (surface == NULL)) { + pixman_image_unref (pixman_image); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base.base, + &test_compositor_surface_backend, + NULL, /* device */ + content, + FALSE); /* is_vector */ + _cairo_image_surface_init (&surface->base, pixman_image, pixman_format); + + surface->base.compositor = compositor; + + return &surface->base.base; +} + +static cairo_surface_t * +test_compositor_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + test_compositor_surface_t *surface = abstract_surface; + + return test_compositor_surface_create (surface->base.compositor, + content, width, height); +} + +static cairo_int_status_t +test_compositor_surface_paint (void *_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_paint (surface->base.compositor, + _surface, op, source, + clip); +} + +static cairo_int_status_t +test_compositor_surface_mask (void *_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_mask (surface->base.compositor, + _surface, op, source, mask, + clip); +} + +static cairo_int_status_t +test_compositor_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) +{ + test_compositor_surface_t *surface = _surface; + if (antialias == CAIRO_ANTIALIAS_DEFAULT) + antialias = CAIRO_ANTIALIAS_BEST; + return _cairo_compositor_stroke (surface->base.compositor, + _surface, op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +test_compositor_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) +{ + test_compositor_surface_t *surface = _surface; + if (antialias == CAIRO_ANTIALIAS_DEFAULT) + antialias = CAIRO_ANTIALIAS_BEST; + return _cairo_compositor_fill (surface->base.compositor, + _surface, op, source, + path, fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +test_compositor_surface_glyphs (void *_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) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_glyphs (surface->base.compositor, + _surface, op, source, + glyphs, num_glyphs, scaled_font, + clip); +} + +static const cairo_surface_backend_t test_compositor_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_image_surface_finish, + _cairo_default_context_create, + + test_compositor_surface_create_similar, + NULL, /* create similar image */ + _cairo_image_surface_map_to_image, + _cairo_image_surface_unmap_image, + + _cairo_image_surface_source, + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + _cairo_image_surface_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_image_surface_get_extents, + _cairo_image_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + test_compositor_surface_paint, + test_compositor_surface_mask, + test_compositor_surface_stroke, + test_compositor_surface_fill, + NULL, /* fill/stroke */ + test_compositor_surface_glyphs, +}; + +static const cairo_compositor_t * +get_fallback_compositor (void) +{ + return &_cairo_fallback_compositor; +} + +cairo_surface_t * +_cairo_test_fallback_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (get_fallback_compositor(), + content, width, height); +} + +cairo_surface_t * +_cairo_test_mask_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (_cairo_image_mask_compositor_get(), + content, width, height); +} + +cairo_surface_t * +_cairo_test_traps_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (_cairo_image_traps_compositor_get(), + content, width, height); +} + +cairo_surface_t * +_cairo_test_spans_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (_cairo_image_spans_compositor_get(), + content, width, height); +} diff --git a/gfx/cairo/cairo/src/test-compositor-surface.h b/gfx/cairo/cairo/src/test-compositor-surface.h new file mode 100644 index 000000000000..8d8af2d5409b --- /dev/null +++ b/gfx/cairo/cairo/src/test-compositor-surface.h @@ -0,0 +1,71 @@ +/* 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef TEST_COMPOSITOR_SURFACE_H +#define TEST_COMPOSITOR_SURFACE_H + +#include "cairo.h" + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +_cairo_test_fallback_compositor_surface_create (cairo_content_t content, + int width, + int height); + + +cairo_surface_t * +_cairo_test_mask_compositor_surface_create (cairo_content_t content, + int width, + int height); + +cairo_surface_t * +_cairo_test_traps_compositor_surface_create (cairo_content_t content, + int width, + int height); + +cairo_surface_t * +_cairo_test_spans_compositor_surface_create (cairo_content_t content, + int width, + int height); + +cairo_surface_t * +_cairo_test_base_compositor_surface_create (cairo_content_t content, + int width, + int height); + +CAIRO_END_DECLS + +#endif /* TEST_COMPOSITOR_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/test-fallback-surface.c b/gfx/cairo/cairo/src/test-fallback-surface.c deleted file mode 100644 index 66399d4abbb3..000000000000 --- a/gfx/cairo/cairo/src/test-fallback-surface.c +++ /dev/null @@ -1,237 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 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 - */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to test a mythical backend that uses nothing but - * fallbacks. - * - * The defining feature of this backend is that it has as many %NULL - * backend function entries as possible. The ones that aren't %NULL are - * simply those that must be implemented to have working fallbacks. - * (Except for create_similar---fallbacks would work fine without - * that---I implemented it here in order to create as many surfaces as - * possible of type test_fallback_surface_t during the test suite - * run). - * - * It's possible that this code might serve as a good starting point - * for someone working on bringing up a new backend, starting with the - * minimal all-fallbacks approach and working up gradually from - * there. - */ - -#include "cairoint.h" - -#include "test-fallback-surface.h" -#include "cairo-error-private.h" - -typedef struct _test_fallback_surface { - cairo_surface_t base; - - /* This is a cairo_image_surface to hold the actual contents. */ - cairo_surface_t *backing; -} test_fallback_surface_t; - -static const cairo_surface_backend_t test_fallback_surface_backend; - -slim_hidden_proto (_cairo_test_fallback_surface_create); - -cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height) -{ - test_fallback_surface_t *surface; - cairo_surface_t *backing; - - backing = _cairo_image_surface_create_with_content (content, width, height); - if (cairo_surface_status (backing)) - return backing; - - surface = malloc (sizeof (test_fallback_surface_t)); - if (unlikely (surface == NULL)) { - cairo_surface_destroy (backing); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (&surface->base, - &test_fallback_surface_backend, - NULL, /* device */ - content); - - surface->backing = backing; - - return &surface->base; -} -slim_hidden_def (_cairo_test_fallback_surface_create); - -static cairo_surface_t * -_test_fallback_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - assert (CAIRO_CONTENT_VALID (content)); - - return _cairo_test_fallback_surface_create (content, - width, height); -} - -static cairo_status_t -_test_fallback_surface_finish (void *abstract_surface) -{ - test_fallback_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->backing); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_fallback_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_source_image (surface->backing, - image_out, image_extra); -} - -static void -_test_fallback_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->backing, - image, image_extra); -} - -static cairo_status_t -_test_fallback_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_dest_image (surface->backing, - interest_rect, - image_out, - image_rect_out, - image_extra); -} - -static void -_test_fallback_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - _cairo_surface_release_dest_image (surface->backing, - interest_rect, - image, - image_rect, - image_extra); -} - -static cairo_status_t -_test_fallback_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) -{ - test_fallback_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_bool_t -_test_fallback_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->backing, rectangle); -} - -static const cairo_surface_backend_t test_fallback_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, - _test_fallback_surface_create_similar, - _test_fallback_surface_finish, - _test_fallback_surface_acquire_source_image, - _test_fallback_surface_release_source_image, - _test_fallback_surface_acquire_dest_image, - _test_fallback_surface_release_dest_image, - _test_fallback_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _test_fallback_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* show_glyphs */ - NULL /* snapshot */ -}; diff --git a/gfx/cairo/cairo/src/test-meta-surface.c b/gfx/cairo/cairo/src/test-meta-surface.c deleted file mode 100644 index d5e14d7d1acb..000000000000 --- a/gfx/cairo/cairo/src/test-meta-surface.c +++ /dev/null @@ -1,342 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - * The Original Code is the cairo graphics library. - * - * The Initial Developer of the Original Code is Red Hat, Inc. - * - * Contributor(s): - * Carl Worth - */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to help exercise the meta-surface paths in cairo. - * - * The defining feature of this backend is that it uses a meta surface - * to record all operations, and then replays everything to an image - * surface. - * - * It's possible that this code might serve as a good starting point - * for someone working on bringing up a new meta-surface-based - * backend. - */ - -#include "cairoint.h" - -#include "test-meta-surface.h" - -#include "cairo-meta-surface-private.h" - -typedef struct _test_meta_surface { - cairo_surface_t base; - - /* This is a cairo_meta_surface to record all operations. */ - cairo_surface_t *meta; - - /* And this is a cairo_image_surface to hold the final result. */ - cairo_surface_t *image; - - cairo_bool_t image_reflects_meta; -} test_meta_surface_t; - -static const cairo_surface_backend_t test_meta_surface_backend; - -static cairo_int_status_t -_test_meta_surface_show_page (void *abstract_surface); - -cairo_surface_t * -_cairo_test_meta_surface_create (cairo_content_t content, - int width, - int height) -{ - test_meta_surface_t *surface; - cairo_status_t status; - - surface = malloc (sizeof (test_meta_surface_t)); - if (unlikely (surface == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto FAIL; - } - - _cairo_surface_init (&surface->base, &test_meta_surface_backend, - content); - - surface->meta = _cairo_meta_surface_create (content, width, height); - status = cairo_surface_status (surface->meta); - if (status) - goto FAIL_CLEANUP_SURFACE; - - surface->image = _cairo_image_surface_create_with_content (content, - width, height); - status = cairo_surface_status (surface->image); - if (status) - goto FAIL_CLEANUP_META; - - surface->image_reflects_meta = FALSE; - - return &surface->base; - - FAIL_CLEANUP_META: - cairo_surface_destroy (surface->meta); - FAIL_CLEANUP_SURFACE: - free (surface); - FAIL: - return _cairo_surface_create_in_error (status); -} - -static cairo_status_t -_test_meta_surface_finish (void *abstract_surface) -{ - test_meta_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->meta); - cairo_surface_destroy (surface->image); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_meta_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_meta_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (! surface->image_reflects_meta) { - status = _test_meta_surface_show_page (abstract_surface); - if (status) - return status; - } - - return _cairo_surface_acquire_source_image (surface->image, - image_out, image_extra); -} - -static void -_test_meta_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_meta_surface_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->image, - image, image_extra); -} - -static cairo_int_status_t -_test_meta_surface_show_page (void *abstract_surface) -{ - test_meta_surface_t *surface = abstract_surface; - cairo_status_t status; - - if (surface->image_reflects_meta) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_meta_surface_replay (surface->meta, surface->image); - if (status) - return status; - - surface->image_reflects_meta = TRUE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_test_meta_surface_intersect_clip_path (void *abstract_surface, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias) -{ - test_meta_surface_t *surface = abstract_surface; - - return _cairo_surface_intersect_clip_path (surface->meta, - path, fill_rule, - tolerance, antialias); -} - -static cairo_int_status_t -_test_meta_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_get_extents (surface->image, rectangle); -} - -static cairo_int_status_t -_test_meta_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_paint (surface->meta, op, source, extents); -} - -static cairo_int_status_t -_test_meta_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_mask (surface->meta, op, source, mask, extents); -} - -static cairo_int_status_t -_test_meta_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_stroke_style_t *style, - cairo_matrix_t *ctm, - cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_stroke (surface->meta, op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, extents); -} - -static cairo_int_status_t -_test_meta_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_path_fixed_t *path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_fill (surface->meta, op, source, - path, fill_rule, - tolerance, antialias, extents); -} - -static cairo_bool_t -_test_meta_surface_has_show_text_glyphs (void *abstract_surface) -{ - test_meta_surface_t *surface = abstract_surface; - - return cairo_surface_has_show_text_glyphs (surface->meta); -} - -static cairo_int_status_t -_test_meta_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - cairo_rectangle_int_t *extents) -{ - test_meta_surface_t *surface = abstract_surface; - - surface->image_reflects_meta = FALSE; - - return _cairo_surface_show_text_glyphs (surface->meta, op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, cluster_flags, - scaled_font, extents); -} - - -static cairo_surface_t * -_test_meta_surface_snapshot (void *abstract_other) -{ - test_meta_surface_t *other = abstract_other; - - return _cairo_surface_snapshot (other->meta); -} - -static const cairo_surface_backend_t test_meta_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_META, - NULL, /* create_similar */ - _test_meta_surface_finish, - _test_meta_surface_acquire_source_image, - _test_meta_surface_release_source_image, - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - _test_meta_surface_show_page, - NULL, /* set_clip_region */ - _test_meta_surface_intersect_clip_path, - _test_meta_surface_get_extents, - NULL, /* old_show_glyphs */ - NULL, /* get_font_options */ - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - _test_meta_surface_paint, - _test_meta_surface_mask, - _test_meta_surface_stroke, - _test_meta_surface_fill, - NULL, /* show_glyphs */ - _test_meta_surface_snapshot, - NULL, /* is_similar */ - NULL, /* reset */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _test_meta_surface_has_show_text_glyphs, - _test_meta_surface_show_text_glyphs -}; diff --git a/gfx/cairo/cairo/src/test-null-compositor-surface.c b/gfx/cairo/cairo/src/test-null-compositor-surface.c new file mode 100644 index 000000000000..35913a2b9fb6 --- /dev/null +++ b/gfx/cairo/cairo/src/test-null-compositor-surface.c @@ -0,0 +1,487 @@ +/* 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + + +#include "cairoint.h" + +#include "test-null-compositor-surface.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-spans-compositor-private.h" +#include "cairo-spans-private.h" + +typedef struct _test_compositor_surface { + cairo_image_surface_t base; +} test_compositor_surface_t; + +static const cairo_surface_backend_t test_compositor_surface_backend; + +static cairo_surface_t * +test_compositor_surface_create (const cairo_compositor_t *compositor, + cairo_content_t content, + int width, + int height) +{ + test_compositor_surface_t *surface; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + + switch (content) { + case CAIRO_CONTENT_ALPHA: + pixman_format = PIXMAN_a8; + break; + case CAIRO_CONTENT_COLOR: + pixman_format = PIXMAN_x8r8g8b8; + break; + case CAIRO_CONTENT_COLOR_ALPHA: + pixman_format = PIXMAN_a8r8g8b8; + break; + default: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + } + + pixman_image = pixman_image_create_bits (pixman_format, width, height, + NULL, 0); + if (unlikely (pixman_image == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _cairo_malloc (sizeof (test_compositor_surface_t)); + if (unlikely (surface == NULL)) { + pixman_image_unref (pixman_image); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base.base, + &test_compositor_surface_backend, + NULL, /* device */ + content, + FALSE); /* is_vector */ + _cairo_image_surface_init (&surface->base, pixman_image, pixman_format); + + surface->base.compositor = compositor; + + return &surface->base.base; +} + +static cairo_surface_t * +test_compositor_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + test_compositor_surface_t *surface = abstract_surface; + + return test_compositor_surface_create (surface->base.compositor, + content, width, height); +} + +static cairo_int_status_t +test_compositor_surface_paint (void *_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_paint (surface->base.compositor, + _surface, op, source, + clip); +} + +static cairo_int_status_t +test_compositor_surface_mask (void *_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_mask (surface->base.compositor, + _surface, op, source, mask, + clip); +} + +static cairo_int_status_t +test_compositor_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) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_stroke (surface->base.compositor, + _surface, op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +test_compositor_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) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_fill (surface->base.compositor, + _surface, op, source, + path, fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +test_compositor_surface_glyphs (void *_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) +{ + test_compositor_surface_t *surface = _surface; + return _cairo_compositor_glyphs (surface->base.compositor, + _surface, op, source, + glyphs, num_glyphs, scaled_font, + clip); +} + +static const cairo_surface_backend_t test_compositor_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_image_surface_finish, + _cairo_default_context_create, + + test_compositor_surface_create_similar, + NULL, /* create similar image */ + _cairo_image_surface_map_to_image, + _cairo_image_surface_unmap_image, + + _cairo_image_surface_source, + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_image_surface_get_extents, + _cairo_image_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + test_compositor_surface_paint, + test_compositor_surface_mask, + test_compositor_surface_stroke, + test_compositor_surface_fill, + NULL, /* fill/stroke */ + test_compositor_surface_glyphs, +}; + +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) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +pattern_to_surface (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) +{ + return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); +} + +static cairo_int_status_t +fill_boxes (void *_dst, + cairo_operator_t op, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + 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) +{ + return CAIRO_STATUS_SUCCESS; +} + +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) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +lerp (void *_dst, + 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) +{ + return CAIRO_STATUS_SUCCESS; +} + +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) +{ + return CAIRO_STATUS_SUCCESS; +} + +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) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +check_composite_glyphs (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +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_STATUS_SUCCESS; +} + +static cairo_status_t +spans (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *spans, + unsigned num_spans) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +finish_spans (void *abstract_renderer) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +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_span_renderer_t *r = (cairo_span_renderer_t *)_r; + r->render_rows = spans; + r->finish = finish_spans; + return CAIRO_STATUS_SUCCESS; +} + +static void +span_renderer_fini (cairo_abstract_span_renderer_t *_r, + cairo_int_status_t status) +{ +} + +static const cairo_compositor_t * +no_fallback_compositor_get (void) +{ + return &__cairo_no_compositor; +} + +static cairo_int_status_t +check_composite (const cairo_composite_rectangles_t *extents) +{ + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_compositor_t * +no_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, + no_fallback_compositor_get ()); + + compositor.acquire = acquire; + compositor.release = release; + compositor.set_clip_region = set_clip_region; + compositor.pattern_to_surface = pattern_to_surface; + 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_glyphs = check_composite_glyphs; + compositor.composite_glyphs = composite_glyphs; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} + +static const cairo_compositor_t * +no_spans_compositor_get (void) +{ + static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; + static cairo_spans_compositor_t compositor; + + if (_cairo_atomic_init_once_enter(&once)) { + _cairo_spans_compositor_init (&compositor, + no_traps_compositor_get()); + + //compositor.acquire = acquire; + //compositor.release = release; + compositor.fill_boxes = fill_boxes; + //compositor.check_composite_boxes = check_composite_boxes; + compositor.composite_boxes = composite_boxes; + //compositor.check_span_renderer = check_span_renderer; + compositor.renderer_init = span_renderer_init; + compositor.renderer_fini = span_renderer_fini; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor.base; +} + +cairo_surface_t * +_cairo_test_no_fallback_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (no_fallback_compositor_get(), + content, width, height); +} + +cairo_surface_t * +_cairo_test_no_traps_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (no_traps_compositor_get(), + content, width, height); +} + +cairo_surface_t * +_cairo_test_no_spans_compositor_surface_create (cairo_content_t content, + int width, + int height) +{ + return test_compositor_surface_create (no_spans_compositor_get(), + content, width, height); +} diff --git a/gfx/cairo/cairo/src/test-null-compositor-surface.h b/gfx/cairo/cairo/src/test-null-compositor-surface.h new file mode 100644 index 000000000000..52d864b28df8 --- /dev/null +++ b/gfx/cairo/cairo/src/test-null-compositor-surface.h @@ -0,0 +1,60 @@ +/* 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 Intel Corporation + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef TEST_NULL_COMPOSITOR_SURFACE_H +#define TEST_NULL_COMPOSITOR_SURFACE_H + +#include "cairo.h" + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +_cairo_test_no_fallback_compositor_surface_create (cairo_content_t content, + int width, + int height); + +cairo_surface_t * +_cairo_test_no_traps_compositor_surface_create (cairo_content_t content, + int width, + int height); + +cairo_surface_t * +_cairo_test_no_spans_compositor_surface_create (cairo_content_t content, + int width, + int height); + +CAIRO_END_DECLS + +#endif /* TEST_NULL_COMPOSITOR_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/test-paginated-surface.c b/gfx/cairo/cairo/src/test-paginated-surface.c index e06cbed719cc..7967f7406cd6 100644 --- a/gfx/cairo/cairo/src/test-paginated-surface.c +++ b/gfx/cairo/cairo/src/test-paginated-surface.c @@ -49,8 +49,10 @@ #include "test-paginated-surface.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-paginated-private.h" +#include "cairo-surface-backend-private.h" typedef struct _test_paginated_surface { cairo_surface_t base; @@ -72,14 +74,15 @@ _cairo_test_paginated_surface_create (cairo_surface_t *target) if (unlikely (status)) return _cairo_surface_create_in_error (status); - surface = malloc (sizeof (test_paginated_surface_t)); + surface = _cairo_malloc (sizeof (test_paginated_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); _cairo_surface_init (&surface->base, &test_paginated_surface_backend, NULL, /* device */ - target->content); + target->content, + TRUE); /* is_vector */ surface->target = cairo_surface_reference (target); @@ -121,7 +124,7 @@ static cairo_int_status_t _test_paginated_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -136,7 +139,7 @@ _test_paginated_surface_mask (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, - cairo_clip_t *clip) + const cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -151,13 +154,13 @@ static cairo_int_status_t _test_paginated_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + 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_clip_t *clip) + const cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -175,11 +178,11 @@ static cairo_int_status_t _test_paginated_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -212,7 +215,7 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, int num_clusters, cairo_text_cluster_flags_t cluster_flags, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip) + const cairo_clip_t *clip) { test_paginated_surface_t *surface = abstract_surface; @@ -229,42 +232,43 @@ _test_paginated_surface_show_text_glyphs (void *abstract_surface, } -static void +static cairo_int_status_t _test_paginated_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t mode) { test_paginated_surface_t *surface = abstract_surface; surface->paginated_mode = mode; + + return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t test_paginated_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED, + _test_paginated_surface_finish, + _cairo_default_context_create, /* Since we are a paginated user, we get to regard most of the * surface backend interface as historical cruft and ignore it. */ NULL, /* create_similar */ - _test_paginated_surface_finish, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ NULL, /* show_page */ + _test_paginated_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* Here is the more "modern" section of the surface backend * interface which is mostly just drawing functions */ @@ -273,14 +277,8 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { _test_paginated_surface_mask, _test_paginated_surface_stroke, _test_paginated_surface_fill, + NULL, /* fill-stroke */ NULL, /* replaced by show_text_glyphs */ - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _test_paginated_surface_has_show_text_glyphs, _test_paginated_surface_show_text_glyphs }; diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp similarity index 99% rename from gfx/cairo/cairo/src/cairo-dwrite-font.cpp rename to gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp index 300f8a09c045..7cbb2d4d0bb6 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp +++ b/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp @@ -1,7 +1,7 @@ /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * - * Copyright 2010 Mozilla Foundation + * Copyright © 2010 Mozilla Foundation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public diff --git a/gfx/cairo/cairo/src/cairo-dwrite-private.h b/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h similarity index 99% rename from gfx/cairo/cairo/src/cairo-dwrite-private.h rename to gfx/cairo/cairo/src/win32/cairo-dwrite-private.h index e4757a23c911..a6efb4bc381d 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-private.h +++ b/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h @@ -1,7 +1,7 @@ /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * - * Copyright 2010 Mozilla Foundation + * Copyright © 2010 Mozilla Foundation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-debug.c b/gfx/cairo/cairo/src/win32/cairo-win32-debug.c new file mode 100644 index 000000000000..5a971bd61479 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-debug.c @@ -0,0 +1,87 @@ +/* -*- 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. + * Copyright © 2012 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): + * Owen Taylor + * Stuart Parmenter + * Vladimir Vukicevic + */ + +#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" + +#include +#include + +void +_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) +{ + RGNDATA *rd; + unsigned int z; + + if (header) + fprintf (stderr, "%s\n", header); + + if (rgn == NULL) { + fprintf (stderr, " NULL\n"); + } + + z = GetRegionData(rgn, 0, NULL); + rd = (RGNDATA*) _cairo_malloc (z); + z = GetRegionData(rgn, z, rd); + + fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", + rd->rdh.nCount, + rd->rdh.rcBound.left, + rd->rdh.rcBound.top, + rd->rdh.rcBound.right - rd->rdh.rcBound.left, + rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); + + for (z = 0; z < rd->rdh.nCount; z++) { + RECT r = ((RECT*)rd->Buffer)[z]; + fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", + z, r.left, r.top, r.right - r.left, r.bottom - r.top); + } + + free(rd); + fflush (stderr); +} diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-device.c b/gfx/cairo/cairo/src/win32/cairo-win32-device.c new file mode 100644 index 000000000000..6fce722ecf0d --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-device.c @@ -0,0 +1,202 @@ +/* -*- 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. + * Copyright © 2012 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): + * Owen Taylor + * Stuart Parmenter + * Vladimir Vukicevic + */ + +#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" +#include "cairo-device-private.h" +#include "cairo-win32-private.h" + +#include +#include + +static cairo_device_t *__cairo_win32_device; + +static cairo_status_t +_cairo_win32_device_flush (void *device) +{ + GdiFlush (); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_device_finish (void *device) +{ +} + +static void +_cairo_win32_device_destroy (void *device) +{ + free (device); +} + +static const cairo_device_backend_t _cairo_win32_device_backend = { + CAIRO_DEVICE_TYPE_WIN32, + + NULL, NULL, /* lock, unlock */ + + _cairo_win32_device_flush, + _cairo_win32_device_finish, + _cairo_win32_device_destroy, +}; + +#if 0 +D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_IGNORE), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT + ); + +hr = m_pD2DFactory->CreateDCRenderTarget(&props, &device->d2d); +#endif + +static cairo_bool_t is_win98 (void) +{ + OSVERSIONINFO os; + + os.dwOSVersionInfoSize = sizeof (os); + GetVersionEx (&os); + + return (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId && + os.dwMajorVersion == 4 && + os.dwMinorVersion == 10); +} + +static void * +_cairo_win32_device_get_alpha_blend (cairo_win32_device_t *device) +{ + void *func = NULL; + + if (is_win98 ()) + return NULL; + + device->msimg32_dll = LoadLibraryW (L"msimg32"); + if (device->msimg32_dll) + func = GetProcAddress (device->msimg32_dll, "AlphaBlend"); + + return func; +} + +cairo_device_t * +_cairo_win32_device_get (void) +{ + cairo_win32_device_t *device; + + CAIRO_MUTEX_INITIALIZE (); + + if (__cairo_win32_device) + return cairo_device_reference (__cairo_win32_device); + + device = _cairo_malloc (sizeof (*device)); + + _cairo_device_init (&device->base, &_cairo_win32_device_backend); + + device->compositor = _cairo_win32_gdi_compositor_get (); + + device->msimg32_dll = NULL; + device->alpha_blend = _cairo_win32_device_get_alpha_blend (device); + + if (_cairo_atomic_ptr_cmpxchg ((void **)&__cairo_win32_device, NULL, device)) + return cairo_device_reference(&device->base); + + _cairo_win32_device_destroy (device); + return cairo_device_reference (__cairo_win32_device); +} + +unsigned +_cairo_win32_flags_for_dc (HDC dc, cairo_format_t format) +{ + uint32_t flags = 0; + cairo_bool_t is_display = GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY; + + if (format == CAIRO_FORMAT_RGB24 || format == CAIRO_FORMAT_ARGB32) + { + int cap = GetDeviceCaps(dc, RASTERCAPS); + if (cap & RC_BITBLT) + flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; + if (!is_display && GetDeviceCaps(dc, SHADEBLENDCAPS) != SB_NONE) + flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; + + /* ARGB32 available operations are a strict subset of RGB24 + * available operations. This is because the same GDI functions + * can be used but most of them always reset alpha channel to 0 + * which is bad for ARGB32. + */ + if (format == CAIRO_FORMAT_RGB24) + { + flags |= CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH; + if (cap & RC_STRETCHBLT) + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; + if (cap & RC_STRETCHDIB) + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; + } + } + + if (is_display) { + flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY; + + /* These will always be possible, but the actual GetDeviceCaps + * calls will return whether they're accelerated or not. + * We may want to use our own (pixman) routines sometimes + * if they're eventually faster, but for now have GDI do + * everything. + */ +#if 0 + flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; + flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; +#endif + } + + return flags; +} diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-display-surface.c b/gfx/cairo/cairo/src/win32/cairo-win32-display-surface.c new file mode 100644 index 000000000000..304d34aea026 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-display-surface.c @@ -0,0 +1,1147 @@ +/* -*- 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. + * Copyright © 2012 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): + * Owen Taylor + * Stuart Parmenter + * Vladimir Vukicevic + */ + +#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" +#include "cairo-composite-rectangles-private.h" +#include "cairo-compositor-private.h" +#include "cairo-damage-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-private.h" +#include "cairo-win32-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-fallback-private.h" +#include "cairo-surface-backend-private.h" + +#include +#include + +#if defined(__MINGW32__) && !defined(ETO_PDY) +# define ETO_PDY 0x2000 +#endif + +#define PELS_72DPI ((LONG)(72. / 0.0254)) + +/** + * SECTION:cairo-win32 + * @Title: Win32 Surfaces + * @Short_Description: Microsoft Windows surface support + * @See_Also: #cairo_surface_t + * + * The Microsoft Windows surface is used to render cairo graphics to + * Microsoft Windows windows, bitmaps, and printing device contexts. + * + * The surface returned by cairo_win32_printing_surface_create() is of surface + * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface + * type. + * + * The surface returned by the other win32 constructors is of surface type + * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. + **/ + +/** + * CAIRO_HAS_WIN32_SURFACE: + * + * Defined if the Microsoft Windows surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + * + * Since: 1.0 + **/ + +static const cairo_surface_backend_t cairo_win32_display_surface_backend; + +static cairo_status_t +_create_dc_and_bitmap (cairo_win32_display_surface_t *surface, + HDC original_dc, + cairo_format_t format, + int width, + int height, + unsigned char **bits_out, + int *rowstride_out) +{ + cairo_status_t status; + + BITMAPINFO *bitmap_info = NULL; + struct { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[2]; + } bmi_stack; + void *bits; + + int num_palette = 0; /* Quiet GCC */ + int i; + + surface->win32.dc = NULL; + surface->bitmap = NULL; + surface->is_dib = FALSE; + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB30: + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + num_palette = 0; + break; + + case CAIRO_FORMAT_A8: + num_palette = 256; + break; + + case CAIRO_FORMAT_A1: + num_palette = 2; + break; + } + + if (num_palette > 2) { + bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER)); + if (!bitmap_info) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + bitmap_info = (BITMAPINFO *)&bmi_stack; + } + + bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width; + bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */ + bitmap_info->bmiHeader.biSizeImage = 0; + bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */ + bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */ + bitmap_info->bmiHeader.biPlanes = 1; + + switch (format) { + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB30: + ASSERT_NOT_REACHED; + /* We can't create real RGB24 bitmaps because something seems to + * break if we do, especially if we don't set up an image + * fallback. It could be a bug with using a 24bpp pixman image + * (and creating one with masks). So treat them like 32bpp. + * Note: This causes problems when using BitBlt/AlphaBlend/etc! + * see end of file. + */ + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_ARGB32: + bitmap_info->bmiHeader.biBitCount = 32; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ + bitmap_info->bmiHeader.biClrImportant = 0; + break; + + case CAIRO_FORMAT_A8: + bitmap_info->bmiHeader.biBitCount = 8; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 256; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 256; i++) { + bitmap_info->bmiColors[i].rgbBlue = i; + bitmap_info->bmiColors[i].rgbGreen = i; + bitmap_info->bmiColors[i].rgbRed = i; + bitmap_info->bmiColors[i].rgbReserved = 0; + } + break; + + case CAIRO_FORMAT_A1: + bitmap_info->bmiHeader.biBitCount = 1; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 2; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 2; i++) { + bitmap_info->bmiColors[i].rgbBlue = i * 255; + bitmap_info->bmiColors[i].rgbGreen = i * 255; + bitmap_info->bmiColors[i].rgbRed = i * 255; + bitmap_info->bmiColors[i].rgbReserved = 0; + } + break; + } + + surface->win32.dc = CreateCompatibleDC (original_dc); + if (!surface->win32.dc) + goto FAIL; + + surface->bitmap = CreateDIBSection (surface->win32.dc, + bitmap_info, + DIB_RGB_COLORS, + &bits, + NULL, 0); + if (!surface->bitmap) + goto FAIL; + + surface->is_dib = TRUE; + + GdiFlush(); + + surface->saved_dc_bitmap = SelectObject (surface->win32.dc, + surface->bitmap); + if (!surface->saved_dc_bitmap) + goto FAIL; + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (bits_out) + *bits_out = bits; + + if (rowstride_out) { + /* Windows bitmaps are padded to 32-bit (dword) boundaries */ + switch (format) { + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB30: + ASSERT_NOT_REACHED; + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + *rowstride_out = 4 * width; + break; + + case CAIRO_FORMAT_A8: + *rowstride_out = (width + 3) & ~3; + break; + + case CAIRO_FORMAT_A1: + *rowstride_out = ((width + 31) & ~31) / 8; + break; + } + } + + surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc, format); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error (__FUNCTION__); + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (surface->saved_dc_bitmap) { + SelectObject (surface->win32.dc, surface->saved_dc_bitmap); + surface->saved_dc_bitmap = NULL; + } + + if (surface->bitmap) { + DeleteObject (surface->bitmap); + surface->bitmap = NULL; + } + + if (surface->win32.dc) { + DeleteDC (surface->win32.dc); + surface->win32.dc = NULL; + } + + return status; +} + +static cairo_surface_t * +_cairo_win32_display_surface_create_for_dc (HDC original_dc, + cairo_format_t format, + int width, + int height) +{ + cairo_status_t status; + cairo_device_t *device; + cairo_win32_display_surface_t *surface; + unsigned char *bits; + int rowstride; + + surface = _cairo_malloc (sizeof (*surface)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->fallback = NULL; + + status = _create_dc_and_bitmap (surface, original_dc, format, + width, height, + &bits, &rowstride); + if (status) + goto FAIL; + + surface->image = cairo_image_surface_create_for_data (bits, format, + width, height, rowstride); + status = surface->image->status; + if (status) + goto FAIL; + + _cairo_image_surface_set_parent (to_image_surface(surface->image), + &surface->win32.base); + + surface->win32.format = format; + + surface->win32.extents.x = 0; + surface->win32.extents.y = 0; + surface->win32.extents.width = width; + surface->win32.extents.height = height; + surface->win32.x_ofs = 0; + surface->win32.y_ofs = 0; + + surface->initial_clip_rgn = NULL; + surface->had_simple_clip = FALSE; + + device = _cairo_win32_device_get (); + + _cairo_surface_init (&surface->win32.base, + &cairo_win32_display_surface_backend, + device, + _cairo_content_from_format (format), + FALSE); /* is_vector */ + + cairo_device_destroy (device); + + return &surface->win32.base; + + FAIL: + if (surface->bitmap) { + SelectObject (surface->win32.dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->win32.dc); + } + free (surface); + + return _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_win32_display_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_win32_display_surface_t *src = abstract_src; + cairo_format_t format = _cairo_format_from_content (content); + cairo_surface_t *new_surf = NULL; + + /* We force a DIB always if: + * - we need alpha; or + * - the parent is a DIB; or + * - the parent is for printing (because we don't care about the + * bit depth at that point) + * + * We also might end up with a DIB even if a DDB is requested if + * DDB creation failed due to out of memory. + */ + if (!(src->is_dib || content & CAIRO_CONTENT_ALPHA)) { + /* try to create a ddb */ + new_surf = cairo_win32_surface_create_with_ddb (src->win32.dc, CAIRO_FORMAT_RGB24, width, height); + + if (new_surf->status) + new_surf = NULL; + } + + if (new_surf == NULL) { + new_surf = _cairo_win32_display_surface_create_for_dc (src->win32.dc, format, width, height); + } + + return new_surf; +} + +static cairo_surface_t * +_cairo_win32_display_surface_create_similar_image (void *abstract_other, + cairo_format_t format, + int width, + int height) +{ + cairo_win32_display_surface_t *surface = abstract_other; + cairo_image_surface_t *image; + + surface = (cairo_win32_display_surface_t *) + _cairo_win32_display_surface_create_for_dc (surface->win32.dc, + format, width, height); + if (surface->win32.base.status) + return &surface->win32.base; + + /* And clear in order to comply with our user API semantics */ + image = (cairo_image_surface_t *) surface->image; + if (! image->base.is_clear) { + memset (image->data, 0, image->stride * height); + image->base.is_clear = TRUE; + } + + return &image->base; +} + +static cairo_status_t +_cairo_win32_display_surface_finish (void *abstract_surface) +{ + cairo_win32_display_surface_t *surface = abstract_surface; + + if (surface->image && to_image_surface(surface->image)->parent) { + assert (to_image_surface(surface->image)->parent == &surface->win32.base); + /* Unhook ourselves first to avoid the double-unref from the image */ + to_image_surface(surface->image)->parent = NULL; + cairo_surface_finish (surface->image); + cairo_surface_destroy (surface->image); + } + + /* If we created the Bitmap and DC, destroy them */ + if (surface->bitmap) { + SelectObject (surface->win32.dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->win32.dc); + } + + _cairo_win32_display_surface_discard_fallback (surface); + + if (surface->initial_clip_rgn) + DeleteObject (surface->initial_clip_rgn); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_image_surface_t * +_cairo_win32_display_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_win32_display_surface_t *surface = abstract_surface; + cairo_status_t status; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->win32.base.unique_id)); + + if (surface->image) + goto done; + + if (surface->fallback == NULL) { + surface->fallback = + _cairo_win32_display_surface_create_for_dc (surface->win32.dc, + surface->win32.format, + surface->win32.extents.x + surface->win32.extents.width, + surface->win32.extents.y + surface->win32.extents.height); + if (unlikely (status = surface->fallback->status)) + goto err; + + if (!BitBlt (to_win32_surface(surface->fallback)->dc, + surface->win32.extents.x, surface->win32.extents.y, + surface->win32.extents.width, + surface->win32.extents.height, + surface->win32.dc, + surface->win32.extents.x + surface->win32.x_ofs, /* Handling multi-monitor... */ + surface->win32.extents.y + surface->win32.y_ofs, /* ... setup on Win32 */ + SRCCOPY)) { + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + goto err; + } + } + + surface = to_win32_display_surface (surface->fallback); +done: + GdiFlush(); + return _cairo_surface_map_to_image (surface->image, extents); + +err: + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; + + return _cairo_image_surface_create_in_error (status); +} + +static cairo_int_status_t +_cairo_win32_display_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_win32_display_surface_t *surface = abstract_surface; + + /* Delay the download until the next flush, which means we also need + * to make sure our sources rare flushed. + */ + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, to_win32_surface(surface)->base.unique_id)); + + if (surface->fallback) { + cairo_rectangle_int_t r; + + r.x = image->base.device_transform_inverse.x0; + r.y = image->base.device_transform_inverse.y0; + r.width = image->width; + r.height = image->height; + + TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n", + __FUNCTION__, r.x, r.y, r.width, r.height)); + surface->fallback->damage = + _cairo_damage_add_rectangle (surface->fallback->damage, &r); + surface = to_win32_display_surface (surface->fallback); + } + + return _cairo_surface_unmap_image (surface->image, image); +} + +static cairo_status_t +_cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) +{ + cairo_win32_display_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->win32.base.unique_id)); + if (surface->fallback == NULL) + return CAIRO_STATUS_SUCCESS; + + if (surface->fallback->damage) { + cairo_win32_display_surface_t *fallback; + cairo_damage_t *damage; + + damage = _cairo_damage_reduce (surface->fallback->damage); + surface->fallback->damage = NULL; + + fallback = to_win32_display_surface (surface->fallback); + assert (fallback->image); + + TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__, + damage->region ? cairo_region_num_rectangles (damage->region) : 0)); + + if (damage->status) { + if (!BitBlt (surface->win32.dc, + surface->win32.extents.x + surface->win32.x_ofs, /* Handling multi-monitor... */ + surface->win32.extents.y + surface->win32.y_ofs, /* ... setup on Win32 */ + surface->win32.extents.width, + surface->win32.extents.height, + fallback->win32.dc, + surface->win32.extents.x, surface->win32.extents.y, + SRCCOPY)) + status = _cairo_win32_print_gdi_error (__FUNCTION__); + } else if (damage->region) { + int n = cairo_region_num_rectangles (damage->region), i; + for (i = 0; i < n; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (damage->region, i, &rect); + TRACE ((stderr, "%s: damage (%d,%d)x(%d,%d)\n", __FUNCTION__, + rect.x, rect.y, + rect.width, rect.height)); + if (!BitBlt (surface->win32.dc, + rect.x + surface->win32.x_ofs, /* Handling multi-monitor... */ + rect.y + surface->win32.y_ofs, /* ... setup on Win32 */ + rect.width, rect.height, + fallback->win32.dc, + rect.x, rect.y, + SRCCOPY)) { + status = _cairo_win32_print_gdi_error (__FUNCTION__); + break; + } + } + } + _cairo_damage_destroy (damage); + } else { + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; + } + + return status; +} + +static cairo_status_t +_cairo_win32_display_surface_mark_dirty (void *abstract_surface, + int x, int y, int width, int height) +{ + _cairo_win32_display_surface_discard_fallback (abstract_surface); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface) +{ + RECT rect; + int clipBoxType; + int gm; + XFORM saved_xform; + + /* GetClipBox/GetClipRgn and friends interact badly with a world transform + * set. GetClipBox returns values in logical (transformed) coordinates; + * it's unclear what GetClipRgn returns, because the region is empty in the + * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. + * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn + * works in device units. + * + * So, avoid the whole mess and get rid of the world transform + * while we store our initial data and when we restore initial coordinates. + * + * XXX we may need to modify x/y by the ViewportOrg or WindowOrg + * here in GM_COMPATIBLE; unclear. + */ + gm = GetGraphicsMode (hdc); + if (gm == GM_ADVANCED) { + GetWorldTransform (hdc, &saved_xform); + ModifyWorldTransform (hdc, NULL, MWT_IDENTITY); + } + + clipBoxType = GetClipBox (hdc, &rect); + if (clipBoxType == ERROR) { + _cairo_win32_print_gdi_error (__FUNCTION__); + SetGraphicsMode (hdc, gm); + /* XXX: Can we make a more reasonable guess at the error cause here? */ + return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + } + + surface->win32.extents.x = rect.left; + surface->win32.extents.y = rect.top; + surface->win32.extents.width = rect.right - rect.left; + surface->win32.extents.height = rect.bottom - rect.top; + + /* On multi-monitor setup, under Windows, the primary monitor always + * have origin (0,0). Any monitors that extends to the left or above + * will have coordinates in the negative range. Take this into + * account, by forcing our Win32 surface to start at extent (0,0) and + * using a device offset. Cairo does not handle extents with negative + * offsets. + */ + surface->win32.x_ofs = 0; + surface->win32.y_ofs = 0; + if ((surface->win32.extents.x < 0) || + (surface->win32.extents.y < 0)) { + /* Negative offsets occurs for (and ONLY for) the desktop DC (virtual + * desktop), when a monitor extend to the left or above the primary + * monitor. + * + * More info @ https://www.microsoft.com/msj/0697/monitor/monitor.aspx + * + * Note that any other DC, including memory DC created with + * CreateCompatibleDC() will have extents in the + * positive range. This will be taken into account later when we perform + * raster operations between the DC (may have to perform offset + * translation). + */ + surface->win32.x_ofs = surface->win32.extents.x; + surface->win32.y_ofs = surface->win32.extents.y; + surface->win32.extents.x = 0; + surface->win32.extents.y = 0; + } + + surface->initial_clip_rgn = NULL; + surface->had_simple_clip = FALSE; + + if (clipBoxType == COMPLEXREGION) { + surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0); + if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) { + DeleteObject(surface->initial_clip_rgn); + surface->initial_clip_rgn = NULL; + } + } else if (clipBoxType == SIMPLEREGION) { + surface->had_simple_clip = TRUE; + } + + if (gm == GM_ADVANCED) + SetWorldTransform (hdc, &saved_xform); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, + cairo_clip_t *clip) +{ + char stack[512]; + cairo_rectangle_int_t extents; + int num_rects; + RGNDATA *data; + size_t data_size; + RECT *rects; + int i; + HRGN gdi_region; + cairo_status_t status; + cairo_region_t *region; + + /* The semantics we want is that any clip set by cairo combines + * is intersected with the clip on device context that the + * surface was created for. To implement this, we need to + * save the original clip when first setting a clip on surface. + */ + + assert (_cairo_clip_is_region (clip)); + region = _cairo_clip_get_region (clip); + if (region == NULL) + return CAIRO_STATUS_SUCCESS; + + cairo_region_get_extents (region, &extents); + num_rects = cairo_region_num_rectangles (region); + + /* XXX see notes in _cairo_win32_save_initial_clip -- + * this code will interact badly with a HDC which had an initial + * world transform -- we should probably manually transform the + * region rects, because SelectClipRgn takes device units, not + * logical units (unlike IntersectClipRect). + */ + + data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); + if (data_size > sizeof (stack)) { + data = _cairo_malloc (data_size); + if (!data) + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + } else + data = (RGNDATA *)stack; + + data->rdh.dwSize = sizeof (RGNDATAHEADER); + data->rdh.iType = RDH_RECTANGLES; + data->rdh.nCount = num_rects; + data->rdh.nRgnSize = num_rects * sizeof (RECT); + data->rdh.rcBound.left = extents.x; + data->rdh.rcBound.top = extents.y; + data->rdh.rcBound.right = extents.x + extents.width; + data->rdh.rcBound.bottom = extents.y + extents.height; + + rects = (RECT *)data->Buffer; + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + + rects[i].left = rect.x; + rects[i].top = rect.y; + rects[i].right = rect.x + rect.width; + rects[i].bottom = rect.y + rect.height; + } + + gdi_region = ExtCreateRegion (NULL, data_size, data); + if ((char *)data != stack) + free (data); + + if (!gdi_region) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* AND the new region into our DC */ + status = CAIRO_STATUS_SUCCESS; + if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR) + status = _cairo_win32_print_gdi_error (__FUNCTION__); + + DeleteObject (gdi_region); + + return status; +} + +void +_cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface) +{ + XFORM saved_xform; + int gm = GetGraphicsMode (surface->win32.dc); + if (gm == GM_ADVANCED) { + GetWorldTransform (surface->win32.dc, &saved_xform); + ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY); + } + + /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ + SelectClipRgn (surface->win32.dc, surface->initial_clip_rgn); + + if (surface->had_simple_clip) { + /* then if we had a simple clip, intersect */ + IntersectClipRect (surface->win32.dc, + surface->win32.extents.x, + surface->win32.extents.y, + surface->win32.extents.x + surface->win32.extents.width, + surface->win32.extents.y + surface->win32.extents.height); + } + + if (gm == GM_ADVANCED) + SetWorldTransform (surface->win32.dc, &saved_xform); +} + +void +_cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface) +{ + if (surface->fallback) { + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, surface->win32.base.unique_id)); + + cairo_surface_finish (surface->fallback); + cairo_surface_destroy (surface->fallback); + surface->fallback = NULL; + } +} + +static cairo_int_status_t +_cairo_win32_display_surface_paint (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_win32_device_t *device = to_win32_device_from_surface (surface); + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, to_win32_surface(surface)->base.unique_id)); + + if (clip == NULL && + (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)) + _cairo_win32_display_surface_discard_fallback (surface); + + return _cairo_compositor_paint (device->compositor, + surface, op, source, clip); +} + +static cairo_int_status_t +_cairo_win32_display_surface_mask (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_win32_device_t *device = to_win32_device_from_surface (surface); + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, to_win32_surface(surface)->base.unique_id)); + + if (clip == NULL && op == CAIRO_OPERATOR_SOURCE) + _cairo_win32_display_surface_discard_fallback (surface); + + return _cairo_compositor_mask (device->compositor, + surface, op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_win32_display_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) +{ + cairo_win32_device_t *device = to_win32_device_from_surface (surface); + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, to_win32_surface(surface)->base.unique_id)); + + return _cairo_compositor_stroke (device->compositor, surface, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, clip); +} + +static cairo_int_status_t +_cairo_win32_display_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) +{ + cairo_win32_device_t *device = to_win32_device_from_surface (surface); + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, to_win32_surface(surface)->base.unique_id)); + + return _cairo_compositor_fill (device->compositor, surface, + op, source, path, + fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_win32_display_surface_glyphs (void *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_win32_device_t *device = to_win32_device_from_surface (surface); + + TRACE ((stderr, "%s (surface=%d)\n", + __FUNCTION__, to_win32_surface(surface)->base.unique_id)); + + return _cairo_compositor_glyphs (device->compositor, surface, + op, source, + glyphs, num_glyphs, scaled_font, + clip); +} + +static const cairo_surface_backend_t cairo_win32_display_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32, + _cairo_win32_display_surface_finish, + + _cairo_default_context_create, + + _cairo_win32_display_surface_create_similar, + _cairo_win32_display_surface_create_similar_image, + _cairo_win32_display_surface_map_to_image, + _cairo_win32_display_surface_unmap_image, + + _cairo_surface_default_source, + _cairo_surface_default_acquire_source_image, + _cairo_surface_default_release_source_image, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_win32_surface_get_extents, + NULL, /* get_font_options */ + + _cairo_win32_display_surface_flush, + _cairo_win32_display_surface_mark_dirty, + + _cairo_win32_display_surface_paint, + _cairo_win32_display_surface_mask, + _cairo_win32_display_surface_stroke, + _cairo_win32_display_surface_fill, + NULL, /* fill/stroke */ + _cairo_win32_display_surface_glyphs, +}; + +/* Notes: + * + * Win32 alpha-understanding functions + * + * BitBlt - will copy full 32 bits from a 32bpp DIB to result + * (so it's safe to use for ARGB32->ARGB32 SOURCE blits) + * (but not safe going RGB24->ARGB32, if RGB24 is also represented + * as a 32bpp DIB, since the alpha isn't discarded!) + * + * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set, + * it will still copy over the src alpha, because the SCA value (255) will be + * multiplied by all the src components. + */ + +/** + * cairo_win32_surface_create_with_format: + * @hdc: the DC to create a surface for + * @format: format of pixels in the surface to create + * + * Creates a cairo surface that targets the given DC. The DC will be + * queried for its initial clip extents, and this will be used as the + * size of the cairo surface. + * + * Supported formats are: + * %CAIRO_FORMAT_ARGB32 + * %CAIRO_FORMAT_RGB24 + * + * Note: @format only tells cairo how to draw on the surface, not what + * the format of the surface is. Namely, cairo does not (and cannot) + * check that @hdc actually supports alpha-transparency. + * + * Return value: the newly created surface, NULL on failure + * + * Since: 1.14 + **/ +cairo_surface_t * +cairo_win32_surface_create_with_format (HDC hdc, cairo_format_t format) +{ + cairo_win32_display_surface_t *surface; + + cairo_status_t status; + cairo_device_t *device; + + switch (format) { + default: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + break; + } + + surface = _cairo_malloc (sizeof (*surface)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + status = _cairo_win32_save_initial_clip (hdc, surface); + if (status) { + free (surface); + return _cairo_surface_create_in_error (status); + } + + surface->image = NULL; + surface->fallback = NULL; + surface->win32.format = format; + + surface->win32.dc = hdc; + surface->bitmap = NULL; + surface->is_dib = FALSE; + surface->saved_dc_bitmap = NULL; + + surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc, format); + + device = _cairo_win32_device_get (); + + _cairo_surface_init (&surface->win32.base, + &cairo_win32_display_surface_backend, + device, + _cairo_content_from_format (format), + FALSE); /* is_vector */ + + cairo_device_destroy (device); + + return &surface->win32.base; +} + +/** + * cairo_win32_surface_create: + * @hdc: the DC to create a surface for + * + * Creates a cairo surface that targets the given DC. The DC will be + * queried for its initial clip extents, and this will be used as the + * size of the cairo surface. The resulting surface will always be of + * format %CAIRO_FORMAT_RGB24; should you need another surface format, + * you will need to create one through + * cairo_win32_surface_create_with_format() or + * cairo_win32_surface_create_with_dib(). + * + * Return value: the newly created surface, NULL on failure + * + * Since: 1.0 + **/ +cairo_surface_t * +cairo_win32_surface_create (HDC hdc) +{ + return cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_RGB24); +} + +/** + * cairo_win32_surface_create_with_dib: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a device-independent-bitmap surface not associated with + * any particular existing surface or device context. The created + * bitmap will be uninitialized. + * + * Return value: the newly created surface + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_win32_surface_create_with_dib (cairo_format_t format, + int width, + int height) +{ + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + return _cairo_win32_display_surface_create_for_dc (NULL, format, width, height); +} + +/** + * cairo_win32_surface_create_with_ddb: + * @hdc: a DC compatible with the surface to create + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a device-dependent-bitmap surface not associated with + * any particular existing surface or device context. The created + * bitmap will be uninitialized. + * + * Return value: the newly created surface + * + * Since: 1.4 + **/ +cairo_surface_t * +cairo_win32_surface_create_with_ddb (HDC hdc, + cairo_format_t format, + int width, + int height) +{ + cairo_win32_display_surface_t *new_surf; + HBITMAP ddb; + HDC screen_dc, ddb_dc; + HBITMAP saved_dc_bitmap; + + switch (format) { + default: +/* XXX handle these eventually */ + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + break; + } + + if (!hdc) { + screen_dc = GetDC (NULL); + hdc = screen_dc; + } else { + screen_dc = NULL; + } + + ddb_dc = CreateCompatibleDC (hdc); + if (ddb_dc == NULL) { + new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto FINISH; + } + + ddb = CreateCompatibleBitmap (hdc, width, height); + if (ddb == NULL) { + DeleteDC (ddb_dc); + + /* Note that if an app actually does hit this out of memory + * condition, it's going to have lots of other issues, as + * video memory is probably exhausted. However, it can often + * continue using DIBs instead of DDBs. + */ + new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto FINISH; + } + + saved_dc_bitmap = SelectObject (ddb_dc, ddb); + + new_surf = (cairo_win32_display_surface_t*) cairo_win32_surface_create (ddb_dc); + new_surf->bitmap = ddb; + new_surf->saved_dc_bitmap = saved_dc_bitmap; + new_surf->is_dib = FALSE; + +FINISH: + if (screen_dc) + ReleaseDC (NULL, screen_dc); + + return &new_surf->win32.base; +} diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/win32/cairo-win32-font.c similarity index 86% rename from gfx/cairo/cairo/src/cairo-win32-font.c rename to gfx/cairo/cairo/src/win32/cairo-win32-font.c index f0bdc8457e5e..d0bb09e1a0ca 100644 --- a/gfx/cairo/cairo/src/cairo-win32-font.c +++ b/gfx/cairo/cairo/src/win32/cairo-win32-font.c @@ -45,7 +45,12 @@ #include "cairoint.h" #include "cairo-win32-private.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-private.h" +#include "cairo-scaled-font-subsets-private.h" #include @@ -72,14 +77,16 @@ * * The Microsoft Windows font backend is primarily used to render text on * Microsoft Windows systems. - */ + **/ /** * CAIRO_HAS_WIN32_FONT: * * Defined if the Microsoft Windows font backend is available. * This macro can be used to conditionally compile backend-specific code. - */ + * + * Since: 1.8 + **/ const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend; @@ -131,6 +138,8 @@ typedef struct { cairo_bool_t is_bitmap; cairo_bool_t is_type1; cairo_bool_t delete_scaled_hfont; + cairo_bool_t has_type1_notdef_index; + unsigned long type1_notdef_index; } cairo_win32_scaled_font_t; static cairo_status_t @@ -153,8 +162,19 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font static HDC _get_global_font_dc (void) { - static HDC hdc; + static DWORD hdc_tls_index; + HDC hdc; + if (!hdc_tls_index) { + CAIRO_MUTEX_LOCK (_cairo_win32_font_dc_mutex); + if (!hdc_tls_index) { + hdc_tls_index = TlsAlloc (); + assert (hdc_tls_index != TLS_OUT_OF_INDEXES); + } + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_dc_mutex); + } + + hdc = TlsGetValue (hdc_tls_index); if (!hdc) { hdc = CreateCompatibleDC (NULL); if (!hdc) { @@ -167,6 +187,11 @@ _get_global_font_dc (void) DeleteDC (hdc); return NULL; } + + if (!TlsSetValue (hdc_tls_index, hdc)) { + DeleteDC (hdc); + return NULL; + } } return hdc; @@ -225,13 +250,15 @@ _compute_transform (cairo_win32_scaled_font_t *scaled_font, if (status) return status; - scaled_font->logical_size = _cairo_lround (WIN32_FONT_LOGICAL_SCALE * - scaled_font->y_scale); - scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; + scaled_font->logical_size = + _cairo_lround (WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale); + scaled_font->logical_scale = + WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; } cairo_matrix_scale (&scaled_font->logical_to_device, - 1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale); + 1.0 / scaled_font->logical_scale, + 1.0 / scaled_font->logical_scale); scaled_font->device_to_logical = scaled_font->logical_to_device; @@ -259,14 +286,14 @@ _have_cleartype_quality (void) version_info.dwMinorVersion >= 1)); /* XP or newer */ } -BYTE -cairo_win32_get_system_text_quality (void) +static BYTE +_get_system_quality (void) { BOOL font_smoothing; UINT smoothing_type; if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { - _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality"); + _cairo_win32_print_gdi_error ("_get_system_quality"); return DEFAULT_QUALITY; } @@ -274,7 +301,7 @@ cairo_win32_get_system_text_quality (void) if (_have_cleartype_quality ()) { if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type, 0)) { - _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality"); + _cairo_win32_print_gdi_error ("_get_system_quality"); return DEFAULT_QUALITY; } @@ -312,7 +339,7 @@ _win32_scaled_font_create (LOGFONTW *logfont, if (hdc == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - f = malloc (sizeof(cairo_win32_scaled_font_t)); + f = _cairo_malloc (sizeof(cairo_win32_scaled_font_t)); if (f == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -327,16 +354,19 @@ _win32_scaled_font_create (LOGFONTW *logfont, * here is the hint_metrics options. */ if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) - f->quality = cairo_win32_get_system_text_quality (); + f->quality = _get_system_quality (); else { switch (options->antialias) { case CAIRO_ANTIALIAS_NONE: f->quality = NONANTIALIASED_QUALITY; break; case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GOOD: f->quality = ANTIALIASED_QUALITY; break; case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: if (_have_cleartype_quality ()) f->quality = CLEARTYPE_QUALITY; else @@ -350,6 +380,7 @@ _win32_scaled_font_create (LOGFONTW *logfont, f->em_square = 0; f->scaled_hfont = NULL; f->unscaled_hfont = NULL; + f->has_type1_notdef_index = FALSE; if (f->quality == logfont->lfQuality || (logfont->lfQuality == DEFAULT_QUALITY && @@ -457,7 +488,7 @@ _win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, if (! otm_size) return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); - otm = malloc (otm_size); + otm = _cairo_malloc (otm_size); if (otm == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -771,14 +802,11 @@ _cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, goto FAIL1; while (TRUE) { - if (glyph_indices) { - free (glyph_indices); - glyph_indices = NULL; - } - if (dx) { - free (dx); - dx = NULL; - } + free (glyph_indices); + glyph_indices = NULL; + + free (dx); + dx = NULL; glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR)); dx = _cairo_malloc_ab (buffer_size, sizeof (int)); @@ -831,10 +859,8 @@ _cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, } FAIL2: - if (glyph_indices) - free (glyph_indices); - if (dx) - free (dx); + free (glyph_indices); + free (dx); cairo_win32_scaled_font_done_font (&scaled_font->base); @@ -879,7 +905,7 @@ _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) cairo_status_t status; cairo_font_extents_t extents; - TEXTMETRIC metrics; + TEXTMETRIC metrics = {0}; HDC hdc; hdc = _get_global_font_dc (); @@ -892,8 +918,14 @@ _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; - GetTextMetrics (hdc, &metrics); + + if (!GetTextMetrics (hdc, &metrics)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_set_metrics:GetTextMetrics"); + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + if (status) + return status; extents.ascent = metrics.tmAscent / scaled_font->logical_scale; extents.descent = metrics.tmDescent / scaled_font->logical_scale; @@ -999,18 +1031,7 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f &metrics, 0, NULL, &matrix) == GDI_ERROR) { memset (&metrics, 0, sizeof (GLYPHMETRICS)); } else { - if (metrics.gmBlackBoxX == 1 && metrics.gmBlackBoxY == 1 && - GetGlyphOutlineW (hdc, - _cairo_scaled_glyph_index (scaled_glyph), - GGO_NATIVE | GGO_GLYPH_INDEX, - &metrics, 0, NULL, &matrix) == 0) { - /* Workaround for GetGlyphOutline returning 1x1 bounding box - * for glyph that is in fact empty. - */ - metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0; - } - else if (metrics.gmBlackBoxX > 0 && - scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) { + if (metrics.gmBlackBoxX > 0 && scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) { /* The bounding box reported by Windows supposedly contains the glyph's "black" area; * however, antialiasing (especially with ClearType) means that the actual image that * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side. @@ -1050,7 +1071,6 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f extents.y_bearing = (- extents.y_bearing - extents.height); extents.y_advance = - extents.y_advance; } - } else { /* For all other transformations, we use the design metrics * of the font. @@ -1296,72 +1316,6 @@ _draw_glyphs_on_surface (cairo_win32_surface_t *surface, return status; } -/* Duplicate the green channel of a 4-channel mask in the alpha channel, then - * invert the whole mask. - */ -static void -_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; - int i, j; - - for (i = 0; i < image->height; i++) { - uint32_t *p = (uint32_t *) (image->data + i * image->stride); - for (j = 0; j < image->width; j++) { - *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); - p++; - } - } -} - -/* Invert a mask - */ -static void -_invert_argb32_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; - int i, j; - - for (i = 0; i < image->height; i++) { - uint32_t *p = (uint32_t *) (image->data + i * image->stride); - for (j = 0; j < image->width; j++) { - *p = 0xffffffff ^ *p; - p++; - } - } -} - -/* Compute an alpha-mask from a monochrome RGB24 image - */ -static cairo_surface_t * -_compute_a8_mask (cairo_win32_surface_t *mask_surface) -{ - cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; - cairo_image_surface_t *image8; - int i, j; - - if (image24->base.status) - return cairo_surface_reference (&image24->base); - - image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, - image24->width, image24->height); - if (image8->base.status) - return &image8->base; - - for (i = 0; i < image24->height; i++) { - uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); - unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); - - for (j = 0; j < image24->width; j++) { - *q = 255 - ((*p & 0x0000ff00) >> 8); - p++; - q++; - } - } - - return &image8->base; -} - static cairo_int_status_t _cairo_win32_scaled_font_glyph_init (void *abstract_font, cairo_scaled_glyph_t *scaled_glyph, @@ -1391,132 +1345,6 @@ _cairo_win32_scaled_font_glyph_init (void *abstract_font, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_win32_scaled_font_show_glyphs (void *abstract_font, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_surface_t *generic_surface, - int source_x, - int source_y, - int dest_x, - int dest_y, - unsigned int width, - unsigned int height, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_region_t *clip_region, - int *remaining_glyphs) -{ - cairo_win32_scaled_font_t *scaled_font = abstract_font; - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; - cairo_status_t status; - - if (width == 0 || height == 0) - return CAIRO_STATUS_SUCCESS; - - if (_cairo_surface_is_win32 (generic_surface) && - surface->format == CAIRO_FORMAT_RGB24 && - (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) && - op == CAIRO_OPERATOR_OVER && - _cairo_pattern_is_opaque_solid (pattern)) { - - cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; - - /* When compositing OVER on a GDI-understood surface, with a - * solid opaque color, we can just call ExtTextOut directly. - */ - COLORREF new_color; - - status = _cairo_win32_surface_set_clip_region (surface, clip_region); - if (unlikely (status)) - return status; - - new_color = RGB (((int)solid_pattern->color.red_short) >> 8, - ((int)solid_pattern->color.green_short) >> 8, - ((int)solid_pattern->color.blue_short) >> 8); - - return _draw_glyphs_on_surface (surface, scaled_font, new_color, - 0, 0, - glyphs, num_glyphs); - } else { - /* Otherwise, we need to draw using software fallbacks. We create a mask - * surface by drawing the the glyphs onto a DIB, black-on-white then - * inverting. GDI outputs gamma-corrected images so inverted black-on-white - * is very different from white-on-black. We favor the more common - * case where the final output is dark-on-light. - */ - cairo_win32_surface_t *tmp_surface; - cairo_surface_t *mask_surface; - cairo_surface_pattern_t mask; - cairo_bool_t use_subpixel_antialiasing = - scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing; - RECT r; - - tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); - if (tmp_surface->base.status) - return tmp_surface->base.status; - - r.left = 0; - r.top = 0; - r.right = width; - r.bottom = height; - FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); - - status = _draw_glyphs_on_surface (tmp_surface, - scaled_font, RGB (0, 0, 0), - dest_x, dest_y, - glyphs, num_glyphs); - if (status) { - cairo_surface_destroy (&tmp_surface->base); - return status; - } - - if (use_subpixel_antialiasing) { - /* For ClearType, we need a 4-channel mask. If we are compositing on - * a surface with alpha, we need to compute the alpha channel of - * the mask (we just copy the green channel). But for a destination - * surface without alpha the alpha channel of the mask is ignored - */ - - if (surface->format != CAIRO_FORMAT_RGB24) - _compute_argb32_mask_alpha (tmp_surface); - else - _invert_argb32_mask (tmp_surface); - - mask_surface = &tmp_surface->base; - } else { - mask_surface = _compute_a8_mask (tmp_surface); - cairo_surface_destroy (&tmp_surface->base); - status = mask_surface->status; - if (status) - return status; - } - - /* For op == OVER, no-cleartype, a possible optimization here is to - * draw onto an intermediate ARGB32 surface and alpha-blend that with the - * destination - */ - _cairo_pattern_init_for_surface (&mask, mask_surface); - cairo_surface_destroy (mask_surface); - - if (use_subpixel_antialiasing) - mask.base.has_component_alpha = TRUE; - - status = _cairo_surface_composite (op, pattern, - &mask.base, - &surface->base, - source_x, source_y, - 0, 0, - dest_x, dest_y, - width, height, - clip_region); - - _cairo_pattern_fini (&mask.base); - - return status; - } -} - static cairo_int_status_t _cairo_win32_scaled_font_load_truetype_table (void *abstract_font, unsigned long tag, @@ -1527,18 +1355,21 @@ _cairo_win32_scaled_font_load_truetype_table (void *abstract_font, cairo_win32_scaled_font_t *scaled_font = abstract_font; HDC hdc; cairo_status_t status; + DWORD ret; hdc = _get_global_font_dc (); assert (hdc != NULL); - tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24; + tag = (tag&0x000000ffu)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24; status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); if (status) return status; - *length = GetFontData (hdc, tag, offset, buffer, *length); - if (*length == GDI_ERROR) + ret = GetFontData (hdc, tag, offset, buffer, *length); + if (ret == GDI_ERROR || (buffer && ret != *length)) status = CAIRO_INT_STATUS_UNSUPPORTED; + else + *length = ret; cairo_win32_scaled_font_done_font (&scaled_font->base); @@ -1573,7 +1404,7 @@ _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, goto exit1; } - glyph_set = malloc (res); + glyph_set = _cairo_malloc (res); if (glyph_set == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto exit1; @@ -1626,10 +1457,8 @@ _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, } exit2: - if (glyph_indices) - free (glyph_indices); - if (utf16) - free (utf16); + free (glyph_indices); + free (utf16); free (glyph_set); exit1: cairo_win32_scaled_font_done_font (&scaled_font->base); @@ -1637,14 +1466,153 @@ exit1: return status; } +static cairo_int_status_t +_cairo_win32_scaled_font_is_synthetic (void *abstract_font, + cairo_bool_t *is_synthetic) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_status_t status; + int weight; + cairo_bool_t bold; + cairo_bool_t italic; + + *is_synthetic = FALSE; + status = _cairo_truetype_get_style (&scaled_font->base, + &weight, + &bold, + &italic); + /* If this doesn't work assume it is not synthetic to avoid + * unnecessary subsetting fallbacks. */ + if (status != CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + + if (scaled_font->logfont.lfWeight != weight || + scaled_font->logfont.lfItalic != italic) + *is_synthetic = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_index_to_glyph_name (void *abstract_font, + char **glyph_names, + int num_glyph_names, + unsigned long glyph_index, + unsigned long *glyph_array_index) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + int i; + + /* Windows puts .notdef at index 0 then numbers the remaining + * glyphs starting from 1 in the order they appear in the font. */ + + /* Find the position of .notdef in the list of glyph names. We + * only need to do this once per scaled font. */ + if (! scaled_font->has_type1_notdef_index) { + for (i = 0; i < num_glyph_names; i++) { + if (strcmp (glyph_names[i], ".notdef") == 0) { + scaled_font->type1_notdef_index = i; + scaled_font->has_type1_notdef_index = TRUE; + break; + } + } + if (! scaled_font->has_type1_notdef_index) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Once we know the position of .notdef the position of any glyph + * in the font can easily be obtained. */ + if (glyph_index == 0) + *glyph_array_index = scaled_font->type1_notdef_index; + else if (glyph_index <= scaled_font->type1_notdef_index) + *glyph_array_index = glyph_index - 1; + else if (glyph_index < (unsigned long)num_glyph_names) + *glyph_array_index = glyph_index; + else + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_load_type1_data (void *abstract_font, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + + if (! scaled_font->is_type1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Using the tag 0 retrieves the entire font file. This works with + * Type 1 fonts as well as TTF/OTF fonts. */ + return _cairo_win32_scaled_font_load_truetype_table (scaled_font, + 0, + offset, + buffer, + length); +} + +static cairo_surface_t * +_compute_mask (cairo_surface_t *surface, + int quality) +{ + 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; + + if (quality == CLEARTYPE_QUALITY) { + /* Duplicate the green channel of a 4-channel mask into the + * alpha channel, then invert the whole mask. + */ + mask = (cairo_image_surface_t *) + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + 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); + uint32_t *q = (uint32_t *) (mask->data + i * mask->stride); + + for (j = 0; j < glyph->width; j++) { + *q++ = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); + p++; + } + } + } + } else { + /* Compute an alpha-mask from a 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_status_t _cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) { cairo_status_t status; cairo_glyph_t glyph; - cairo_win32_surface_t *surface; - cairo_t *cr; + cairo_surface_t *surface; cairo_surface_t *image; int width, height; int x1, y1, x2, y2; @@ -1656,28 +1624,23 @@ _cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_f width = x2 - x1; height = y2 - y1; - surface = (cairo_win32_surface_t *) - cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); - - cr = cairo_create (&surface->base); - cairo_set_source_rgb (cr, 1, 1, 1); - cairo_paint (cr); - status = cairo_status (cr); - cairo_destroy(cr); + surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, + width, height); + status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, NULL); if (status) goto FAIL; glyph.index = _cairo_scaled_glyph_index (scaled_glyph); glyph.x = -x1; glyph.y = -y1; - status = _draw_glyphs_on_surface (surface, scaled_font, RGB(0,0,0), + status = _draw_glyphs_on_surface (to_win32_surface (surface), + scaled_font, RGB(0,0,0), 0, 0, &glyph, 1); if (status) goto FAIL; - GdiFlush(); - - image = _compute_a8_mask (surface); + image = _compute_mask (surface, scaled_font->quality); status = image->status; if (status) goto FAIL; @@ -1688,7 +1651,7 @@ _cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_f (cairo_image_surface_t *) image); FAIL: - cairo_surface_destroy (&surface->base); + cairo_surface_destroy (surface); return status; } @@ -1749,8 +1712,8 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font goto CLEANUP_FONT; } - ptr = buffer = malloc (bytesGlyph); - if (!buffer) { + ptr = buffer = _cairo_malloc (bytesGlyph); + if (!buffer && bytesGlyph != 0) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP_FONT; } @@ -1882,9 +1845,11 @@ const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { _cairo_win32_scaled_font_glyph_init, NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */ _cairo_win32_scaled_font_ucs4_to_index, - _cairo_win32_scaled_font_show_glyphs, _cairo_win32_scaled_font_load_truetype_table, _cairo_win32_scaled_font_index_to_ucs4, + _cairo_win32_scaled_font_is_synthetic, + _cairo_win32_scaled_font_index_to_glyph_name, + _cairo_win32_scaled_font_load_type1_data }; /* #cairo_win32_font_face_t */ @@ -1902,52 +1867,6 @@ struct _cairo_win32_font_face { HFONT hfont; }; -/* implement the platform-specific interface */ - -static void -_cairo_win32_font_face_destroy (void *abstract_face); - -static cairo_bool_t -_is_scale (const cairo_matrix_t *matrix, double scale) -{ - return matrix->xx == scale && matrix->yy == scale && - matrix->xy == 0. && matrix->yx == 0. && - matrix->x0 == 0. && matrix->y0 == 0.; -} - -static cairo_status_t -_cairo_win32_font_face_scaled_font_create (void *abstract_face, - const cairo_matrix_t *font_matrix, - const cairo_matrix_t *ctm, - const cairo_font_options_t *options, - cairo_scaled_font_t **font) -{ - HFONT hfont = NULL; - - cairo_win32_font_face_t *font_face = 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)) { - hfont = font_face->hfont; - } - } - - return _win32_scaled_font_create (&font_face->logfont, - hfont, - &font_face->base, - font_matrix, ctm, options, - font); -} - -const cairo_font_face_backend_t _cairo_win32_font_face_backend = { - CAIRO_FONT_TYPE_WIN32, - _cairo_win32_font_face_create_for_toy, - _cairo_win32_font_face_destroy, - _cairo_win32_font_face_scaled_font_create -}; - /* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t. * The primary purpose of this mapping is to provide unique * #cairo_font_face_t values so that our cache and mapping from @@ -1957,11 +1876,6 @@ const cairo_font_face_backend_t _cairo_win32_font_face_backend = { * * Modifications to this hash table are protected by * _cairo_win32_font_face_mutex. - * - * Only #cairo_font_face_t values with null 'hfont' (no - * HFONT preallocated by caller) are stored in this table. We rely - * on callers to manage the lifetime of the HFONT, and they can't - * do that if we share #cairo_font_face_t values with other callers. */ static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL; @@ -2013,6 +1927,34 @@ _cairo_win32_font_face_hash_table_unlock (void) CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); } +static cairo_bool_t +_cairo_win32_font_face_destroy (void *abstract_face) +{ + cairo_win32_font_face_t *font_face = abstract_face; + cairo_hash_table_t *hash_table; + + hash_table = _cairo_win32_font_face_hash_table_lock (); + /* All created objects must have been mapped in the hash table. */ + assert (hash_table != NULL); + + if (! _cairo_reference_count_dec_and_test (&font_face->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_win32_font_face_hash_table_unlock (); + return FALSE; + } + + /* Font faces in SUCCESS status are guaranteed to be in the + * hashtable. Font faces in an error status are removed from the + * hashtable if they are found during a lookup, thus they should + * only be removed if they are in the hashtable. */ + if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) || + _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face) + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + + _cairo_win32_font_face_hash_table_unlock (); + return TRUE; +} + static void _cairo_win32_font_face_init_key (cairo_win32_font_face_t *key, LOGFONTW *logfont, @@ -2051,22 +1993,49 @@ _cairo_win32_font_face_keys_equal (const void *key_a, return FALSE; } -static void -_cairo_win32_font_face_destroy (void *abstract_face) +/* implement the platform-specific interface */ + +static cairo_bool_t +_is_scale (const cairo_matrix_t *matrix, double scale) { - cairo_hash_table_t *hash_table; + return matrix->xx == scale && matrix->yy == scale && + matrix->xy == 0. && matrix->yx == 0. && + matrix->x0 == 0. && matrix->y0 == 0.; +} + +static cairo_status_t +_cairo_win32_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font) +{ + HFONT hfont = NULL; + cairo_win32_font_face_t *font_face = abstract_face; - if (!font_face->hfont) { - hash_table = _cairo_win32_font_face_hash_table_lock (); - if (unlikely (hash_table == NULL)) { - return; + 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)) { + hfont = font_face->hfont; } - _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); - _cairo_win32_font_face_hash_table_unlock (); } + + return _win32_scaled_font_create (&font_face->logfont, + hfont, + &font_face->base, + font_matrix, ctm, options, + font); } +const cairo_font_face_backend_t _cairo_win32_font_face_backend = { + CAIRO_FONT_TYPE_WIN32, + _cairo_win32_font_face_create_for_toy, + _cairo_win32_font_face_destroy, + _cairo_win32_font_face_scaled_font_create +}; + /** * cairo_win32_font_face_create_for_logfontw_hfont: * @logfont: A #LOGFONTW structure specifying the font to use. @@ -2085,6 +2054,8 @@ _cairo_win32_font_face_destroy (void *abstract_face) * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.6 **/ cairo_font_face_t * cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) @@ -2093,26 +2064,30 @@ cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) cairo_hash_table_t *hash_table; cairo_status_t status; - if (!font) { - hash_table = _cairo_win32_font_face_hash_table_lock (); - if (unlikely (hash_table == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; - } + hash_table = _cairo_win32_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } - _cairo_win32_font_face_init_key (&key, logfont, font); + _cairo_win32_font_face_init_key (&key, logfont, font); - /* Return existing unscaled font if it exists in the hash table. */ - font_face = _cairo_hash_table_lookup (hash_table, - &key.base.hash_entry); - if (font_face != NULL) { + /* Return existing unscaled font if it exists in the hash table. */ + font_face = _cairo_hash_table_lookup (hash_table, + &key.base.hash_entry); + if (font_face != NULL) { + if (font_face->base.status == CAIRO_STATUS_SUCCESS) { cairo_font_face_reference (&font_face->base); - goto DONE; - } + _cairo_win32_font_face_hash_table_unlock (); + return &font_face->base; + } + + /* remove the bad font from the hash table */ + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); } /* Otherwise create it and insert into hash table. */ - font_face = malloc (sizeof (cairo_win32_font_face_t)); + font_face = _cairo_malloc (sizeof (cairo_win32_font_face_t)); if (!font_face) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); goto FAIL; @@ -2121,26 +2096,17 @@ cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) _cairo_win32_font_face_init_key (font_face, logfont, font); _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); - if (!font) { - assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); - status = _cairo_hash_table_insert (hash_table, - &font_face->base.hash_entry); - if (unlikely (status)) - goto FAIL; - } - -DONE: - if (!font) { - _cairo_win32_font_face_hash_table_unlock (); - } + assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (hash_table, + &font_face->base.hash_entry); + if (unlikely (status)) + goto FAIL; + _cairo_win32_font_face_hash_table_unlock (); return &font_face->base; FAIL: - if (!font) { - _cairo_win32_font_face_hash_table_unlock (); - } - + _cairo_win32_font_face_hash_table_unlock (); return (cairo_font_face_t *)&_cairo_font_face_nil; } @@ -2159,6 +2125,8 @@ FAIL: * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.0 **/ cairo_font_face_t * cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) @@ -2179,6 +2147,8 @@ cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) * * Return value: a newly created #cairo_font_face_t. Free with * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.2 **/ cairo_font_face_t * cairo_win32_font_face_create_for_hfont (HFONT font) @@ -2205,7 +2175,7 @@ _cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) /** * cairo_win32_scaled_font_select_font: * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an - * object can be created with cairo_win32_scaled_font_create_for_logfontw(). + * object can be created with cairo_win32_font_face_create_for_logfontw(). * @hdc: a device context * * Selects the font into the given device context and changes the @@ -2225,6 +2195,8 @@ _cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and * the device context is unchanged. + * + * Since: 1.0 **/ cairo_status_t cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, @@ -2274,6 +2246,8 @@ cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, * @scaled_font: A scaled font from the Win32 font backend. * * Releases any resources allocated by cairo_win32_scaled_font_select_font() + * + * Since: 1.0 **/ void cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) @@ -2294,6 +2268,8 @@ cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) * * Return value: factor to multiply logical units by to get font space * coordinates. + * + * Since: 1.0 **/ double cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font) diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-gdi-compositor.c b/gfx/cairo/cairo/src/win32/cairo-win32-gdi-compositor.c new file mode 100644 index 000000000000..1d1d7f87370c --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-gdi-compositor.c @@ -0,0 +1,671 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2012 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +/* The original X drawing API was very restrictive in what it could handle, + * pixel-aligned fill/blits are all that map into Cairo's drawing model. + */ + +#include "cairoint.h" + +#include "cairo-win32-private.h" + +#include "cairo-boxes-private.h" +#include "cairo-clip-inline.h" +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-private.h" +#include "cairo-region-private.h" +#include "cairo-surface-inline.h" +#include "cairo-surface-offset-private.h" + +#if !defined(AC_SRC_OVER) +#define AC_SRC_OVER 0x00 +#pragma pack(1) +typedef struct { + BYTE BlendOp; + BYTE BlendFlags; + BYTE SourceConstantAlpha; + BYTE AlphaFormat; +}BLENDFUNCTION; +#pragma pack() +#endif + +/* for compatibility with VC++ 6 */ +#ifndef AC_SRC_ALPHA +#define AC_SRC_ALPHA 0x01 +#endif + +#define PELS_72DPI ((LONG)(72. / 0.0254)) + +/* the low-level interface */ + +struct fill_box { + HDC dc; + HBRUSH brush; +}; + +static cairo_bool_t fill_box (cairo_box_t *box, void *closure) +{ + struct fill_box *fb = closure; + RECT rect; + + rect.left = _cairo_fixed_integer_part (box->p1.x); + rect.top = _cairo_fixed_integer_part (box->p1.y); + rect.right = _cairo_fixed_integer_part (box->p2.x); + rect.bottom = _cairo_fixed_integer_part (box->p2.y); + + TRACE ((stderr, "%s\n", __FUNCTION__)); + return FillRect (fb->dc, &rect, fb->brush); +} + +struct check_box { + cairo_rectangle_int_t limit; + int tx, ty; +}; + +struct copy_box { + cairo_rectangle_int_t limit; + int tx, ty; + HDC dst, src; + BLENDFUNCTION bf; + cairo_win32_alpha_blend_func_t alpha_blend; +}; + +static cairo_bool_t copy_box (cairo_box_t *box, void *closure) +{ + const struct copy_box *cb = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + + TRACE ((stderr, "%s\n", __FUNCTION__)); + return BitBlt (cb->dst, x, y, width, height, + cb->src, x + cb->tx, y + cb->ty, + SRCCOPY); +} + +static cairo_bool_t alpha_box (cairo_box_t *box, void *closure) +{ + const struct copy_box *cb = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + + TRACE ((stderr, "%s\n", __FUNCTION__)); + return cb->alpha_blend (cb->dst, x, y, width, height, + cb->src, x + cb->tx, y + cb->ty, width, height, + cb->bf); +} + +struct upload_box { + cairo_rectangle_int_t limit; + int tx, ty; + HDC dst; + BITMAPINFO bi; + void *data; +}; + +static cairo_bool_t upload_box (cairo_box_t *box, void *closure) +{ + const struct upload_box *cb = closure; + int x = _cairo_fixed_integer_part (box->p1.x); + int y = _cairo_fixed_integer_part (box->p1.y); + int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x); + int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); + int src_height = -cb->bi.bmiHeader.biHeight; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + return StretchDIBits (cb->dst, x, y + height - 1, width, -height, + x + cb->tx, src_height - (y + cb->ty - 1), + width, -height, + cb->data, &cb->bi, + DIB_RGB_COLORS, SRCCOPY); +} + +/* the mid-level: converts boxes into drawing operations */ + +static COLORREF color_to_rgb(const cairo_color_t *c) +{ + return RGB (c->red_short >> 8, c->green_short >> 8, c->blue_short >> 8); +} + +static cairo_int_status_t +fill_boxes (cairo_win32_display_surface_t *dst, + const cairo_pattern_t *src, + cairo_boxes_t *boxes) +{ + const cairo_color_t *color = &((cairo_solid_pattern_t *) src)->color; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + struct fill_box fb; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + fb.dc = dst->win32.dc; + fb.brush = CreateSolidBrush (color_to_rgb(color)); + if (!fb.brush) + return _cairo_win32_print_gdi_error (__FUNCTION__); + + if (! _cairo_boxes_for_each_box (boxes, fill_box, &fb)) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + DeleteObject (fb.brush); + + return status; +} + +static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure) +{ + struct check_box *data = closure; + + /* The box is pixel-aligned so the truncation is safe. */ + return + _cairo_fixed_integer_part (box->p1.x) + data->tx >= data->limit.x && + _cairo_fixed_integer_part (box->p1.y) + data->ty >= data->limit.y && + _cairo_fixed_integer_part (box->p2.x) + data->tx <= data->limit.x + data->limit.width && + _cairo_fixed_integer_part (box->p2.y) + data->ty <= data->limit.y + data->limit.height; +} + +static cairo_status_t +copy_boxes (cairo_win32_display_surface_t *dst, + const cairo_pattern_t *source, + cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *pattern; + struct copy_box cb; + cairo_surface_t *surface; + cairo_status_t status; + cairo_win32_surface_t *src; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + pattern = (const cairo_surface_pattern_t *) source; + surface = _cairo_surface_get_source (pattern->surface, &cb.limit); + if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + surface = to_image_surface(surface)->parent; + if (surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (surface->type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, + &cb.tx, &cb.ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + src = to_win32_surface(surface); + + if (src->format != dst->win32.format && + !(src->format == CAIRO_FORMAT_ARGB32 && dst->win32.format == CAIRO_FORMAT_RGB24)) + { + /* forbid copy different surfaces unless it is from argb32 to + * rgb (dropping alpha) */ + return CAIRO_INT_STATUS_UNSUPPORTED; + } + cb.dst = dst->win32.dc; + cb.src = src->dc; + + /* First check that the data is entirely within the image */ + if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = __cairo_surface_flush (surface, 0); + if (status) + return status; + + cb.tx += cb.limit.x; + cb.ty += cb.limit.y; + status = CAIRO_STATUS_SUCCESS; + if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb)) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_win32_display_surface_discard_fallback (dst); + return status; +} + +static cairo_status_t +upload_boxes (cairo_win32_display_surface_t *dst, + const cairo_pattern_t *source, + cairo_boxes_t *boxes) +{ + const cairo_surface_pattern_t *pattern; + struct upload_box cb; + cairo_surface_t *surface; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, + &cb.tx, &cb.ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pattern = (const cairo_surface_pattern_t *) source; + surface = _cairo_surface_get_source (pattern->surface, &cb.limit); + + /* First check that the data is entirely within the image */ + if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) { + status = _cairo_surface_acquire_source_image (surface, + &image, &image_extra); + if (status) + return status; + } else + image = to_image_surface(surface); + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (!(image->format == CAIRO_FORMAT_ARGB32 || + image->format == CAIRO_FORMAT_RGB24)) + goto err; + if (image->stride != 4*image->width) + goto err; + + cb.dst = dst->win32.dc; + cb.data = image->data; + + cb.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + cb.bi.bmiHeader.biWidth = image->width; + cb.bi.bmiHeader.biHeight = -image->height; + cb.bi.bmiHeader.biSizeImage = 0; + cb.bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + cb.bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + cb.bi.bmiHeader.biPlanes = 1; + cb.bi.bmiHeader.biBitCount = 32; + cb.bi.bmiHeader.biCompression = BI_RGB; + cb.bi.bmiHeader.biClrUsed = 0; + cb.bi.bmiHeader.biClrImportant = 0; + + cb.tx += cb.limit.x; + cb.ty += cb.limit.y; + status = CAIRO_STATUS_SUCCESS; + if (! _cairo_boxes_for_each_box (boxes, upload_box, &cb)) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_win32_display_surface_discard_fallback (dst); +err: + if (&image->base != surface) + _cairo_surface_release_source_image (surface, image, image_extra); + + return status; +} + +static cairo_status_t +alpha_blend_boxes (cairo_win32_display_surface_t *dst, + const cairo_pattern_t *source, + cairo_boxes_t *boxes, + uint8_t alpha) +{ + const cairo_surface_pattern_t *pattern; + struct copy_box cb; + cairo_surface_t *surface; + cairo_win32_display_surface_t *src; + cairo_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pattern = (const cairo_surface_pattern_t *) source; + surface = _cairo_surface_get_source (pattern->surface, &cb.limit); + if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + surface = to_image_surface(surface)->parent; + if (surface == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (surface->type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_matrix_is_integer_translation (&source->matrix, + &cb.tx, &cb.ty)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + src = to_win32_display_surface (surface); + cb.dst = dst->win32.dc; + cb.src = src->win32.dc; + + /* First check that the data is entirely within the image */ + if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = __cairo_surface_flush (&src->win32.base, 0); + if (status) + return status; + + cb.bf.BlendOp = AC_SRC_OVER; + cb.bf.BlendFlags = 0; + cb.bf.SourceConstantAlpha = alpha; + cb.bf.AlphaFormat = (src->win32.format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; + cb.alpha_blend = to_win32_device(dst->win32.base.device)->alpha_blend; + + cb.tx += cb.limit.x; + cb.ty += cb.limit.y; + status = CAIRO_STATUS_SUCCESS; + if (! _cairo_boxes_for_each_box (boxes, alpha_box, &cb)) + status = CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_win32_display_surface_discard_fallback (dst); + return status; +} + +static cairo_bool_t +can_alpha_blend (cairo_win32_display_surface_t *dst) +{ + if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND) == 0) + return FALSE; + + return to_win32_device(dst->win32.base.device)->alpha_blend != NULL; +} + +static cairo_status_t +draw_boxes (cairo_composite_rectangles_t *composite, + cairo_boxes_t *boxes) +{ + cairo_win32_display_surface_t *dst = to_win32_display_surface(composite->surface); + cairo_operator_t op = composite->op; + const cairo_pattern_t *src = &composite->source_pattern.base; + cairo_int_status_t status; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (boxes->num_boxes == 0 && composite->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (!boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op == CAIRO_OPERATOR_CLEAR) + op = CAIRO_OPERATOR_SOURCE; + + if (op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_opaque (src, &composite->bounded)) + op = CAIRO_OPERATOR_SOURCE; + + if (dst->win32.base.is_clear && + (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)) + op = CAIRO_OPERATOR_SOURCE; + + if (op == CAIRO_OPERATOR_SOURCE) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (src->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = copy_boxes (dst, src, boxes); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = upload_boxes (dst, src, boxes); + } else if (src->type == CAIRO_PATTERN_TYPE_SOLID) { + status = fill_boxes (dst, src, boxes); + } + return status; + } + + if (op == CAIRO_OPERATOR_OVER && can_alpha_blend (dst)) + return alpha_blend_boxes (dst, src, boxes, 255); + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +opacity_boxes (cairo_composite_rectangles_t *composite, + cairo_boxes_t *boxes) +{ + cairo_win32_display_surface_t *dst = to_win32_display_surface(composite->surface); + cairo_operator_t op = composite->op; + const cairo_pattern_t *src = &composite->source_pattern.base; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (boxes->num_boxes == 0 && composite->is_bounded) + return CAIRO_STATUS_SUCCESS; + + if (!boxes->is_pixel_aligned) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op != CAIRO_OPERATOR_OVER) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!can_alpha_blend (dst)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return alpha_blend_boxes (dst, src, boxes, + composite->mask_pattern.solid.color.alpha_short >> 8); +} + +/* high-level compositor interface */ + +static cairo_bool_t check_blit (cairo_composite_rectangles_t *composite) +{ + cairo_win32_display_surface_t *dst; + + if (composite->clip->path) + return FALSE; + + dst = to_win32_display_surface (composite->surface); + if (dst->fallback) + return FALSE; + + if (dst->win32.format != CAIRO_FORMAT_RGB24 + && dst->win32.format != CAIRO_FORMAT_ARGB32) + return FALSE; + + if (dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) + return TRUE; + + return dst->image == NULL; +} + +static cairo_int_status_t +_cairo_win32_gdi_compositor_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *composite) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (check_blit (composite)) { + cairo_boxes_t boxes; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_clip_steal_boxes (composite->clip, &boxes); + status = draw_boxes (composite, &boxes); + _cairo_clip_unsteal_boxes (composite->clip, &boxes); + } + + return status; +} + +static cairo_int_status_t +_cairo_win32_gdi_compositor_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *composite) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (check_blit (composite)) { + cairo_boxes_t boxes; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_clip_steal_boxes (composite->clip, &boxes); + status = opacity_boxes (composite, &boxes); + _cairo_clip_unsteal_boxes (composite->clip, &boxes); + } + + return status; +} + +static cairo_int_status_t +_cairo_win32_gdi_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; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (check_blit (composite) && + _cairo_path_fixed_stroke_is_rectilinear (path)) { + cairo_boxes_t boxes; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_boxes_init_with_clip (&boxes, composite->clip); + status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, + style, + ctm, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = draw_boxes (composite, &boxes); + _cairo_boxes_fini (&boxes); + } + + return status; +} + +static cairo_int_status_t +_cairo_win32_gdi_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_int_status_t status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (check_blit (composite) && + _cairo_path_fixed_fill_is_rectilinear (path)) { + cairo_boxes_t boxes; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + _cairo_boxes_init_with_clip (&boxes, composite->clip); + status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, + fill_rule, + antialias, + &boxes); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = draw_boxes (composite, &boxes); + _cairo_boxes_fini (&boxes); + } + + return status; +} + +static cairo_bool_t check_glyphs (cairo_composite_rectangles_t *composite, + cairo_scaled_font_t *scaled_font) +{ + if (! _cairo_clip_is_region (composite->clip)) + return FALSE; + + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) + return FALSE; + + if (! _cairo_pattern_is_opaque_solid (&composite->source_pattern.base)) + return FALSE; + + return (composite->op == CAIRO_OPERATOR_CLEAR || + composite->op == CAIRO_OPERATOR_SOURCE || + composite->op == CAIRO_OPERATOR_OVER); +} + +static cairo_int_status_t +_cairo_win32_gdi_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; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (check_blit (composite) && check_glyphs (composite, scaled_font)) { + cairo_win32_display_surface_t *dst = to_win32_display_surface (composite->surface); + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH) == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_win32_display_surface_set_clip(dst, composite->clip); + if (status) + return status; + + status = _cairo_win32_surface_emit_glyphs (&dst->win32, + &composite->source_pattern.base, + glyphs, + num_glyphs, + scaled_font, + TRUE); + + _cairo_win32_display_surface_unset_clip (dst); + } + + return status; +} + +const cairo_compositor_t * +_cairo_win32_gdi_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)) { + compositor.delegate = &_cairo_fallback_compositor; + + compositor.paint = _cairo_win32_gdi_compositor_paint; + compositor.mask = _cairo_win32_gdi_compositor_mask; + compositor.fill = _cairo_win32_gdi_compositor_fill; + compositor.stroke = _cairo_win32_gdi_compositor_stroke; + compositor.glyphs = _cairo_win32_gdi_compositor_glyphs; + + _cairo_atomic_init_once_leave(&once); + } + + return &compositor; +} diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c similarity index 65% rename from gfx/cairo/cairo/src/cairo-win32-printing-surface.c rename to gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c index bc636cd36796..094068c159af 100644 --- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c +++ b/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c @@ -46,15 +46,22 @@ #include "cairoint.h" +#include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-paginated-private.h" #include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" #include "cairo-win32-private.h" -#include "cairo-recording-surface-private.h" +#include "cairo-recording-surface-inline.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-image-info-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" #include "cairo-surface-clipper-private.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-subsurface-private.h" #include @@ -88,40 +95,47 @@ #define PELS_72DPI ((LONG)(72. / 0.0254)) +static const char *_cairo_win32_printing_supported_mime_types[] = +{ + CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_PNG, + NULL +}; + static const cairo_surface_backend_t cairo_win32_printing_surface_backend; static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend; static void -_cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface) +_cairo_win32_printing_surface_init_ps_mode (cairo_win32_printing_surface_t *surface) { DWORD word; INT ps_feature, ps_level; word = PSIDENT_GDICENTRIC; - if (ExtEscape (surface->dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) + if (ExtEscape (surface->win32.dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) return; ps_feature = FEATURESETTING_PSLEVEL; - if (ExtEscape (surface->dc, GET_PS_FEATURESETTING, sizeof(INT), + if (ExtEscape (surface->win32.dc, GET_PS_FEATURESETTING, sizeof(INT), (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0) return; if (ps_level >= 3) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; + surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; } static void -_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface) +_cairo_win32_printing_surface_init_image_support (cairo_win32_printing_surface_t *surface) { DWORD word; word = CHECKJPEGFORMAT; - if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; + if (ExtEscape(surface->win32.dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; word = CHECKPNGFORMAT; - if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) - surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; + if (ExtEscape(surface->win32.dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->win32.flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; } /* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not @@ -142,7 +156,7 @@ _cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface * and argument 0. */ static void -_cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface) +_cairo_win32_printing_surface_init_language_pack (cairo_win32_printing_surface_t *surface) { typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); gdi_init_lang_pack_func_t gdi_init_lang_pack; @@ -160,20 +174,145 @@ _cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface } } -static cairo_int_status_t -analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) +/** + * _cairo_win32_printing_surface_acquire_image_pattern: + * @surface: the win32 printing surface + * @pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source + * @extents: extents of the operation that is using this source + * @image_pattern: returns pattern containing acquired image. The matrix (adjusted for + * the device offset of raster source) is copied from the pattern. + * @width: returns width of the pattern + * @height: returns height of pattern + * @image_extra: returns image extra for image type surface + * + * Acquire source surface or raster source pattern. + **/ +static cairo_status_t +_cairo_win32_printing_surface_acquire_image_pattern ( + cairo_win32_printing_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_surface_pattern_t *image_pattern, + int *width, + int *height, + void **image_extra) { + cairo_status_t status; + cairo_image_surface_t *image; + cairo_matrix_t tm; + double x = 0; + double y = 0; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_t *surf = ((cairo_surface_pattern_t *) pattern)->surface; + + status = _cairo_surface_acquire_source_image (surf, &image, image_extra); + if (unlikely (status)) + return status; + + *width = image->width; + *height = image->height; + } break; + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: { + cairo_surface_t *surf; + cairo_box_t box; + cairo_rectangle_int_t rect; + cairo_raster_source_pattern_t *raster; + + /* get the operation extents in pattern space */ + _cairo_box_from_rectangle (&box, extents); + _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); + _cairo_box_round_to_rectangle (&box, &rect); + surf = _cairo_raster_source_pattern_acquire (pattern, &surface->win32.base, &rect); + if (!surf) + return CAIRO_INT_STATUS_UNSUPPORTED; + + assert (_cairo_surface_is_image (surf)); + image = (cairo_image_surface_t *) surf; + cairo_surface_get_device_offset (surf, &x, &y); + + raster = (cairo_raster_source_pattern_t *) pattern; + *width = raster->extents.width; + *height = raster->extents.height; + } break; + + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + default: + ASSERT_NOT_REACHED; + break; + } + + _cairo_pattern_init_for_surface (image_pattern, &image->base); + image_pattern->base.extend = pattern->extend; + cairo_matrix_init_translate (&tm, x, y); + status = cairo_matrix_invert (&tm); + /* translation matrices are invertibile */ + assert (status == CAIRO_STATUS_SUCCESS); + + image_pattern->base.matrix = pattern->matrix; + cairo_matrix_multiply (&image_pattern->base.matrix, &image_pattern->base.matrix, &tm); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_printing_surface_release_image_pattern (cairo_win32_printing_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_surface_pattern_t *image_pattern, + void *image_extra) +{ + cairo_surface_t *surf = image_pattern->surface; + + _cairo_pattern_fini (&image_pattern->base); + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern; + cairo_image_surface_t *image = (cairo_image_surface_t *) surf; + _cairo_surface_release_source_image (surf_pat->surface, image, image_extra); + } break; + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + _cairo_raster_source_pattern_release (pattern, surf); + break; + + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + default: + ASSERT_NOT_REACHED; + break; + } +} + +static cairo_int_status_t +analyze_surface_pattern_transparency (cairo_win32_printing_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_pattern_t image_pattern; cairo_image_surface_t *image; void *image_extra; cairo_int_status_t status; cairo_image_transparency_t transparency; + int pattern_width, pattern_height; - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &image_extra); + status = _cairo_win32_printing_surface_acquire_image_pattern (surface, + pattern, + extents, + &image_pattern, + &pattern_width, + &pattern_height, + &image_extra); if (status) return status; + image = (cairo_image_surface_t *)(image_pattern.surface); transparency = _cairo_image_analyze_transparency (image); switch (transparency) { case CAIRO_IMAGE_UNKNOWN: @@ -188,7 +327,7 @@ analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) break; } - _cairo_surface_release_source_image (pattern->surface, image, image_extra); + _cairo_win32_printing_surface_release_image_pattern (surface, pattern, &image_pattern, image_extra); return status; } @@ -199,8 +338,7 @@ surface_pattern_supported (const cairo_surface_pattern_t *pattern) if (_cairo_surface_is_recording (pattern->surface)) return TRUE; - if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 && - pattern->surface->backend->acquire_source_image == NULL) + if (pattern->surface->backend->acquire_source_image == NULL) { return FALSE; } @@ -209,24 +347,36 @@ surface_pattern_supported (const cairo_surface_pattern_t *pattern) } static cairo_bool_t -pattern_supported (cairo_win32_surface_t *surface, const cairo_pattern_t *pattern) +pattern_supported (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *pattern) { - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: return TRUE; - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) - return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_LINEAR: + return surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; - if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) - return surface->flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + return FALSE; - return FALSE; + case CAIRO_PATTERN_TYPE_SURFACE: + return surface_pattern_supported ((cairo_surface_pattern_t *) pattern); + + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + return TRUE; + + default: + ASSERT_NOT_REACHED; + return FALSE; + } } static cairo_int_status_t -_cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) +_cairo_win32_printing_surface_analyze_operation (cairo_win32_printing_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) { if (! pattern_supported (surface, pattern)) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -239,7 +389,7 @@ _cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - if ( _cairo_surface_is_recording (surface_pattern->surface)) + if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; } @@ -257,11 +407,8 @@ _cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, * background to convert the pattern to opaque. */ - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - return analyze_surface_pattern_transparency (surface_pattern); - } + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE || pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + return analyze_surface_pattern_transparency (surface, pattern, extents); if (_cairo_pattern_is_opaque (pattern, NULL)) return CAIRO_STATUS_SUCCESS; @@ -270,18 +417,19 @@ _cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, } static cairo_bool_t -_cairo_win32_printing_surface_operation_supported (cairo_win32_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern) +_cairo_win32_printing_surface_operation_supported (cairo_win32_printing_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) { - if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) + if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED) return TRUE; else return FALSE; } static void -_cairo_win32_printing_surface_init_clear_color (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_init_clear_color (cairo_win32_printing_surface_t *surface, cairo_solid_pattern_t *color) { if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) @@ -291,7 +439,7 @@ _cairo_win32_printing_surface_init_clear_color (cairo_win32_surface_t *surface, } static COLORREF -_cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_flatten_transparency (cairo_win32_printing_surface_t *surface, const cairo_color_t *color) { COLORREF c; @@ -322,7 +470,7 @@ _cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surfa } static cairo_status_t -_cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_select_solid_brush (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *source) { cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source; @@ -333,66 +481,66 @@ _cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface surface->brush = CreateSolidBrush (color); if (!surface->brush) return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)"); - surface->old_brush = SelectObject (surface->dc, surface->brush); + surface->old_brush = SelectObject (surface->win32.dc, surface->brush); return CAIRO_STATUS_SUCCESS; } static void -_cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface) +_cairo_win32_printing_surface_done_solid_brush (cairo_win32_printing_surface_t *surface) { if (surface->old_brush) { - SelectObject (surface->dc, surface->old_brush); + SelectObject (surface->win32.dc, surface->old_brush); DeleteObject (surface->brush); surface->old_brush = NULL; } } static cairo_status_t -_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_printing_surface_t *surface, RECT *clip) { XFORM xform; _cairo_matrix_to_win32_xform (&surface->ctm, &xform); - if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) + if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); - GetClipBox (surface->dc, clip); + GetClipBox (surface->win32.dc, clip); _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); - if (!SetWorldTransform (surface->dc, &xform)) + if (!SetWorldTransform (surface->win32.dc, &xform)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *pattern) { RECT clip; cairo_status_t status; - GetClipBox (surface->dc, &clip); + GetClipBox (surface->win32.dc, &clip); status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern); if (status) return status; - FillRect (surface->dc, &clip, surface->brush); + FillRect (surface->win32.dc, &clip, surface->brush); _cairo_win32_printing_surface_done_solid_brush (surface); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_printing_surface_t *surface, cairo_surface_pattern_t *pattern) { cairo_content_t old_content; cairo_matrix_t old_ctm; cairo_bool_t old_has_ctm; cairo_rectangle_int_t recording_extents; - cairo_status_t status; + cairo_int_status_t status; cairo_extend_t extend; cairo_matrix_t p2d; XFORM xform; @@ -400,30 +548,45 @@ _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t * RECT clip; cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; cairo_box_t bbox; + cairo_surface_t *free_me = NULL; + cairo_bool_t is_subsurface; extend = cairo_pattern_get_extend (&pattern->base); p2d = pattern->base.matrix; status = cairo_matrix_invert (&p2d); /* _cairo_pattern_set_matrix guarantees invertibility */ - assert (status == CAIRO_STATUS_SUCCESS); + assert (status == CAIRO_INT_STATUS_SUCCESS); old_ctm = surface->ctm; old_has_ctm = surface->has_ctm; cairo_matrix_multiply (&p2d, &p2d, &surface->ctm); surface->ctm = p2d; - SaveDC (surface->dc); + SaveDC (surface->win32.dc); _cairo_matrix_to_win32_xform (&p2d, &xform); - status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); - if (status) - return status; + if (_cairo_surface_is_snapshot (&recording_surface->base)) { + free_me = _cairo_surface_snapshot_get_target (&recording_surface->base); + recording_surface = (cairo_recording_surface_t *) free_me; + } - _cairo_box_round_to_rectangle (&bbox, &recording_extents); + if (recording_surface->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) recording_surface; + + recording_surface = (cairo_recording_surface_t *) (sub->target); + recording_extents = sub->extents; + is_subsurface = TRUE; + } else { + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); + if (status) + goto err; + + _cairo_box_round_to_rectangle (&bbox, &recording_extents); + } status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip); if (status) - return status; + goto err; if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); @@ -443,7 +606,7 @@ _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t * status = _cairo_win32_printing_surface_paint_solid_pattern (surface, &_cairo_pattern_black.base); if (status) - return status; + goto err; } for (y_tile = top; y_tile < bottom; y_tile++) { @@ -451,7 +614,7 @@ _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t * cairo_matrix_t m; double x, y; - SaveDC (surface->dc); + SaveDC (surface->win32.dc); m = p2d; cairo_matrix_translate (&m, x_tile*recording_extents.width, @@ -470,55 +633,58 @@ _cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t * surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); /* Set clip path around bbox of the pattern. */ - BeginPath (surface->dc); + BeginPath (surface->win32.dc); x = 0; y = 0; cairo_matrix_transform_point (&surface->ctm, &x, &y); - MoveToEx (surface->dc, (int) x, (int) y, NULL); + MoveToEx (surface->win32.dc, (int) x, (int) y, NULL); x = recording_extents.width; y = 0; cairo_matrix_transform_point (&surface->ctm, &x, &y); - LineTo (surface->dc, (int) x, (int) y); + LineTo (surface->win32.dc, (int) x, (int) y); x = recording_extents.width; y = recording_extents.height; cairo_matrix_transform_point (&surface->ctm, &x, &y); - LineTo (surface->dc, (int) x, (int) y); + LineTo (surface->win32.dc, (int) x, (int) y); x = 0; y = recording_extents.height; cairo_matrix_transform_point (&surface->ctm, &x, &y); - LineTo (surface->dc, (int) x, (int) y); + LineTo (surface->win32.dc, (int) x, (int) y); - CloseFigure (surface->dc); - EndPath (surface->dc); - SelectClipPath (surface->dc, RGN_AND); + CloseFigure (surface->win32.dc); + EndPath (surface->win32.dc); + SelectClipPath (surface->win32.dc, RGN_AND); - SaveDC (surface->dc); /* Allow clip path to be reset during replay */ - status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL, - &surface->base, + SaveDC (surface->win32.dc); /* Allow clip path to be reset during replay */ + status = _cairo_recording_surface_replay_region (&recording_surface->base, + is_subsurface ? &recording_extents : NULL, + &surface->win32.base, CAIRO_RECORDING_REGION_NATIVE); assert (status != CAIRO_INT_STATUS_UNSUPPORTED); /* Restore both the clip save and our earlier path SaveDC */ - RestoreDC (surface->dc, -2); + RestoreDC (surface->win32.dc, -2); if (status) - return status; + goto err; } } surface->content = old_content; surface->ctm = old_ctm; surface->has_ctm = old_has_ctm; - RestoreDC (surface->dc, -1); + RestoreDC (surface->win32.dc, -1); + err: + cairo_surface_destroy (free_me); return status; } static cairo_int_status_t -_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_check_jpeg (cairo_win32_printing_surface_t *surface, cairo_surface_t *source, const unsigned char **data, unsigned long *length, @@ -529,7 +695,7 @@ _cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, cairo_int_status_t status; DWORD result; - if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) + if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) return CAIRO_INT_STATUS_UNSUPPORTED; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, @@ -542,7 +708,7 @@ _cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, return status; result = 0; - if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, + if (ExtEscape(surface->win32.dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, sizeof(result), (char *) &result) <= 0) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -556,7 +722,7 @@ _cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, } static cairo_int_status_t -_cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_check_png (cairo_win32_printing_surface_t *surface, cairo_surface_t *source, const unsigned char **data, unsigned long *length, @@ -568,7 +734,7 @@ _cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, cairo_int_status_t status; DWORD result; - if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) + if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) return CAIRO_INT_STATUS_UNSUPPORTED; cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG, @@ -581,7 +747,7 @@ _cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, return status; result = 0; - if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, + if (ExtEscape(surface->win32.dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, sizeof(result), (char *) &result) <= 0) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -595,11 +761,12 @@ _cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, } static cairo_status_t -_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surface, - cairo_surface_pattern_t *pattern) +_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_printing_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) { - cairo_status_t status; - cairo_extend_t extend; + cairo_int_status_t status; + cairo_surface_pattern_t image_pattern; cairo_image_surface_t *image; void *image_extra; cairo_image_surface_t *opaque_image = NULL; @@ -608,6 +775,7 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf int oldmode; XFORM xform; int x_tile, y_tile, left, right, top, bottom; + int pattern_width, pattern_height; RECT clip; const cairo_color_t *background_color; const unsigned char *mime_data; @@ -615,12 +783,11 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf cairo_image_info_t mime_info; cairo_bool_t use_mime; DWORD mime_type; - cairo_bool_t axis_swap; /* If we can't use StretchDIBits with this surface, we can't do anything * here. */ - if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) return CAIRO_INT_STATUS_UNSUPPORTED; if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) @@ -628,13 +795,17 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf else background_color = CAIRO_COLOR_BLACK; - extend = cairo_pattern_get_extend (&pattern->base); - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, &image_extra); + status = _cairo_win32_printing_surface_acquire_image_pattern (surface, + pattern, + extents, + &image_pattern, + &pattern_width, + &pattern_height, + &image_extra); if (status) return status; + image = (cairo_image_surface_t *)(image_pattern.surface); if (image->base.status) { status = image->base.status; goto CLEANUP_IMAGE; @@ -647,72 +818,46 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf mime_type = BI_JPEG; status = _cairo_win32_printing_surface_check_jpeg (surface, - pattern->surface, + image_pattern.surface, &mime_data, &mime_size, &mime_info); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { mime_type = BI_PNG; status = _cairo_win32_printing_surface_check_png (surface, - pattern->surface, + image_pattern.surface, &mime_data, &mime_size, &mime_info); } - if (_cairo_status_is_error (status)) + if (_cairo_int_status_is_error (status)) return status; - use_mime = (status == CAIRO_STATUS_SUCCESS); + use_mime = (status == CAIRO_INT_STATUS_SUCCESS); - m = pattern->base.matrix; - status = cairo_matrix_invert (&m); - /* _cairo_pattern_set_matrix guarantees invertibility */ - assert (status == CAIRO_STATUS_SUCCESS); - cairo_matrix_multiply (&m, &m, &surface->ctm); - cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); - /* Check if the matrix swaps the X and Y axes by checking if the diagonal - * is effectively zero. This can happen, for example, if it was composed - * with a rotation such as a landscape transform. Some printing devices - * don't support such transforms in StretchDIBits. - */ - axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1; - - if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) { + if (!use_mime && image->format != CAIRO_FORMAT_RGB24) { cairo_surface_t *opaque_surface; cairo_surface_pattern_t image_pattern; cairo_solid_pattern_t background_pattern; - int width = image->width, height = image->height; - if (axis_swap) { - width = image->height; - height = image->width; - } opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, - width, - height); + image->width, + image->height); if (opaque_surface->status) { status = opaque_surface->status; goto CLEANUP_OPAQUE_IMAGE; } - if (image->format != CAIRO_FORMAT_RGB24) { - _cairo_pattern_init_solid (&background_pattern, - background_color); - status = _cairo_surface_paint (opaque_surface, - CAIRO_OPERATOR_SOURCE, - &background_pattern.base, - NULL); - if (status) - goto CLEANUP_OPAQUE_IMAGE; - } + _cairo_pattern_init_solid (&background_pattern, + background_color); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_SOURCE, + &background_pattern.base, + NULL); + if (status) + goto CLEANUP_OPAQUE_IMAGE; _cairo_pattern_init_for_surface (&image_pattern, &image->base); - if (axis_swap) { - /* swap the X and Y axes to undo the axis swap in the matrix */ - cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 }; - cairo_pattern_set_matrix (&image_pattern.base, &swap_xy); - cairo_matrix_multiply (&m, &swap_xy, &m); - } status = _cairo_surface_paint (opaque_surface, CAIRO_OPERATOR_OVER, &image_pattern.base, @@ -738,18 +883,25 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf bi.bmiHeader.biClrUsed = 0; bi.bmiHeader.biClrImportant = 0; - SaveDC (surface->dc); + m = image_pattern.base.matrix; + status = cairo_matrix_invert (&m); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_INT_STATUS_SUCCESS); + + cairo_matrix_multiply (&m, &m, &surface->ctm); + cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); + SaveDC (surface->win32.dc); _cairo_matrix_to_win32_xform (&m, &xform); - if (! SetWorldTransform (surface->dc, &xform)) { + if (! SetWorldTransform (surface->win32.dc, &xform)) { status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); goto CLEANUP_OPAQUE_IMAGE; } - oldmode = SetStretchBltMode(surface->dc, HALFTONE); + oldmode = SetStretchBltMode(surface->win32.dc, HALFTONE); - GetClipBox (surface->dc, &clip); - if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + GetClipBox (surface->win32.dc, &clip); + if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) { left = floor ( clip.left / (double) opaque_image->width); right = ceil (clip.right / (double) opaque_image->width); top = floor (clip.top / (double) opaque_image->height); @@ -763,7 +915,7 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf for (y_tile = top; y_tile < bottom; y_tile++) { for (x_tile = left; x_tile < right; x_tile++) { - if (!StretchDIBits (surface->dc, + if (!StretchDIBits (surface->win32.dc, x_tile*opaque_image->width, y_tile*opaque_image->height, opaque_image->width, @@ -782,31 +934,18 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surf } } } - SetStretchBltMode(surface->dc, oldmode); - RestoreDC (surface->dc, -1); + SetStretchBltMode(surface->win32.dc, oldmode); + RestoreDC (surface->win32.dc, -1); CLEANUP_OPAQUE_IMAGE: if (opaque_image != image) cairo_surface_destroy (&opaque_image->base); CLEANUP_IMAGE: - _cairo_surface_release_source_image (pattern->surface, image, image_extra); + _cairo_win32_printing_surface_release_image_pattern (surface, pattern, &image_pattern, image_extra); return status; } -static cairo_status_t -_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - if (_cairo_surface_is_recording (pattern->surface)) { - return _cairo_win32_printing_surface_paint_recording_pattern (surface, - pattern); - } else { - return _cairo_win32_printing_surface_paint_image_pattern (surface, - pattern); - } -} - static void vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) { @@ -820,7 +959,7 @@ vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) } static cairo_int_status_t -_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_printing_surface_t *surface, cairo_linear_pattern_t *pattern) { TRIVERTEX *vert; @@ -836,7 +975,7 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa cairo_status_t status; extend = cairo_pattern_get_extend (&pattern->base.base); - SaveDC (surface->dc); + SaveDC (surface->win32.dc); mat = pattern->base.base.matrix; status = cairo_matrix_invert (&mat); @@ -845,10 +984,10 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa cairo_matrix_multiply (&mat, &surface->ctm, &mat); - p1x = _cairo_fixed_to_double (pattern->p1.x); - p1y = _cairo_fixed_to_double (pattern->p1.y); - p2x = _cairo_fixed_to_double (pattern->p2.x); - p2y = _cairo_fixed_to_double (pattern->p2.y); + p1x = pattern->pd1.x; + p1y = pattern->pd1.y; + p2x = pattern->pd2.x; + p2y = pattern->pd2.y; cairo_matrix_translate (&mat, p1x, p1y); xd = p2x - p1x; @@ -864,10 +1003,10 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa _cairo_matrix_to_win32_xform (&mat, &xform); - if (!SetWorldTransform (surface->dc, &xform)) + if (!SetWorldTransform (surface->win32.dc, &xform)) return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); - GetClipBox (surface->dc, &clip); + GetClipBox (surface->win32.dc, &clip); if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { range_start = floor (clip.left / d); @@ -881,8 +1020,8 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa num_rects = num_stops - 1; /* Add an extra four points and two rectangles for EXTEND_PAD */ - vert = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4)); - rect = malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2)); + vert = _cairo_malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4)); + rect = _cairo_malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2)); for (i = 0; i < num_ranges*num_rects; i++) { vert[i*2].y = (LONG) clip.top; @@ -950,7 +1089,7 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa total_rects += 2; } - if (!GradientFill (surface->dc, + if (!GradientFill (surface->win32.dc, vert, total_verts, rect, total_rects, GRADIENT_FILL_RECT_H)) @@ -958,14 +1097,15 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa free (rect); free (vert); - RestoreDC (surface->dc, -1); + RestoreDC (surface->win32.dc, -1); return 0; } static cairo_int_status_t -_cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, - const cairo_pattern_t *pattern) +_cairo_win32_printing_surface_paint_pattern (cairo_win32_printing_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) { cairo_status_t status; @@ -976,9 +1116,20 @@ _cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, return status; break; - case CAIRO_PATTERN_TYPE_SURFACE: - status = _cairo_win32_printing_surface_paint_surface_pattern (surface, - (cairo_surface_pattern_t *) pattern); + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + if ( _cairo_surface_is_recording (surface_pattern->surface)) + status = _cairo_win32_printing_surface_paint_recording_pattern (surface, surface_pattern); + else + status = _cairo_win32_printing_surface_paint_image_pattern (surface, pattern, extents); + + if (status) + return status; + break; + } + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + status = _cairo_win32_printing_surface_paint_image_pattern (surface, pattern, extents); if (status) return status; break; @@ -992,13 +1143,16 @@ _cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, case CAIRO_PATTERN_TYPE_RADIAL: return CAIRO_INT_STATUS_UNSUPPORTED; break; + + case CAIRO_PATTERN_TYPE_MESH: + ASSERT_NOT_REACHED; } return CAIRO_STATUS_SUCCESS; } typedef struct _win32_print_path_info { - cairo_win32_surface_t *surface; + cairo_win32_printing_surface_t *surface; } win32_path_info_t; static cairo_status_t @@ -1013,9 +1167,9 @@ _cairo_win32_printing_surface_path_move_to (void *closure, x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - MoveToEx (path_info->surface->dc, (int) x, (int) y, NULL); + MoveToEx (path_info->surface->win32.dc, (int) x, (int) y, NULL); } else { - MoveToEx (path_info->surface->dc, + MoveToEx (path_info->surface->win32.dc, _cairo_fixed_integer_part (point->x), _cairo_fixed_integer_part (point->y), NULL); @@ -1037,9 +1191,9 @@ _cairo_win32_printing_surface_path_line_to (void *closure, x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); - LineTo (path_info->surface->dc, (int) x, (int) y); + LineTo (path_info->surface->win32.dc, (int) x, (int) y); } else { - LineTo (path_info->surface->dc, + LineTo (path_info->surface->win32.dc, _cairo_fixed_integer_part (point->x), _cairo_fixed_integer_part (point->y)); } @@ -1085,7 +1239,7 @@ _cairo_win32_printing_surface_path_curve_to (void *closure, points[2].x = _cairo_fixed_integer_part (d->x); points[2].y = _cairo_fixed_integer_part (d->y); } - PolyBezierTo (path_info->surface->dc, points, 3); + PolyBezierTo (path_info->surface->win32.dc, points, 3); return CAIRO_STATUS_SUCCESS; } @@ -1095,20 +1249,19 @@ _cairo_win32_printing_surface_path_close_path (void *closure) { win32_path_info_t *path_info = closure; - CloseFigure (path_info->surface->dc); + CloseFigure (path_info->surface->win32.dc); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface, - cairo_path_fixed_t *path) +_cairo_win32_printing_surface_emit_path (cairo_win32_printing_surface_t *surface, + const cairo_path_fixed_t *path) { win32_path_info_t path_info; path_info.surface = surface; return _cairo_path_fixed_interpret (path, - CAIRO_DIRECTION_FORWARD, _cairo_win32_printing_surface_path_move_to, _cairo_win32_printing_surface_path_line_to, _cairo_win32_printing_surface_path_curve_to, @@ -1119,10 +1272,15 @@ _cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface, static cairo_int_status_t _cairo_win32_printing_surface_show_page (void *abstract_surface) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; /* Undo both SaveDC's that we did in start_page */ - RestoreDC (surface->dc, -2); + RestoreDC (surface->win32.dc, -2); + + /* Invalidate extents since the size of the next page is not known at + * this point. + */ + surface->extents_valid = FALSE; return CAIRO_STATUS_SUCCESS; } @@ -1134,8 +1292,8 @@ _cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper double tolerance, cairo_antialias_t antialias) { - cairo_win32_surface_t *surface = cairo_container_of (clipper, - cairo_win32_surface_t, + cairo_win32_printing_surface_t *surface = cairo_container_of (clipper, + cairo_win32_printing_surface_t, clipper); cairo_status_t status; @@ -1143,32 +1301,44 @@ _cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper return CAIRO_STATUS_SUCCESS; if (path == NULL) { - RestoreDC (surface->dc, -1); - SaveDC (surface->dc); + RestoreDC (surface->win32.dc, -1); + SaveDC (surface->win32.dc); return CAIRO_STATUS_SUCCESS; } - BeginPath (surface->dc); + BeginPath (surface->win32.dc); status = _cairo_win32_printing_surface_emit_path (surface, path); - EndPath (surface->dc); + EndPath (surface->win32.dc); switch (fill_rule) { case CAIRO_FILL_RULE_WINDING: - SetPolyFillMode (surface->dc, WINDING); + SetPolyFillMode (surface->win32.dc, WINDING); break; case CAIRO_FILL_RULE_EVEN_ODD: - SetPolyFillMode (surface->dc, ALTERNATE); + SetPolyFillMode (surface->win32.dc, ALTERNATE); break; default: ASSERT_NOT_REACHED; } - SelectClipPath (surface->dc, RGN_AND); + SelectClipPath (surface->win32.dc, RGN_AND); return status; } +static cairo_bool_t +_cairo_win32_printing_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_win32_printing_surface_t *surface = abstract_surface; + + if (surface->extents_valid) + *rectangle = surface->win32.extents; + + return surface->extents_valid; +} + static void _cairo_win32_printing_surface_get_font_options (void *abstract_surface, cairo_font_options_t *options) @@ -1185,28 +1355,41 @@ static cairo_int_status_t _cairo_win32_printing_surface_paint (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; cairo_solid_pattern_t clear; + cairo_composite_rectangles_t extents; cairo_status_t status; - status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) + status = _cairo_composite_rectangles_init_for_paint (&extents, + &surface->win32.base, + op, source, clip); + if (unlikely (status)) return status; + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto cleanup_composite; + if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); source = (cairo_pattern_t*) &clear; op = CAIRO_OPERATOR_SOURCE; } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; + } - assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source, &extents.bounded)); - return _cairo_win32_printing_surface_paint_pattern (surface, source); + status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); + + cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; } static int @@ -1262,34 +1445,58 @@ static cairo_int_status_t _cairo_win32_printing_surface_stroke (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *stroke_ctm, const cairo_matrix_t *stroke_ctm_inverse, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; cairo_int_status_t status; HPEN pen; LOGBRUSH brush; COLORREF color; XFORM xform; DWORD pen_style; - DWORD pen_width; DWORD *dash_array; HGDIOBJ obj; unsigned int i; cairo_solid_pattern_t clear; cairo_matrix_t mat; double scale; - double scaled_width; - double major, minor; + cairo_composite_rectangles_t extents; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->win32.base, + op, source, + path, style, stroke_ctm, + clip); + if (unlikely (status)) + return status; + + /* use the more accurate extents */ + { + cairo_rectangle_int_t r; + cairo_box_t b; + + status = _cairo_path_fixed_stroke_extents (path, style, + stroke_ctm, stroke_ctm_inverse, + tolerance, + &r); + if (unlikely (status)) + goto cleanup_composite; + + _cairo_box_from_rectangle (&b, &r); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b); + if (unlikely (status)) + goto cleanup_composite; + } status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; + if (unlikely (status)) + goto cleanup_composite; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); @@ -1298,17 +1505,16 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, } if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - /* Win32 does not support more than 16 elements in the dash array. */ - if (style->num_dashes > 16) - return CAIRO_INT_STATUS_UNSUPPORTED; /* Win32 does not support a dash offset. */ if (style->num_dashes > 0 && style->dash_offset != 0.0) - return CAIRO_INT_STATUS_UNSUPPORTED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + goto cleanup_composite; } - assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source, &extents.bounded)); assert (!(style->num_dashes > 0 && style->dash_offset != 0.0)); cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); @@ -1320,17 +1526,13 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, pen_style |= PS_USERSTYLE; dash_array = calloc (sizeof (DWORD), style->num_dashes); for (i = 0; i < style->num_dashes; i++) { - DWORD dashes = (DWORD) (scale * style->dash[i]); - /* zero dash-lengths cause ExtCreatePen to fail. Make the dashes - * longer if necessary. - */ - dash_array[i] = MAX(1, dashes); + dash_array[i] = (DWORD) (scale * style->dash[i]); } } else { pen_style |= PS_SOLID; } - SetMiterLimit (surface->dc, (FLOAT) (style->miter_limit), NULL); + SetMiterLimit (surface->win32.dc, (FLOAT) (style->miter_limit), NULL); if (source->type == CAIRO_PATTERN_TYPE_SOLID) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; @@ -1346,83 +1548,68 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, brush.lbHatch = 0; pen_style |= _cairo_win32_line_cap (style->line_cap); pen_style |= _cairo_win32_line_join (style->line_join); - scaled_width = scale * style->line_width; - if (scaled_width == 0.0) - return status; - pen_width = (DWORD)scaled_width; - if (pen_width == 0) { - /* ExtCreatePen will fail if passed zero width. We have to choose - * between drawing something too wide, or drawing nothing at all. - * Let's draw something. - */ - pen_width = 1; - } pen = ExtCreatePen(pen_style, - pen_width, + scale * style->line_width, &brush, style->num_dashes, dash_array); - if (pen == NULL) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); - obj = SelectObject (surface->dc, pen); - if (obj == NULL) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); + if (pen == NULL) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); + goto cleanup_composite; + } - BeginPath (surface->dc); + obj = SelectObject (surface->win32.dc, pen); + if (obj == NULL) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); + goto cleanup_composite; + } + + BeginPath (surface->win32.dc); status = _cairo_win32_printing_surface_emit_path (surface, path); - EndPath (surface->dc); - if (status) - return status; + EndPath (surface->win32.dc); + if (unlikely (status)) + goto cleanup_composite; /* * Switch to user space to set line parameters */ - SaveDC (surface->dc); + SaveDC (surface->win32.dc); - /* Some printers don't handle transformed strokes. Avoid the transform - * if not required for the pen shape. Use the SVD here to find the major - * and minor scales then check if they differ by more than 1 device unit. - * If the difference is smaller, then just treat the scaling as uniform. - * This check ignores rotations as the pen shape is symmetric before - * transformation. - */ - _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor); - if (fabs (major - minor) > 1) { - /* Check if the matrix swaps the X and Y axes such that the diagonal - * is nearly zero. This was observed to cause problems with XPS export. - */ - if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) { - /* swap the X and Y axes to undo the axis swap in the matrix */ - cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 }; - cairo_matrix_multiply (&mat, &swap_xy, &mat); - } - _cairo_matrix_to_win32_xform (&mat, &xform); - xform.eDx = 0.0f; - xform.eDy = 0.0f; + _cairo_matrix_to_win32_xform (&mat, &xform); + xform.eDx = 0.0f; + xform.eDy = 0.0f; - if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); + if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); + goto cleanup_composite; } if (source->type == CAIRO_PATTERN_TYPE_SOLID) { - StrokePath (surface->dc); + StrokePath (surface->win32.dc); } else { - if (!WidenPath (surface->dc)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); - if (!SelectClipPath (surface->dc, RGN_AND)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); + if (!WidenPath (surface->win32.dc)) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); + goto cleanup_composite; + } + if (!SelectClipPath (surface->win32.dc, RGN_AND)) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); + goto cleanup_composite; + } /* Return to device space to paint the pattern */ _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); - if (!SetWorldTransform (surface->dc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); - status = _cairo_win32_printing_surface_paint_pattern (surface, source); + if (!SetWorldTransform (surface->win32.dc, &xform)) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); + goto cleanup_composite; + } + status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); } - RestoreDC (surface->dc, -1); + RestoreDC (surface->win32.dc, -1); DeleteObject (pen); - if (dash_array) - free (dash_array); + free (dash_array); +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); return status; } @@ -1430,19 +1617,43 @@ static cairo_int_status_t _cairo_win32_printing_surface_fill (void *abstract_surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_path_fixed_t *path, + const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, - cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; cairo_int_status_t status; cairo_solid_pattern_t clear; + cairo_composite_rectangles_t extents; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &surface->win32.base, + op, source, path, + clip); + if (unlikely (status)) + return status; + + /* use the more accurate extents */ + { + cairo_rectangle_int_t r; + cairo_box_t b; + + _cairo_path_fixed_fill_extents (path, + fill_rule, + tolerance, + &r); + + _cairo_box_from_rectangle (&b, &r); + status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b); + if (unlikely (status)) + goto cleanup_composite; + } status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; + if (unlikely (status)) + goto cleanup_composite; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); @@ -1450,22 +1661,24 @@ _cairo_win32_printing_surface_fill (void *abstract_surface, op = CAIRO_OPERATOR_SOURCE; } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; + } - assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source, &extents.bounded)); surface->path_empty = TRUE; - BeginPath (surface->dc); + BeginPath (surface->win32.dc); status = _cairo_win32_printing_surface_emit_path (surface, path); - EndPath (surface->dc); + EndPath (surface->win32.dc); switch (fill_rule) { case CAIRO_FILL_RULE_WINDING: - SetPolyFillMode (surface->dc, WINDING); + SetPolyFillMode (surface->win32.dc, WINDING); break; case CAIRO_FILL_RULE_EVEN_ODD: - SetPolyFillMode (surface->dc, ALTERNATE); + SetPolyFillMode (surface->win32.dc, ALTERNATE); break; default: ASSERT_NOT_REACHED; @@ -1473,32 +1686,34 @@ _cairo_win32_printing_surface_fill (void *abstract_surface, if (source->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_win32_printing_surface_select_solid_brush (surface, source); - if (status) - return status; + if (unlikely (status)) + goto cleanup_composite; - FillPath (surface->dc); + FillPath (surface->win32.dc); _cairo_win32_printing_surface_done_solid_brush (surface); } else if (surface->path_empty == FALSE) { - SaveDC (surface->dc); - SelectClipPath (surface->dc, RGN_AND); - status = _cairo_win32_printing_surface_paint_pattern (surface, source); - RestoreDC (surface->dc, -1); + SaveDC (surface->win32.dc); + SelectClipPath (surface->win32.dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); + RestoreDC (surface->win32.dc, -1); } fflush(stderr); +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); return status; } + static cairo_int_status_t -_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *surface, +_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_printing_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, - cairo_glyph_t *glyphs, + cairo_glyph_t *glyphs, int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) { cairo_matrix_t ctm; cairo_glyph_t *unicode_glyphs; @@ -1552,16 +1767,12 @@ _cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *surface if (i == num_glyphs - 1 || ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode)) { - status = _cairo_win32_surface_show_glyphs_internal ( - surface, - op, - source, - sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first], - i - first + 1, - scaled_font, - clip, - remaining_glyphs, - ! sequence_is_unicode); + status = _cairo_win32_surface_emit_glyphs (&surface->win32, + source, + sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first], + i - first + 1, + scaled_font, + ! sequence_is_unicode); first = i + 1; if (i < num_glyphs - 1) sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff; @@ -1585,10 +1796,9 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_scaled_glyph_t *scaled_glyph; cairo_pattern_t *opaque = NULL; @@ -1596,11 +1806,22 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cairo_matrix_t old_ctm; cairo_bool_t old_has_ctm; cairo_solid_pattern_t clear; - cairo_scaled_font_t *local_scaled_font = NULL; + cairo_composite_rectangles_t extents; + cairo_bool_t overlap; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &surface->win32.base, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + if (unlikely (status)) + return status; status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (status) - return status; + if (unlikely (status)) + goto cleanup_composite; if (op == CAIRO_OPERATOR_CLEAR) { _cairo_win32_printing_surface_init_clear_color (surface, &clear); @@ -1620,35 +1841,37 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac */ #if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { - if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - else - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto cleanup_composite; + } else { + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; + } } #endif -#if CAIRO_HAS_DWRITE_FONT - if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); - } -#endif - /* For non win32 fonts we need to check that each glyph has a * path available. If a path is not available, * _cairo_scaled_glyph_lookup() will return * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be * used. */ + _cairo_scaled_font_freeze_cache (scaled_font); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); if (status) - return status; + break; } + _cairo_scaled_font_thaw_cache (scaled_font); + if (unlikely (status)) + goto cleanup_composite; - return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; } if (source->type == CAIRO_PATTERN_TYPE_SOLID) { @@ -1660,28 +1883,13 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0, GetGValue (color) / 255.0, GetBValue (color) / 255.0); - if (opaque->status) - return opaque->status; + if (unlikely (opaque->status)) { + status = opaque->status; + goto cleanup_composite; + } source = opaque; } -#if CAIRO_HAS_DWRITE_FONT - /* For a printer, the dwrite path is not desirable as it goes through the - * bitmap-blitting GDI interop route. Better to create a win32 (GDI) font - * so that ExtTextOut can be used, giving the printer driver the chance - * to do the right thing with the text. - */ - if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { - status = _cairo_dwrite_scaled_font_create_win32_scaled_font (scaled_font, &local_scaled_font); - if (status == CAIRO_STATUS_SUCCESS) { - scaled_font = local_scaled_font; - } else { - /* Reset status; we'll fall back to drawing glyphs as paths */ - status = CAIRO_STATUS_SUCCESS; - } - } -#endif - #if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && source->type == CAIRO_PATTERN_TYPE_SOLID) @@ -1692,18 +1900,18 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac glyphs, num_glyphs, scaled_font, - clip, - remaining_glyphs); - goto FINISH; + clip); + goto cleanup_composite; } #endif - SaveDC (surface->dc); + SaveDC (surface->win32.dc); old_ctm = surface->ctm; old_has_ctm = surface->has_ctm; surface->has_ctm = TRUE; surface->path_empty = TRUE; - BeginPath (surface->dc); + _cairo_scaled_font_freeze_cache (scaled_font); + BeginPath (surface->win32.dc); for (i = 0; i < num_glyphs; i++) { status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, @@ -1715,35 +1923,51 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y); status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path); } - EndPath (surface->dc); + EndPath (surface->win32.dc); + _cairo_scaled_font_thaw_cache (scaled_font); surface->ctm = old_ctm; surface->has_ctm = old_has_ctm; if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) { if (source->type == CAIRO_PATTERN_TYPE_SOLID) { status = _cairo_win32_printing_surface_select_solid_brush (surface, source); - if (status) - goto FINISH; + if (unlikely (status)) + goto cleanup_composite; - SetPolyFillMode (surface->dc, WINDING); - FillPath (surface->dc); + SetPolyFillMode (surface->win32.dc, WINDING); + FillPath (surface->win32.dc); _cairo_win32_printing_surface_done_solid_brush (surface); } else { - SelectClipPath (surface->dc, RGN_AND); - status = _cairo_win32_printing_surface_paint_pattern (surface, source); + SelectClipPath (surface->win32.dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); } } - RestoreDC (surface->dc, -1); + RestoreDC (surface->win32.dc, -1); if (opaque) cairo_pattern_destroy (opaque); -FINISH: - if (local_scaled_font) - cairo_scaled_font_destroy (local_scaled_font); - +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); return status; } +static const char ** +_cairo_win32_printing_surface_get_supported_mime_types (void *abstract_surface) +{ + return _cairo_win32_printing_supported_mime_types; +} + +static cairo_status_t +_cairo_win32_printing_surface_finish (void *abstract_surface) +{ + cairo_win32_printing_surface_t *surface = abstract_surface; + + if (surface->font_subsets != NULL) + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + + return CAIRO_STATUS_SUCCESS; +} + static cairo_surface_t * _cairo_win32_printing_surface_create_similar (void *abstract_surface, cairo_content_t content, @@ -1761,13 +1985,34 @@ _cairo_win32_printing_surface_create_similar (void *abstract_surface, static cairo_int_status_t _cairo_win32_printing_surface_start_page (void *abstract_surface) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; XFORM xform; double x_res, y_res; cairo_matrix_t inverse_ctm; cairo_status_t status; + RECT rect; - SaveDC (surface->dc); /* Save application context first, before doing MWT */ + /* Since the page size may be changed after _show_page() and before the + * next drawing command, the extents are set in _start_page() and invalidated + * in _show_page(). The paginated surface will obtain the extents immediately + * after calling _show_page() and before any drawing commands. At this point + * the next page will not have been setup on the DC so we return invalid + * extents and the paginated surface will create an unbounded recording surface. + * Prior to replay of the record surface, the paginated surface will call + * _start_page and we setup the correct extents. + * + * Note that we always set the extents x,y to 0 so prevent replay from translating + * the coordinates of objects. Windows will clip anything outside of the page clip + * area. + */ + GetClipBox(surface->win32.dc, &rect); + surface->win32.extents.x = 0; + surface->win32.extents.y = 0; + surface->win32.extents.width = rect.right; + surface->win32.extents.height = rect.bottom; + surface->extents_valid = TRUE; + + SaveDC (surface->win32.dc); /* Save application context first, before doing MWT */ /* As the logical coordinates used by GDI functions (eg LineTo) * are integers we need to do some additional work to prevent @@ -1796,7 +2041,7 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) * coordinates. * * If the device context is an EMF file, using an identity - * transform often provides insufficent resolution. The workaround + * transform often provides insufficient resolution. The workaround * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0] * and scale the cairo CTM by [16 0 0 16 0 0]. The * SetWorldTransform function call to scale the GDI CTM by 1.0/16 @@ -1806,8 +2051,8 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) * To support allowing the user to set a GDI CTM with scale < 1, * we avoid switching to an identity CTM if the CTM xx and yy is < 1. */ - SetGraphicsMode (surface->dc, GM_ADVANCED); - GetWorldTransform(surface->dc, &xform); + SetGraphicsMode (surface->win32.dc, GM_ADVANCED); + GetWorldTransform(surface->win32.dc, &xform); if (xform.eM11 < 1 && xform.eM22 < 1) { cairo_matrix_init_identity (&surface->ctm); surface->gdi_ctm.xx = xform.eM11; @@ -1824,7 +2069,7 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) surface->ctm.x0 = xform.eDx; surface->ctm.y0 = xform.eDy; cairo_matrix_init_identity (&surface->gdi_ctm); - if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY)) + if (!ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY)) return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); } @@ -1835,23 +2080,25 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) if (status) return status; - x_res = GetDeviceCaps (surface->dc, LOGPIXELSX); - y_res = GetDeviceCaps (surface->dc, LOGPIXELSY); + x_res = GetDeviceCaps (surface->win32.dc, LOGPIXELSX); + y_res = GetDeviceCaps (surface->win32.dc, LOGPIXELSY); cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res); - _cairo_surface_set_resolution (&surface->base, x_res, y_res); + _cairo_surface_set_resolution (&surface->win32.base, x_res, y_res); - SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ + SaveDC (surface->win32.dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ return CAIRO_STATUS_SUCCESS; } -static void +static cairo_int_status_t _cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, cairo_paginated_mode_t paginated_mode) { - cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_printing_surface_t *surface = abstract_surface; surface->paginated_mode = paginated_mode; + + return CAIRO_STATUS_SUCCESS; } static cairo_bool_t @@ -1871,7 +2118,7 @@ _cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_su * possible to draw to the surface. * * The returned surface will be wrapped using the paginated surface to - * provide correct complex rendering behaviour; show_page() and + * provide correct complex rendering behaviour; cairo_surface_show_page() and * associated methods must be used for correct output. * * Return value: the newly created surface @@ -1881,31 +2128,29 @@ _cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_su cairo_surface_t * cairo_win32_printing_surface_create (HDC hdc) { - cairo_win32_surface_t *surface; + cairo_win32_printing_surface_t *surface; cairo_surface_t *paginated; - RECT rect; - surface = malloc (sizeof (cairo_win32_surface_t)); + surface = _cairo_malloc (sizeof (cairo_win32_printing_surface_t)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); +#if 0 if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { free (surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } +#endif _cairo_surface_clipper_init (&surface->clipper, _cairo_win32_printing_surface_clipper_intersect_clip_path); - surface->image = NULL; - surface->format = CAIRO_FORMAT_RGB24; + surface->win32.format = CAIRO_FORMAT_RGB24; surface->content = CAIRO_CONTENT_COLOR_ALPHA; - surface->d3d9surface = NULL; - surface->dc = hdc; - surface->bitmap = NULL; - surface->is_dib = FALSE; - surface->saved_dc_bitmap = NULL; + surface->win32.dc = hdc; + surface->extents_valid = FALSE; + surface->brush = NULL; surface->old_brush = NULL; surface->font_subsets = _cairo_scaled_font_subsets_create_scaled (); @@ -1914,71 +2159,62 @@ cairo_win32_printing_surface_create (HDC hdc) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } - GetClipBox(hdc, &rect); - surface->extents.x = rect.left; - surface->extents.y = rect.top; - surface->extents.width = rect.right - rect.left; - surface->extents.height = rect.bottom - rect.top; - - surface->flags = _cairo_win32_flags_for_dc (surface->dc); - surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; + surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc, CAIRO_FORMAT_RGB24); + surface->win32.flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; _cairo_win32_printing_surface_init_ps_mode (surface); _cairo_win32_printing_surface_init_image_support (surface); _cairo_win32_printing_surface_init_language_pack (surface); - _cairo_surface_init (&surface->base, + _cairo_surface_init (&surface->win32.base, &cairo_win32_printing_surface_backend, NULL, /* device */ - CAIRO_CONTENT_COLOR_ALPHA); + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ - paginated = _cairo_paginated_surface_create (&surface->base, + paginated = _cairo_paginated_surface_create (&surface->win32.base, CAIRO_CONTENT_COLOR_ALPHA, &cairo_win32_surface_paginated_backend); /* paginated keeps the only reference to surface now, drop ours */ - cairo_surface_destroy (&surface->base); + cairo_surface_destroy (&surface->win32.base); return paginated; } -cairo_bool_t -_cairo_surface_is_win32_printing (cairo_surface_t *surface) -{ - return surface->backend == &cairo_win32_printing_surface_backend; -} - static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { CAIRO_SURFACE_TYPE_WIN32_PRINTING, + _cairo_win32_printing_surface_finish, + + _cairo_default_context_create, + _cairo_win32_printing_surface_create_similar, - _cairo_win32_surface_finish, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, NULL, /* acquire_source_image */ NULL, /* release_source_image */ - NULL, /* acquire_dest_image */ - NULL, /* release_dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + NULL, /* snapshot */ + NULL, /* copy_page */ _cairo_win32_printing_surface_show_page, - _cairo_win32_surface_get_extents, - NULL, /* old_show_glyphs */ + + _cairo_win32_printing_surface_get_extents, _cairo_win32_printing_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ _cairo_win32_printing_surface_paint, NULL, /* mask */ _cairo_win32_printing_surface_stroke, _cairo_win32_printing_surface_fill, + NULL, /* fill/stroke */ _cairo_win32_printing_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ + NULL, /* has_show_text_glyphs */ + NULL, /* show_text_glyphs */ + _cairo_win32_printing_surface_get_supported_mime_types, }; static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = { diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/win32/cairo-win32-private.h similarity index 54% rename from gfx/cairo/cairo/src/cairo-win32-private.h rename to gfx/cairo/cairo/src/win32/cairo-win32-private.h index 44c38535fef9..85f88a9e8d06 100644 --- a/gfx/cairo/cairo/src/cairo-win32-private.h +++ b/gfx/cairo/cairo/src/win32/cairo-win32-private.h @@ -37,8 +37,12 @@ #define CAIRO_WIN32_PRIVATE_H #include "cairo-win32.h" + #include "cairoint.h" + +#include "cairo-device-private.h" #include "cairo-surface-clipper-private.h" +#include "cairo-surface-private.h" #ifndef SHADEBLENDCAPS #define SHADEBLENDCAPS 120 @@ -47,65 +51,7 @@ #define SB_NONE 0 #endif -#define WIN32_FONT_LOGICAL_SCALE 1 - - -CAIRO_BEGIN_DECLS - -typedef struct _cairo_win32_surface { - cairo_surface_t base; - - cairo_format_t format; - - HDC dc; - - struct IDirect3DSurface9 *d3d9surface; - - /* We create off-screen surfaces as DIBs or DDBs, based on what we created - * originally*/ - HBITMAP bitmap; - cairo_bool_t is_dib; - - /* Used to save the initial 1x1 monochrome bitmap for the DC to - * select back into the DC before deleting the DC and our - * bitmap. For Windows XP, this doesn't seem to be necessary - * ... we can just delete the DC and that automatically unselects - * out bitmap. But it's standard practice so apparently is needed - * on some versions of Windows. - */ - HBITMAP saved_dc_bitmap; - - cairo_surface_t *image; - - cairo_rectangle_int_t extents; - - /* Initial clip bits - * We need these kept around so that we maintain - * whatever clip was set on the original DC at creation - * time when cairo is asked to reset the surface clip. - */ - cairo_rectangle_int_t clip_rect; - HRGN initial_clip_rgn; - cairo_bool_t had_simple_clip; - cairo_region_t *clip_region; - - /* For path clipping to the printing-surface */ - cairo_surface_clipper_t clipper; - - /* Surface DC flags */ - uint32_t flags; - - /* printing surface bits */ - cairo_paginated_mode_t paginated_mode; - cairo_content_t content; - cairo_bool_t path_empty; - cairo_bool_t has_ctm; - cairo_matrix_t ctm; - cairo_bool_t has_gdi_ctm; - cairo_matrix_t gdi_ctm; - HBRUSH brush, old_brush; - cairo_scaled_font_subsets_t *font_subsets; -} cairo_win32_surface_t; +#define WIN32_FONT_LOGICAL_SCALE 32 /* Surface DC flag values */ enum { @@ -133,78 +79,147 @@ enum { /* Whether we can use the CHECKJPEGFORMAT escape function */ CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), - /* Whether we can use the CHECKJPEGFORMAT escape function */ + /* Whether we can use the CHECKPNGFORMAT escape function */ CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), - /* if this DDB surface can be converted to a DIB if necessary */ - CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<9), + /* Whether we can use gdi drawing with solid rgb brush with this surface */ + CAIRO_WIN32_SURFACE_CAN_RGB_BRUSH = (1<<9), }; +typedef struct _cairo_win32_surface { + cairo_surface_t base; + + cairo_format_t format; + HDC dc; + + /* Surface DC flags */ + unsigned flags; + + /* We use the x and y parts of extents for situations where + * we're not supposed to draw to the entire surface. + * For example, during a paint event a program will get + * a DC that has been clipped to the dirty region. + * A cairo surface constructed for that DC will have extents + * that match bounds of the clipped region. + */ + cairo_rectangle_int_t extents; + + /* Offset added to extents, used when the extents start with a negative + * offset, which can occur on Windows for, and only for, desktop DC. This + * occurs when you have multiple monitors, and at least one monitor + * extends to the left, or above, the primaty monitor. The primary + * monitor on Windows always starts with offset (0,0), and any other points + * to the left, or above, have negative offsets. So the 'desktop DC' is + * in fact a 'virtual desktop' which can start with extents in the negative + * range. + * + * Why use new variables, and not the device transform? Simply because since + * the device transform functions are exposed, a lot of 3rd party libraries + * simply overwrite those, disregarding the prior content, instead of actually + * adding the offset. GTK for example simply resets the device transform of the + * desktop cairo surface to zero. So make some private member variables for + * this, which will not be fiddled with externally. + */ + int x_ofs, y_ofs; +} cairo_win32_surface_t; +#define to_win32_surface(S) ((cairo_win32_surface_t *)(S)) + +typedef struct _cairo_win32_display_surface { + cairo_win32_surface_t win32; + + /* We create off-screen surfaces as DIBs or DDBs, based on what we created + * originally */ + HBITMAP bitmap; + cairo_bool_t is_dib; + + /* Used to save the initial 1x1 monochrome bitmap for the DC to + * select back into the DC before deleting the DC and our + * bitmap. For Windows XP, this doesn't seem to be necessary + * ... we can just delete the DC and that automatically unselects + * our bitmap. But it's standard practice so apparently is needed + * on some versions of Windows. + */ + HBITMAP saved_dc_bitmap; + cairo_surface_t *image; + cairo_surface_t *fallback; + + HRGN initial_clip_rgn; + cairo_bool_t had_simple_clip; +} cairo_win32_display_surface_t; +#define to_win32_display_surface(S) ((cairo_win32_display_surface_t *)(S)) + +typedef struct _cairo_win32_printing_surface { + cairo_win32_surface_t win32; + + cairo_surface_clipper_t clipper; + + cairo_paginated_mode_t paginated_mode; + cairo_content_t content; + cairo_bool_t path_empty; + cairo_bool_t has_ctm; + cairo_matrix_t ctm; + cairo_bool_t has_gdi_ctm; + cairo_matrix_t gdi_ctm; + cairo_bool_t extents_valid; + HBRUSH brush, old_brush; + cairo_scaled_font_subsets_t *font_subsets; +} cairo_win32_printing_surface_t; +#define to_win32_printing_surface(S) ((cairo_win32_printing_surface_t *)(S)) + +typedef BOOL (WINAPI *cairo_win32_alpha_blend_func_t) (HDC hdcDest, + int nXOriginDest, + int nYOriginDest, + int nWidthDest, + int hHeightDest, + HDC hdcSrc, + int nXOriginSrc, + int nYOriginSrc, + int nWidthSrc, + int nHeightSrc, + BLENDFUNCTION blendFunction); + +typedef struct _cairo_win32_device { + cairo_device_t base; + + HMODULE msimg32_dll; + + const cairo_compositor_t *compositor; + + cairo_win32_alpha_blend_func_t alpha_blend; +} cairo_win32_device_t; +#define to_win32_device(D) ((cairo_win32_device_t *)(D)) +#define to_win32_device_from_surface(S) to_win32_device(((cairo_surface_t *)(S))->device) + +cairo_private cairo_device_t * +_cairo_win32_device_get (void); + +const cairo_compositor_t * +_cairo_win32_gdi_compositor_get (void); + cairo_status_t _cairo_win32_print_gdi_error (const char *context); -cairo_bool_t -_cairo_surface_is_win32 (cairo_surface_t *surface); +cairo_private void +_cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface); cairo_bool_t -_cairo_surface_is_win32_printing (cairo_surface_t *surface); - -cairo_status_t -_cairo_win32_surface_finish (void *abstract_surface); - -cairo_bool_t -_cairo_win32_surface_get_extents (void *abstract_surface, +_cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); uint32_t -_cairo_win32_flags_for_dc (HDC dc); - -cairo_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - cairo_region_t *region); +_cairo_win32_flags_for_dc (HDC dc, cairo_format_t format); cairo_int_status_t -_cairo_win32_surface_show_glyphs_internal (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs, - cairo_bool_t glyph_indices); - -cairo_int_status_t -_cairo_win32_surface_show_glyphs (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip, - int *remaining_glyphs); - -cairo_surface_t * -_cairo_win32_surface_create_similar (void *abstract_src, - cairo_content_t content, - int width, - int height); - -cairo_status_t -_cairo_win32_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - cairo_content_t content, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out); +_cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_bool_t glyph_indexing); static inline void _cairo_matrix_to_win32_xform (const cairo_matrix_t *m, - XFORM *xform) + XFORM *xform) { xform->eM11 = (FLOAT) m->xx; xform->eM21 = (FLOAT) m->xy; @@ -214,11 +229,12 @@ _cairo_matrix_to_win32_xform (const cairo_matrix_t *m, xform->eDy = (FLOAT) m->y0; } -cairo_int_status_t -_cairo_win32_save_initial_clip (HDC dc, cairo_win32_surface_t *surface); +cairo_status_t +_cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, + cairo_clip_t *clip); -cairo_int_status_t -_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface); +void +_cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface); void _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header); @@ -229,21 +245,4 @@ _cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); cairo_bool_t _cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font); -#ifdef CAIRO_HAS_DWRITE_FONT - -cairo_int_status_t -_cairo_dwrite_show_glyphs_on_surface(void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_clip_t *clip); - -cairo_int_status_t -_cairo_dwrite_scaled_font_create_win32_scaled_font(cairo_scaled_font_t *scaled_font, - cairo_scaled_font_t **new_font); - -#endif /* CAIRO_HAS_DWRITE_FONT */ -CAIRO_END_DECLS #endif /* CAIRO_WIN32_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-win32-refptr.h b/gfx/cairo/cairo/src/win32/cairo-win32-refptr.h similarity index 98% rename from gfx/cairo/cairo/src/cairo-win32-refptr.h rename to gfx/cairo/cairo/src/win32/cairo-win32-refptr.h index 0baf8ee7b185..f045ddf6277e 100644 --- a/gfx/cairo/cairo/src/cairo-win32-refptr.h +++ b/gfx/cairo/cairo/src/win32/cairo-win32-refptr.h @@ -1,7 +1,7 @@ /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * - * Copyright 2010 Mozilla Foundation + * Copyright 2010 Mozilla Foundation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -31,7 +31,7 @@ * The Initial Developer of the Original Code is the Mozilla Foundation * * Contributor(s): - * Bas Schouten + * Bas Schouten */ #ifndef CAIRO_WIN32_REFPTR_H #define CAIRO_WIN32_REFPTR_H diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-surface.c b/gfx/cairo/cairo/src/win32/cairo-win32-surface.c new file mode 100644 index 000000000000..73078362d10d --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-surface.c @@ -0,0 +1,337 @@ +/* -*- 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. + * Copyright © 2012 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): + * Owen Taylor + * Stuart Parmenter + * Vladimir Vukicevic + */ + +#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" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-private.h" +#include "cairo-win32-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-surface-fallback-private.h" +#include "cairo-surface-backend-private.h" + +#include +#include + +#if defined(__MINGW32__) && !defined(ETO_PDY) +# define ETO_PDY 0x2000 +#endif + +/** + * SECTION:cairo-win32 + * @Title: Win32 Surfaces + * @Short_Description: Microsoft Windows surface support + * @See_Also: #cairo_surface_t + * + * The Microsoft Windows surface is used to render cairo graphics to + * Microsoft Windows windows, bitmaps, and printing device contexts. + * + * The surface returned by cairo_win32_printing_surface_create() is of surface + * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface + * type. + * + * The surface returned by the other win32 constructors is of surface type + * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. + **/ + +/** + * CAIRO_HAS_WIN32_SURFACE: + * + * Defined if the Microsoft Windows surface backend is available. + * This macro can be used to conditionally compile backend-specific code. + * + * Since: 1.0 + **/ + +/** + * _cairo_win32_print_gdi_error: + * @context: context string to display along with the error + * + * Helper function to dump out a human readable form of the + * current error code. + * + * Return value: A cairo status code for the error code + **/ +cairo_status_t +_cairo_win32_print_gdi_error (const char *context) +{ + void *lpMsgBuf; + DWORD last_error = GetLastError (); + + if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + last_error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, NULL)) { + fprintf (stderr, "%s: Unknown GDI error", context); + } else { + fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf); + + LocalFree (lpMsgBuf); + } + + fflush (stderr); + + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); +} + +cairo_bool_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_win32_surface_t *surface = abstract_surface; + + *rectangle = surface->extents; + return TRUE; +} + +/** + * cairo_win32_surface_get_dc: + * @surface: a #cairo_surface_t + * + * Returns the HDC associated with this surface, or %NULL if none. + * Also returns %NULL if the surface is not a win32 surface. + * + * A call to cairo_surface_flush() is required before using the HDC 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 HDC has been modified. + * + * Return value: HDC or %NULL if no HDC available. + * + * Since: 1.2 + **/ +HDC +cairo_win32_surface_get_dc (cairo_surface_t *surface) +{ + if (surface->backend == NULL) + return NULL; + + if (surface->backend->type == CAIRO_SURFACE_TYPE_WIN32) + return to_win32_surface(surface)->dc; + + if (_cairo_surface_is_paginated (surface)) { + cairo_surface_t *target = _cairo_paginated_surface_get_target (surface); + if (target->backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING) + return to_win32_surface(target)->dc; + } + + return NULL; +} + +/** + * _cairo_surface_is_win32: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_win32_surface_t + * + * Return value: %TRUE if the surface is an win32 surface + **/ +static inline cairo_bool_t +_cairo_surface_is_win32 (const cairo_surface_t *surface) +{ + /* _cairo_surface_nil sets a NULL backend so be safe */ + return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_WIN32; +} + +/** + * cairo_win32_surface_get_image: + * @surface: a #cairo_surface_t + * + * Returns a #cairo_surface_t image surface that refers to the same bits + * as the DIB of the Win32 surface. If the passed-in win32 surface + * is not a DIB surface, %NULL is returned. + * + * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t), + * or %NULL if the win32 surface is not a DIB. + * + * Since: 1.4 + **/ +cairo_surface_t * +cairo_win32_surface_get_image (cairo_surface_t *surface) +{ + + if (! _cairo_surface_is_win32 (surface)) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + } + + GdiFlush(); + return to_win32_display_surface(surface)->image; +} + +#define STACK_GLYPH_SIZE 256 +cairo_int_status_t +_cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_bool_t glyph_indexing) +{ +#if CAIRO_HAS_WIN32_FONT + WORD glyph_buf_stack[STACK_GLYPH_SIZE]; + WORD *glyph_buf = glyph_buf_stack; + int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; + int *dxy_buf = dxy_buf_stack; + + BOOL win_result = 0; + int i, j; + + cairo_solid_pattern_t *solid_pattern; + COLORREF color; + + cairo_matrix_t device_to_logical; + + int start_x, start_y; + double user_x, user_y; + int logical_x, logical_y; + unsigned int glyph_index_option; + + /* We can only handle win32 fonts */ + assert (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32); + + /* We can only handle opaque solid color sources and destinations */ + assert (_cairo_pattern_is_opaque_solid(source)); + assert (dst->format == CAIRO_FORMAT_RGB24); + + solid_pattern = (cairo_solid_pattern_t *)source; + color = RGB(((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); + + SaveDC(dst->dc); + + cairo_win32_scaled_font_select_font(scaled_font, dst->dc); + SetTextColor(dst->dc, color); + SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); + SetBkMode(dst->dc, TRANSPARENT); + + if (num_glyphs > STACK_GLYPH_SIZE) { + glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); + dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); + } + + /* 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 + * coordinate due to accumulated rounding error. As a result strings could + * be painted shorter or longer than expected. */ + + user_x = glyphs[0].x; + user_y = glyphs[0].y; + + cairo_matrix_transform_point(&device_to_logical, + &user_x, &user_y); + + logical_x = _cairo_lround (user_x); + logical_y = _cairo_lround (user_y); + + start_x = logical_x; + start_y = logical_y; + + for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { + glyph_buf[i] = (WORD) glyphs[i].index; + if (i == num_glyphs - 1) { + dxy_buf[j] = 0; + dxy_buf[j+1] = 0; + } else { + double next_user_x = glyphs[i+1].x; + double next_user_y = glyphs[i+1].y; + int next_logical_x, next_logical_y; + + cairo_matrix_transform_point(&device_to_logical, + &next_user_x, &next_user_y); + + next_logical_x = _cairo_lround (next_user_x); + next_logical_y = _cairo_lround (next_user_y); + + dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); + dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y); + + logical_x = next_logical_x; + logical_y = next_logical_y; + } + } + + if (glyph_indexing) + glyph_index_option = ETO_GLYPH_INDEX; + else + glyph_index_option = 0; + + win_result = ExtTextOutW(dst->dc, + start_x, + start_y, + glyph_index_option | ETO_PDY, + NULL, + glyph_buf, + num_glyphs, + dxy_buf); + if (!win_result) { + _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); + } + + RestoreDC(dst->dc, -1); + + if (glyph_buf != glyph_buf_stack) { + free(glyph_buf); + free(dxy_buf); + } + return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} +#undef STACK_GLYPH_SIZE diff --git a/gfx/cairo/cairo/src/cairo-system.c b/gfx/cairo/cairo/src/win32/cairo-win32-system.c similarity index 94% rename from gfx/cairo/cairo/src/cairo-system.c rename to gfx/cairo/cairo/src/win32/cairo-win32-system.c index 1ff4d078f892..878553009f2c 100644 --- a/gfx/cairo/cairo/src/cairo-system.c +++ b/gfx/cairo/cairo/src/win32/cairo-win32-system.c @@ -47,8 +47,6 @@ #include "cairoint.h" - - #if CAIRO_MUTEX_IMPL_WIN32 #if !CAIRO_WIN32_STATIC_BUILD @@ -61,11 +59,6 @@ # define _WIN32_WINNT 0x0500 #endif -#include "cairo-clip-private.h" -#include "cairo-paginated-private.h" -#include "cairo-win32-private.h" -#include "cairo-scaled-font-subsets-private.h" - #include /* declare to avoid "no previous prototype for 'DllMain'" warning */ @@ -94,4 +87,3 @@ DllMain (HINSTANCE hinstDLL, #endif #endif -